(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 postsjson values[1] // => commentsjson return values; }); ``` @class promise @param {function} resolver useful for tooling. @constructor */ function promise(resolver) { this[promise_id] = nextid(); this._result = this._state = undefined; this._subscribers = []; if (noop !== resolver) { typeof resolver !== 'function' && needsresolver(); this instanceof promise ? initializepromise(this, resolver) : needsnew(); } } promise.all = all; promise.race = race; promise.resolve = resolve; promise.reject = reject; promise._setscheduler = setscheduler; promise._setasap = setasap; promise._asap = asap; promise.prototype = { constructor: promise, /** the primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled. ```js finduser().then(function(user){ // user is available }, function(reason){ // user is unavailable, and you are given the reason why }); ``` chaining -------- the return value of `then` is itself a promise. this second, 'downstream' promise is resolved with the return value of the first promise's fulfillment or rejection handler, or rejected if the handler throws an exception. ```js finduser().then(function (user) { return user.name; }, function (reason) { return 'default name'; }).then(function (username) { // if `finduser` fulfilled, `username` will be the user's name, otherwise it // will be `'default name'` }); finduser().then(function (user) { throw new error('found user, but still unhappy'); }, function (reason) { throw new error('`finduser` rejected and we're unhappy'); }).then(function (value) { // never reached }, function (reason) { // if `finduser` fulfilled, `reason` will be 'found user, but still unhappy'. // if `finduser` rejected, `reason` will be '`finduser` rejected and we're unhappy'. }); ``` if the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. ```js finduser().then(function (user) { throw new pedagogicalexception('upstream error'); }).then(function (value) { // never reached }).then(function (value) { // never reached }, function (reason) { // the `pedgagocialexception` is propagated all the way down to here }); ``` assimilation ------------ sometimes the value you want to propagate to a downstream promise can only be retrieved asynchronously. this can be achieved by returning a promise in the fulfillment or rejection handler. the downstream promise will then be pending until the returned promise is settled. this is called *assimilation*. ```js finduser().then(function (user) { return findcommentsbyauthor(user); }).then(function (comments) { // the user's comments are now available }); ``` if the assimliated promise rejects, then the downstream promise will also reject. ```js finduser().then(function (user) { return findcommentsbyauthor(user); }).then(function (comments) { // if `findcommentsbyauthor` fulfills, we'll have the value here }, function (reason) { // if `findcommentsbyauthor` rejects, we'll have the reason here }); ``` simple example -------------- synchronous example ```javascript let result; try { result = findresult(); // success } catch(reason) { // failure } ``` errback example ```js findresult(function(result, err){ if (err) { // failure } else { // success } }); ``` promise example; ```javascript findresult().then(function(result){ // success }, function(reason){ // failure }); ``` advanced example -------------- synchronous example ```javascript let author, books; try { author = findauthor(); books = findbooksbyauthor(author); // success } catch(reason) { // failure } ``` errback example ```js function foundbooks(books) { } function failure(reason) { } findauthor(function(author, err){ if (err) { failure(err); // failure } else { try { findboooksbyauthor(author, function(books, err) { if (err) { failure(err); } else { try { foundbooks(books); } catch(reason) { failure(reason); } } }); } catch(error) { failure(err); } // success } }); ``` promise example; ```javascript findauthor(). then(findbooksbyauthor). then(function(books){ // found books }).catch(function(reason){ // something went wrong }); ``` @method then @param {function} onfulfilled @param {function} onrejected useful for tooling. @return {promise} */ then: then, /** `catch` is simply sugar for `then(undefined, onrejection)` which makes it the same as the catch block of a try/catch statement. ```js function findauthor(){ throw new error('couldn't find that author'); } // synchronous try { findauthor(); } catch(reason) { // something went wrong } // async with promises findauthor().catch(function(reason){ // something went wrong }); ``` @method catch @param {function} onrejection useful for tooling. @return {promise} */ 'catch': function _catch(onrejection) { return this.then(null, onrejection); } }; function polyfill() { var local = undefined; if (typeof global !== 'undefined') { local = global; } else if (typeof self !== 'undefined') { local = self; } else { try { local = function('return this')(); } catch (e) { throw new error('polyfill failed because global object is unavailable in this environment'); } } var p = local.promise; if (p) { var promisetostring = null; try { promisetostring = object.prototype.tostring.call(p.resolve()); } catch (e) { // silently ignored } if (promisetostring === '[object promise]' && !p.cast) { return; } } local.promise = promise; } // strange compat.. promise.polyfill = polyfill; promise.promise = promise; return promise; }))); }).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"_process":3}],2:[function(_dereq_,module,exports){ // copyright joyent, inc. and other node contributors. // // permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "software"), to deal in the software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the software, and to permit // persons to whom the software is furnished to do so, subject to the // following conditions: // // the above copyright notice and this permission notice shall be included // in all copies or substantial portions of the software. // // the software is provided "as is", without warranty of any kind, express // or implied, including but not limited to the warranties of // merchantability, fitness for a particular purpose and noninfringement. in // no event shall the authors or copyright holders be liable for any claim, // damages or other liability, whether in an action of contract, tort or // otherwise, arising from, out of or in connection with the software or the // use or other dealings in the software. function eventemitter() { this._events = this._events || {}; this._maxlisteners = this._maxlisteners || undefined; } module.exports = eventemitter; // backwards-compat with node 0.10.x eventemitter.eventemitter = eventemitter; eventemitter.prototype._events = undefined; eventemitter.prototype._maxlisteners = undefined; // by default eventemitters will print a warning if more than 10 listeners are // added to it. this is a useful default which helps finding memory leaks. eventemitter.defaultmaxlisteners = 10; // obviously not all emitters should be limited to 10. this function allows // that to be increased. set to zero for unlimited. eventemitter.prototype.setmaxlisteners = function(n) { if (!isnumber(n) || n < 0 || isnan(n)) throw typeerror('n must be a positive number'); this._maxlisteners = n; return this; }; eventemitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // if there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isobject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof error) { throw er; // unhandled 'error' event } else { // at least give some kind of context to the user var err = new error('uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isundefined(handler)) return false; if (isfunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isobject(handler)) { args = array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; eventemitter.prototype.addlistener = function(type, listener) { var m; if (!isfunction(listener)) throw typeerror('listener must be a function'); if (!this._events) this._events = {}; // to avoid recursion in the case that type === "newlistener"! before // adding it to the listeners, first emit "newlistener". if (this._events.newlistener) this.emit('newlistener', type, isfunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // optimize the case of one listener. don't need the extra array object. this._events[type] = listener; else if (isobject(this._events[type])) // if we've already got an array, just append. this._events[type].push(listener); else // adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // check for listener leak if (isobject(this._events[type]) && !this._events[type].warned) { if (!isundefined(this._maxlisteners)) { m = this._maxlisteners; } else { m = eventemitter.defaultmaxlisteners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible eventemitter memory ' + 'leak detected. %d listeners added. ' + 'use emitter.setmaxlisteners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in ie 10 console.trace(); } } } return this; }; eventemitter.prototype.on = eventemitter.prototype.addlistener; eventemitter.prototype.once = function(type, listener) { if (!isfunction(listener)) throw typeerror('listener must be a function'); var fired = false; function g() { this.removelistener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removelistener' event iff the listener was removed eventemitter.prototype.removelistener = function(type, listener) { var list, position, length, i; if (!isfunction(listener)) throw typeerror('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isfunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removelistener) this.emit('removelistener', type, listener); } else if (isobject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removelistener) this.emit('removelistener', type, listener); } return this; }; eventemitter.prototype.removealllisteners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removelistener, no need to emit if (!this._events.removelistener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removelistener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removelistener') continue; this.removealllisteners(key); } this.removealllisteners('removelistener'); this._events = {}; return this; } listeners = this._events[type]; if (isfunction(listeners)) { this.removelistener(type, listeners); } else if (listeners) { // lifo order while (listeners.length) this.removelistener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; eventemitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isfunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; eventemitter.prototype.listenercount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isfunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; eventemitter.listenercount = function(emitter, type) { return emitter.listenercount(type); }; function isfunction(arg) { return typeof arg === 'function'; } function isnumber(arg) { return typeof arg === 'number'; } function isobject(arg) { return typeof arg === 'object' && arg !== null; } function isundefined(arg) { return arg === void 0; } },{}],3:[function(_dereq_,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. but we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. it's inside a // function because try/catches deoptimize in certain engines. var cachedsettimeout; var cachedcleartimeout; function defaultsettimout() { throw new error('settimeout has not been defined'); } function defaultcleartimeout () { throw new error('cleartimeout has not been defined'); } (function () { try { if (typeof settimeout === 'function') { cachedsettimeout = settimeout; } else { cachedsettimeout = defaultsettimout; } } catch (e) { cachedsettimeout = defaultsettimout; } try { if (typeof cleartimeout === 'function') { cachedcleartimeout = cleartimeout; } else { cachedcleartimeout = defaultcleartimeout; } } catch (e) { cachedcleartimeout = defaultcleartimeout; } } ()) function runtimeout(fun) { if (cachedsettimeout === settimeout) { //normal enviroments in sane situations return settimeout(fun, 0); } // if settimeout wasn't available but was latter defined if ((cachedsettimeout === defaultsettimout || !cachedsettimeout) && settimeout) { cachedsettimeout = settimeout; return settimeout(fun, 0); } try { // when when somebody has screwed with settimeout but no i.e. maddness return cachedsettimeout(fun, 0); } catch(e){ try { // when we are in i.e. but the script has been evaled so i.e. doesn't trust the global object when called normally return cachedsettimeout.call(null, fun, 0); } catch(e){ // 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 return cachedsettimeout.call(this, fun, 0); } } } function runcleartimeout(marker) { if (cachedcleartimeout === cleartimeout) { //normal enviroments in sane situations return cleartimeout(marker); } // if cleartimeout wasn't available but was latter defined if ((cachedcleartimeout === defaultcleartimeout || !cachedcleartimeout) && cleartimeout) { cachedcleartimeout = cleartimeout; return cleartimeout(marker); } try { // when when somebody has screwed with settimeout but no i.e. maddness return cachedcleartimeout(marker); } catch (e){ try { // when we are in i.e. but the script has been evaled so i.e. doesn't trust the global object when called normally return cachedcleartimeout.call(null, marker); } catch (e){ // 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. // some versions of i.e. have different rules for cleartimeout vs settimeout return cachedcleartimeout.call(this, marker); } } } var queue = []; var draining = false; var currentqueue; var queueindex = -1; function cleanupnexttick() { if (!draining || !currentqueue) { return; } draining = false; if (currentqueue.length) { queue = currentqueue.concat(queue); } else { queueindex = -1; } if (queue.length) { drainqueue(); } } function drainqueue() { if (draining) { return; } var timeout = runtimeout(cleanupnexttick); draining = true; var len = queue.length; while(len) { currentqueue = queue; queue = []; while (++queueindex < len) { if (currentqueue) { currentqueue[queueindex].run(); } } queueindex = -1; len = queue.length; } currentqueue = null; draining = false; runcleartimeout(timeout); } process.nexttick = function (fun) { var args = new array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new item(fun, args)); if (queue.length === 1 && !draining) { runtimeout(drainqueue); } }; // v8 likes predictible objects function item(fun, array) { this.fun = fun; this.array = array; } item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addlistener = noop; process.once = noop; process.off = noop; process.removelistener = noop; process.removealllisteners = noop; process.emit = noop; process.prependlistener = noop; process.prependoncelistener = noop; process.listeners = function (name) { return [] } process.binding = function (name) { throw new error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],4:[function(_dereq_,module,exports){ var bundlefn = arguments[3]; var sources = arguments[4]; var cache = arguments[5]; var stringify = json.stringify; module.exports = function (fn, options) { var wkey; var cachekeys = object.keys(cache); for (var i = 0, l = cachekeys.length; i < l; i++) { var key = cachekeys[i]; var exp = cache[key].exports; // using babel as a transpiler to use esmodule, the export will always // be an object with the default export as a property of it. to ensure // the existing api and babel esmodule exports are both supported we // check for both if (exp === fn || exp && exp.default === fn) { wkey = key; break; } } if (!wkey) { wkey = math.floor(math.pow(16, 8) * math.random()).tostring(16); var wcache = {}; for (var i = 0, l = cachekeys.length; i < l; i++) { var key = cachekeys[i]; wcache[key] = key; } sources[wkey] = [ function(['require','module','exports'], '(' + fn + ')(self)'), wcache ]; } var skey = math.floor(math.pow(16, 8) * math.random()).tostring(16); var scache = {}; scache[wkey] = wkey; sources[skey] = [ function(['require'], ( // try to call default if defined to also support babel esmodule // exports 'var f = require(' + stringify(wkey) + ');' + '(f.default ? f.default : f)(self);' )), scache ]; var workersources = {}; resolvesources(skey); function resolvesources(key) { workersources[key] = true; for (var deppath in sources[key][1]) { var depkey = sources[key][1][deppath]; if (!workersources[depkey]) { resolvesources(depkey); } } } var src = '(' + bundlefn + ')({' + object.keys(workersources).map(function (key) { return stringify(key) + ':[' + sources[key][0] + ',' + stringify(sources[key][1]) + ']' ; }).join(',') + '},{},[' + stringify(skey) + '])' ; var url = window.url || window.webkiturl || window.mozurl || window.msurl; var blob = new blob([src], { type: 'text/javascript' }); if (options && options.bare) { return blob; } var workerurl = url.createobjecturl(blob); var worker = new worker(workerurl); worker.objecturl = workerurl; return worker; }; },{}],5:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); exports.createdefaultconfig = createdefaultconfig; /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var defaultconfig = exports.defaultconfig = { enableworker: false, enablestashbuffer: true, stashinitialsize: undefined, islive: false, lazyload: true, lazyloadmaxduration: 3 * 60, lazyloadrecoverduration: 30, deferloadaftersourceopen: true, // autocleanupsourcebuffer: default as false, leave unspecified autocleanupmaxbackwardduration: 3 * 60, autocleanupminbackwardduration: 2 * 60, statisticsinforeportinterval: 600, fixaudiotimestampgap: true, accurateseek: false, seektype: 'range', // [range, param, custom] seekparamstart: 'bstart', seekparamend: 'bend', rangeloadzerostart: false, customseekhandler: undefined, reuseredirectedurl: false // referrerpolicy: leave as unspecified }; function createdefaultconfig() { return object.assign({}, defaultconfig); } },{}],6:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _iocontroller = _dereq_('../io/io-controller.js'); var _iocontroller2 = _interoprequiredefault(_iocontroller); var _config = _dereq_('../config.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var features = function () { function features() { _classcallcheck(this, features); } _createclass(features, null, [{ key: 'supportmseh264playback', value: function supportmseh264playback() { return window.mediasource && window.mediasource.istypesupported('video/mp4; codecs="avc1.42e01e,mp4a.40.2"'); } }, { key: 'supportnetworkstreamio', value: function supportnetworkstreamio() { var ioctl = new _iocontroller2.default({}, (0, _config.createdefaultconfig)()); var loadertype = ioctl.loadertype; ioctl.destroy(); return loadertype == 'fetch-stream-loader' || loadertype == 'xhr-moz-chunked-loader'; } }, { key: 'getnetworkloadertypename', value: function getnetworkloadertypename() { var ioctl = new _iocontroller2.default({}, (0, _config.createdefaultconfig)()); var loadertype = ioctl.loadertype; ioctl.destroy(); return loadertype; } }, { key: 'supportnativemediaplayback', value: function supportnativemediaplayback(mimetype) { if (features.videoelement == undefined) { features.videoelement = window.document.createelement('video'); } var canplay = features.videoelement.canplaytype(mimetype); return canplay === 'probably' || canplay == 'maybe'; } }, { key: 'getfeaturelist', value: function getfeaturelist() { var features = { mseflvplayback: false, mseliveflvplayback: false, networkstreamio: false, networkloadername: '', nativemp4h264playback: false, nativewebmvp8playback: false, nativewebmvp9playback: false }; features.mseflvplayback = features.supportmseh264playback(); features.networkstreamio = features.supportnetworkstreamio(); features.networkloadername = features.getnetworkloadertypename(); features.mseliveflvplayback = features.mseflvplayback && features.networkstreamio; features.nativemp4h264playback = features.supportnativemediaplayback('video/mp4; codecs="avc1.42001e, mp4a.40.2"'); features.nativewebmvp8playback = features.supportnativemediaplayback('video/webm; codecs="vp8.0, vorbis"'); features.nativewebmvp9playback = features.supportnativemediaplayback('video/webm; codecs="vp9"'); return features; } }]); return features; }(); exports.default = features; },{"../config.js":5,"../io/io-controller.js":23}],7:[function(_dereq_,module,exports){ "use strict"; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var mediainfo = function () { function mediainfo() { _classcallcheck(this, mediainfo); this.mimetype = null; this.duration = null; this.hasaudio = null; this.hasvideo = null; this.audiocodec = null; this.videocodec = null; this.audiodatarate = null; this.videodatarate = null; this.audiosamplerate = null; this.audiochannelcount = 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; this.metadata = null; this.segments = null; // mediainfo[] this.segmentcount = null; this.haskeyframesindex = null; this.keyframesindex = null; } _createclass(mediainfo, [{ key: "iscomplete", value: function iscomplete() { var audioinfocomplete = this.hasaudio === false || this.hasaudio === true && this.audiocodec != null && this.audiosamplerate != null && this.audiochannelcount != null; 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; // keyframesindex may not be present return this.mimetype != null && this.duration != null && this.metadata != null && this.haskeyframesindex != null && audioinfocomplete && videoinfocomplete; } }, { key: "isseekable", value: function isseekable() { return this.haskeyframesindex === true; } }, { key: "getnearestkeyframe", value: function getnearestkeyframe(milliseconds) { if (this.keyframesindex == null) { return null; } var table = this.keyframesindex; var keyframeidx = this._search(table.times, milliseconds); return { index: keyframeidx, milliseconds: table.times[keyframeidx], fileposition: table.filepositions[keyframeidx] }; } }, { key: "_search", value: function _search(list, value) { var idx = 0; var last = list.length - 1; var mid = 0; var lbound = 0; var ubound = last; if (value < list[0]) { idx = 0; lbound = ubound + 1; // skip search } while (lbound <= ubound) { mid = lbound + math.floor((ubound - lbound) / 2); if (mid === last || value >= list[mid] && value < list[mid + 1]) { idx = mid; break; } else if (list[mid] < value) { lbound = mid + 1; } else { ubound = mid - 1; } } return idx; } }]); return mediainfo; }(); exports.default = mediainfo; },{}],8:[function(_dereq_,module,exports){ "use strict"; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // represents an media sample (audio / video) var sampleinfo = exports.sampleinfo = function sampleinfo(dts, pts, duration, originaldts, issync) { _classcallcheck(this, sampleinfo); this.dts = dts; this.pts = pts; this.duration = duration; this.originaldts = originaldts; this.issyncpoint = issync; this.fileposition = null; }; // media segment concept is defined in media source extensions spec. // particularly in iso bmff format, an media segment contains a moof box followed by a mdat box. var mediasegmentinfo = exports.mediasegmentinfo = function () { function mediasegmentinfo() { _classcallcheck(this, mediasegmentinfo); this.begindts = 0; this.enddts = 0; this.beginpts = 0; this.endpts = 0; this.originalbegindts = 0; this.originalenddts = 0; this.syncpoints = []; // sampleinfo[n], for video idr frames only this.firstsample = null; // sampleinfo this.lastsample = null; // sampleinfo } _createclass(mediasegmentinfo, [{ key: "appendsyncpoint", value: function appendsyncpoint(sampleinfo) { // also called random access point sampleinfo.issyncpoint = true; this.syncpoints.push(sampleinfo); } }]); return mediasegmentinfo; }(); // ordered list for recording video idr frames, sorted by originaldts var idrsamplelist = exports.idrsamplelist = function () { function idrsamplelist() { _classcallcheck(this, idrsamplelist); this._list = []; } _createclass(idrsamplelist, [{ key: "clear", value: function clear() { this._list = []; } }, { key: "appendarray", value: function appendarray(syncpoints) { var list = this._list; if (syncpoints.length === 0) { return; } if (list.length > 0 && syncpoints[0].originaldts < list[list.length - 1].originaldts) { this.clear(); } array.prototype.push.apply(list, syncpoints); } }, { key: "getlastsyncpointbeforedts", value: function getlastsyncpointbeforedts(dts) { if (this._list.length == 0) { return null; } var list = this._list; var idx = 0; var last = list.length - 1; var mid = 0; var lbound = 0; var ubound = last; if (dts < list[0].dts) { idx = 0; lbound = ubound + 1; } while (lbound <= ubound) { mid = lbound + math.floor((ubound - lbound) / 2); if (mid === last || dts >= list[mid].dts && dts < list[mid + 1].dts) { idx = mid; break; } else if (list[mid].dts < dts) { lbound = mid + 1; } else { ubound = mid - 1; } } return this._list[idx]; } }]); return idrsamplelist; }(); // data structure for recording information of media segments in single track. var mediasegmentinfolist = exports.mediasegmentinfolist = function () { function mediasegmentinfolist(type) { _classcallcheck(this, mediasegmentinfolist); this._type = type; this._list = []; this._lastappendlocation = -1; // cached last insert location } _createclass(mediasegmentinfolist, [{ key: "isempty", value: function isempty() { return this._list.length === 0; } }, { key: "clear", value: function clear() { this._list = []; this._lastappendlocation = -1; } }, { key: "_searchnearestsegmentbefore", value: function _searchnearestsegmentbefore(originalbegindts) { var list = this._list; if (list.length === 0) { return -2; } var last = list.length - 1; var mid = 0; var lbound = 0; var ubound = last; var idx = 0; if (originalbegindts < list[0].originalbegindts) { idx = -1; return idx; } while (lbound <= ubound) { mid = lbound + math.floor((ubound - lbound) / 2); if (mid === last || originalbegindts > list[mid].lastsample.originaldts && originalbegindts < list[mid + 1].originalbegindts) { idx = mid; break; } else if (list[mid].originalbegindts < originalbegindts) { lbound = mid + 1; } else { ubound = mid - 1; } } return idx; } }, { key: "_searchnearestsegmentafter", value: function _searchnearestsegmentafter(originalbegindts) { return this._searchnearestsegmentbefore(originalbegindts) + 1; } }, { key: "append", value: function append(mediasegmentinfo) { var list = this._list; var msi = mediasegmentinfo; var lastappendidx = this._lastappendlocation; var insertidx = 0; 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)) { insertidx = lastappendidx + 1; // use cached location idx } else { if (list.length > 0) { insertidx = this._searchnearestsegmentbefore(msi.originalbegindts) + 1; } } this._lastappendlocation = insertidx; this._list.splice(insertidx, 0, msi); } }, { key: "getlastsegmentbefore", value: function getlastsegmentbefore(originalbegindts) { var idx = this._searchnearestsegmentbefore(originalbegindts); if (idx >= 0) { return this._list[idx]; } else { // -1 return null; } } }, { key: "getlastsamplebefore", value: function getlastsamplebefore(originalbegindts) { var segment = this.getlastsegmentbefore(originalbegindts); if (segment != null) { return segment.lastsample; } else { return null; } } }, { key: "getlastsyncpointbefore", value: function getlastsyncpointbefore(originalbegindts) { var segmentidx = this._searchnearestsegmentbefore(originalbegindts); var syncpoints = this._list[segmentidx].syncpoints; while (syncpoints.length === 0 && segmentidx > 0) { segmentidx--; syncpoints = this._list[segmentidx].syncpoints; } if (syncpoints.length > 0) { return syncpoints[syncpoints.length - 1]; } else { return null; } } }, { key: "type", get: function get() { return this._type; } }, { key: "length", get: function get() { return this._list.length; } }]); return mediasegmentinfolist; }(); },{}],9:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _browser = _dereq_('../utils/browser.js'); var _browser2 = _interoprequiredefault(_browser); var _mseevents = _dereq_('./mse-events.js'); var _mseevents2 = _interoprequiredefault(_mseevents); var _mediasegmentinfo = _dereq_('./media-segment-info.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } // media source extensions controller var msecontroller = function () { function msecontroller(config) { _classcallcheck(this, msecontroller); this.tag = 'msecontroller'; this._config = config; this._emitter = new _events2.default(); if (this._config.islive && this._config.autocleanupsourcebuffer == undefined) { // for live stream, do auto cleanup by default this._config.autocleanupsourcebuffer = true; } this.e = { onsourceopen: this._onsourceopen.bind(this), onsourceended: this._onsourceended.bind(this), onsourceclose: this._onsourceclose.bind(this), onsourcebuffererror: this._onsourcebuffererror.bind(this), onsourcebufferupdateend: this._onsourcebufferupdateend.bind(this) }; this._mediasource = null; this._mediasourceobjecturl = null; this._mediaelement = null; this._isbufferfull = false; this._haspendingeos = false; this._requiresetmediaduration = false; this._pendingmediaduration = 0; this._pendingsourcebufferinit = []; this._mimetypes = { video: null, audio: null }; this._sourcebuffers = { video: null, audio: null }; this._lastinitsegments = { video: null, audio: null }; this._pendingsegments = { video: [], audio: [] }; this._pendingremoveranges = { video: [], audio: [] }; this._idrlist = new _mediasegmentinfo.idrsamplelist(); } _createclass(msecontroller, [{ key: 'destroy', value: function destroy() { if (this._mediaelement || this._mediasource) { this.detachmediaelement(); } this.e = null; this._emitter.removealllisteners(); this._emitter = null; } }, { key: 'on', value: function on(event, listener) { this._emitter.addlistener(event, listener); } }, { key: 'off', value: function off(event, listener) { this._emitter.removelistener(event, listener); } }, { key: 'attachmediaelement', value: function attachmediaelement(mediaelement) { if (this._mediasource) { throw new _exception.illegalstateexception('mediasource has been attached to an htmlmediaelement!'); } var ms = this._mediasource = new window.mediasource(); ms.addeventlistener('sourceopen', this.e.onsourceopen); ms.addeventlistener('sourceended', this.e.onsourceended); ms.addeventlistener('sourceclose', this.e.onsourceclose); this._mediaelement = mediaelement; this._mediasourceobjecturl = window.url.createobjecturl(this._mediasource); mediaelement.src = this._mediasourceobjecturl; } }, { key: 'detachmediaelement', value: function detachmediaelement() { if (this._mediasource) { var ms = this._mediasource; for (var type in this._sourcebuffers) { // pending segments should be discard var ps = this._pendingsegments[type]; ps.splice(0, ps.length); this._pendingsegments[type] = null; this._pendingremoveranges[type] = null; this._lastinitsegments[type] = null; // remove all sourcebuffers var sb = this._sourcebuffers[type]; if (sb) { if (ms.readystate !== 'closed') { ms.removesourcebuffer(sb); sb.removeeventlistener('error', this.e.onsourcebuffererror); sb.removeeventlistener('updateend', this.e.onsourcebufferupdateend); } this._mimetypes[type] = null; this._sourcebuffers[type] = null; } } if (ms.readystate === 'open') { try { ms.endofstream(); } catch (error) { _logger2.default.e(this.tag, error.message); } } ms.removeeventlistener('sourceopen', this.e.onsourceopen); ms.removeeventlistener('sourceended', this.e.onsourceended); ms.removeeventlistener('sourceclose', this.e.onsourceclose); this._pendingsourcebufferinit = []; this._isbufferfull = false; this._idrlist.clear(); this._mediasource = null; } if (this._mediaelement) { this._mediaelement.src = ''; this._mediaelement.removeattribute('src'); this._mediaelement = null; } if (this._mediasourceobjecturl) { window.url.revokeobjecturl(this._mediasourceobjecturl); this._mediasourceobjecturl = null; } } }, { key: 'appendinitsegment', value: function appendinitsegment(initsegment, deferred) { if (!this._mediasource || this._mediasource.readystate !== 'open') { // sourcebuffer creation requires mediasource.readystate === 'open' // so we defer the sourcebuffer creation, until sourceopen event triggered this._pendingsourcebufferinit.push(initsegment); // make sure that this initsegment is in the front of pending segments queue this._pendingsegments[initsegment.type].push(initsegment); return; } var is = initsegment; var mimetype = '' + is.container; if (is.codec && is.codec.length > 0) { mimetype += ';codecs=' + is.codec; } var firstinitsegment = false; _logger2.default.v(this.tag, 'received initialization segment, mimetype: ' + mimetype); this._lastinitsegments[is.type] = is; if (mimetype !== this._mimetypes[is.type]) { if (!this._mimetypes[is.type]) { // empty, first chance create sourcebuffer firstinitsegment = true; try { var sb = this._sourcebuffers[is.type] = this._mediasource.addsourcebuffer(mimetype); sb.addeventlistener('error', this.e.onsourcebuffererror); sb.addeventlistener('updateend', this.e.onsourcebufferupdateend); } catch (error) { _logger2.default.e(this.tag, error.message); this._emitter.emit(_mseevents2.default.error, { code: error.code, msg: error.message }); return; } } else { _logger2.default.v(this.tag, 'notice: ' + is.type + ' mimetype changed, origin: ' + this._mimetypes[is.type] + ', target: ' + mimetype); } this._mimetypes[is.type] = mimetype; } if (!deferred) { // deferred means this initsegment has been pushed to pendingsegments queue this._pendingsegments[is.type].push(is); } if (!firstinitsegment) { // append immediately only if init segment in subsequence if (this._sourcebuffers[is.type] && !this._sourcebuffers[is.type].updating) { this._doappendsegments(); } } if (_browser2.default.safari && is.container === 'audio/mpeg' && is.mediaduration > 0) { // 'audio/mpeg' track under safari may cause mediaelement's duration to be nan // manually correct mediasource.duration to make progress bar seekable, and report right duration this._requiresetmediaduration = true; this._pendingmediaduration = is.mediaduration / 1000; // in seconds this._updatemediasourceduration(); } } }, { key: 'appendmediasegment', value: function appendmediasegment(mediasegment) { var ms = mediasegment; this._pendingsegments[ms.type].push(ms); if (this._config.autocleanupsourcebuffer && this._needcleanupsourcebuffer()) { this._docleanupsourcebuffer(); } var sb = this._sourcebuffers[ms.type]; if (sb && !sb.updating && !this._haspendingremoveranges()) { this._doappendsegments(); } } }, { key: 'seek', value: function seek(seconds) { // remove all appended buffers for (var type in this._sourcebuffers) { if (!this._sourcebuffers[type]) { continue; } // abort current buffer append algorithm var sb = this._sourcebuffers[type]; if (this._mediasource.readystate === 'open') { try { // if range removal algorithm is running, invalidstateerror will be throwed // ignore it. sb.abort(); } catch (error) { _logger2.default.e(this.tag, error.message); } } // idrlist should be clear this._idrlist.clear(); // pending segments should be discard var ps = this._pendingsegments[type]; ps.splice(0, ps.length); if (this._mediasource.readystate === 'closed') { // parent mediasource object has been detached from htmlmediaelement continue; } // record ranges to be remove from sourcebuffer for (var i = 0; i < sb.buffered.length; i++) { var start = sb.buffered.start(i); var end = sb.buffered.end(i); this._pendingremoveranges[type].push({ start: start, end: end }); } // if sb is not updating, let's remove ranges now! if (!sb.updating) { this._doremoveranges(); } // safari 10 may get invalidstateerror in the later appendbuffer() after sourcebuffer.remove() call // internal parser's state may be invalid at this time. re-append last initsegment to workaround. // related issue: https://bugs.webkit.org/show_bug.cgi?id=159230 if (_browser2.default.safari) { var lastinitsegment = this._lastinitsegments[type]; if (lastinitsegment) { this._pendingsegments[type].push(lastinitsegment); if (!sb.updating) { this._doappendsegments(); } } } } } }, { key: 'endofstream', value: function endofstream() { var ms = this._mediasource; var sb = this._sourcebuffers; if (!ms || ms.readystate !== 'open') { if (ms && ms.readystate === 'closed' && this._haspendingsegments()) { // if mediasource hasn't turned into open state, and there're pending segments // mark pending endofstream, defer call until all pending segments appended complete this._haspendingeos = true; } return; } if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) { // if any sourcebuffer is updating, defer endofstream operation // see _onsourcebufferupdateend() this._haspendingeos = true; } else { this._haspendingeos = false; // notify media data loading complete // this is helpful for correcting total duration to match last media segment // otherwise mediaelement's ended event may not be triggered ms.endofstream(); } } }, { key: 'getnearestkeyframe', value: function getnearestkeyframe(dts) { return this._idrlist.getlastsyncpointbeforedts(dts); } }, { key: '_needcleanupsourcebuffer', value: function _needcleanupsourcebuffer() { if (!this._config.autocleanupsourcebuffer) { return false; } var currenttime = this._mediaelement.currenttime; for (var type in this._sourcebuffers) { var sb = this._sourcebuffers[type]; if (sb) { var buffered = sb.buffered; if (buffered.length >= 1) { if (currenttime - buffered.start(0) >= this._config.autocleanupmaxbackwardduration) { return true; } } } } return false; } }, { key: '_docleanupsourcebuffer', value: function _docleanupsourcebuffer() { var currenttime = this._mediaelement.currenttime; for (var type in this._sourcebuffers) { var sb = this._sourcebuffers[type]; if (sb) { var buffered = sb.buffered; var doremove = false; for (var i = 0; i < buffered.length; i++) { var start = buffered.start(i); var end = buffered.end(i); if (start <= currenttime && currenttime < end + 3) { // padding 3 seconds if (currenttime - start >= this._config.autocleanupmaxbackwardduration) { doremove = true; var removeend = currenttime - this._config.autocleanupminbackwardduration; this._pendingremoveranges[type].push({ start: start, end: removeend }); } } else if (end < currenttime) { doremove = true; this._pendingremoveranges[type].push({ start: start, end: end }); } } if (doremove && !sb.updating) { this._doremoveranges(); } } } } }, { key: '_updatemediasourceduration', value: function _updatemediasourceduration() { var sb = this._sourcebuffers; if (this._mediaelement.readystate === 0 || this._mediasource.readystate !== 'open') { return; } if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) { return; } var current = this._mediasource.duration; var target = this._pendingmediaduration; if (target > 0 && (isnan(current) || target > current)) { _logger2.default.v(this.tag, 'update mediasource duration from ' + current + ' to ' + target); this._mediasource.duration = target; } this._requiresetmediaduration = false; this._pendingmediaduration = 0; } }, { key: '_doremoveranges', value: function _doremoveranges() { for (var type in this._pendingremoveranges) { if (!this._sourcebuffers[type] || this._sourcebuffers[type].updating) { continue; } var sb = this._sourcebuffers[type]; var ranges = this._pendingremoveranges[type]; while (ranges.length && !sb.updating) { var range = ranges.shift(); sb.remove(range.start, range.end); } } } }, { key: '_doappendsegments', value: function _doappendsegments() { var pendingsegments = this._pendingsegments; for (var type in pendingsegments) { if (!this._sourcebuffers[type] || this._sourcebuffers[type].updating) { continue; } if (pendingsegments[type].length > 0) { var segment = pendingsegments[type].shift(); if (segment.timestampoffset) { // for mpeg audio stream in mse, if unbuffered-seeking occurred // we need explicitly set timestampoffset to the desired point in timeline for mpeg sourcebuffer. var currentoffset = this._sourcebuffers[type].timestampoffset; var targetoffset = segment.timestampoffset / 1000; // in seconds var delta = math.abs(currentoffset - targetoffset); if (delta > 0.1) { // if time delta > 100ms _logger2.default.v(this.tag, 'update mpeg audio timestampoffset from ' + currentoffset + ' to ' + targetoffset); this._sourcebuffers[type].timestampoffset = targetoffset; } delete segment.timestampoffset; } if (!segment.data || segment.data.bytelength === 0) { // ignore empty buffer continue; } try { this._sourcebuffers[type].appendbuffer(segment.data); this._isbufferfull = false; if (type === 'video' && segment.hasownproperty('info')) { this._idrlist.appendarray(segment.info.syncpoints); } } catch (error) { this._pendingsegments[type].unshift(segment); if (error.code === 22) { // quotaexceedederror /* notice that firefox may not throw quotaexceedederror if sourcebuffer is full * currently we can only do lazy-load to avoid sourcebuffer become scattered. * sourcebuffer eviction policy may be changed in future version of firefox. * * related issues: * https://bugzilla.mozilla.org/show_bug.cgi?id=1279885 * https://bugzilla.mozilla.org/show_bug.cgi?id=1280023 */ // report buffer full, abort network io if (!this._isbufferfull) { this._emitter.emit(_mseevents2.default.buffer_full); } this._isbufferfull = true; } else { _logger2.default.e(this.tag, error.message); this._emitter.emit(_mseevents2.default.error, { code: error.code, msg: error.message }); } } } } } }, { key: '_onsourceopen', value: function _onsourceopen() { _logger2.default.v(this.tag, 'mediasource onsourceopen'); this._mediasource.removeeventlistener('sourceopen', this.e.onsourceopen); // deferred sourcebuffer creation / initialization if (this._pendingsourcebufferinit.length > 0) { var pendings = this._pendingsourcebufferinit; while (pendings.length) { var segment = pendings.shift(); this.appendinitsegment(segment, true); } } // there may be some pending media segments, append them if (this._haspendingsegments()) { this._doappendsegments(); } this._emitter.emit(_mseevents2.default.source_open); } }, { key: '_onsourceended', value: function _onsourceended() { // fired on endofstream _logger2.default.v(this.tag, 'mediasource onsourceended'); } }, { key: '_onsourceclose', value: function _onsourceclose() { // fired on detaching from media element _logger2.default.v(this.tag, 'mediasource onsourceclose'); if (this._mediasource && this.e != null) { this._mediasource.removeeventlistener('sourceopen', this.e.onsourceopen); this._mediasource.removeeventlistener('sourceended', this.e.onsourceended); this._mediasource.removeeventlistener('sourceclose', this.e.onsourceclose); } } }, { key: '_haspendingsegments', value: function _haspendingsegments() { var ps = this._pendingsegments; return ps.video.length > 0 || ps.audio.length > 0; } }, { key: '_haspendingremoveranges', value: function _haspendingremoveranges() { var prr = this._pendingremoveranges; return prr.video.length > 0 || prr.audio.length > 0; } }, { key: '_onsourcebufferupdateend', value: function _onsourcebufferupdateend() { if (this._requiresetmediaduration) { this._updatemediasourceduration(); } else if (this._haspendingremoveranges()) { this._doremoveranges(); } else if (this._haspendingsegments()) { this._doappendsegments(); } else if (this._haspendingeos) { this.endofstream(); } this._emitter.emit(_mseevents2.default.update_end); } }, { key: '_onsourcebuffererror', value: function _onsourcebuffererror(e) { _logger2.default.e(this.tag, 'sourcebuffer error: ' + e); // this error might not always be fatal, just ignore it } }]); return msecontroller; }(); exports.default = msecontroller; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var mseevents = { error: 'error', source_open: 'source_open', update_end: 'update_end', buffer_full: 'buffer_full' }; exports.default = mseevents; },{}],11:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _loggingcontrol = _dereq_('../utils/logging-control.js'); var _loggingcontrol2 = _interoprequiredefault(_loggingcontrol); var _transmuxingcontroller = _dereq_('./transmuxing-controller.js'); var _transmuxingcontroller2 = _interoprequiredefault(_transmuxingcontroller); var _transmuxingevents = _dereq_('./transmuxing-events.js'); var _transmuxingevents2 = _interoprequiredefault(_transmuxingevents); var _transmuxingworker = _dereq_('./transmuxing-worker.js'); var _transmuxingworker2 = _interoprequiredefault(_transmuxingworker); var _mediainfo = _dereq_('./media-info.js'); var _mediainfo2 = _interoprequiredefault(_mediainfo); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var transmuxer = function () { function transmuxer(mediadatasource, config) { _classcallcheck(this, transmuxer); this.tag = 'transmuxer'; this._emitter = new _events2.default(); if (config.enableworker && typeof worker !== 'undefined') { try { var work = _dereq_('webworkify'); this._worker = work(_transmuxingworker2.default); this._workerdestroying = false; this._worker.addeventlistener('message', this._onworkermessage.bind(this)); this._worker.postmessage({ cmd: 'init', param: [mediadatasource, config] }); this.e = { onloggingconfigchanged: this._onloggingconfigchanged.bind(this) }; _loggingcontrol2.default.registerlistener(this.e.onloggingconfigchanged); this._worker.postmessage({ cmd: 'logging_config', param: _loggingcontrol2.default.getconfig() }); } catch (error) { _logger2.default.e(this.tag, 'error while initialize transmuxing worker, fallback to inline transmuxing'); this._worker = null; this._controller = new _transmuxingcontroller2.default(mediadatasource, config); } } else { this._controller = new _transmuxingcontroller2.default(mediadatasource, config); } if (this._controller) { var ctl = this._controller; ctl.on(_transmuxingevents2.default.io_error, this._onioerror.bind(this)); ctl.on(_transmuxingevents2.default.demux_error, this._ondemuxerror.bind(this)); ctl.on(_transmuxingevents2.default.init_segment, this._oninitsegment.bind(this)); ctl.on(_transmuxingevents2.default.media_segment, this._onmediasegment.bind(this)); ctl.on(_transmuxingevents2.default.loading_complete, this._onloadingcomplete.bind(this)); ctl.on(_transmuxingevents2.default.recovered_early_eof, this._onrecoveredearlyeof.bind(this)); ctl.on(_transmuxingevents2.default.media_info, this._onmediainfo.bind(this)); ctl.on(_transmuxingevents2.default.statistics_info, this._onstatisticsinfo.bind(this)); ctl.on(_transmuxingevents2.default.recommend_seekpoint, this._onrecommendseekpoint.bind(this)); } } _createclass(transmuxer, [{ key: 'destroy', value: function destroy() { if (this._worker) { if (!this._workerdestroying) { this._workerdestroying = true; this._worker.postmessage({ cmd: 'destroy' }); _loggingcontrol2.default.removelistener(this.e.onloggingconfigchanged); this.e = null; } } else { this._controller.destroy(); this._controller = null; } this._emitter.removealllisteners(); this._emitter = null; } }, { key: 'on', value: function on(event, listener) { this._emitter.addlistener(event, listener); } }, { key: 'off', value: function off(event, listener) { this._emitter.removelistener(event, listener); } }, { key: 'hasworker', value: function hasworker() { return this._worker != null; } }, { key: 'open', value: function open() { if (this._worker) { this._worker.postmessage({ cmd: 'start' }); } else { this._controller.start(); } } }, { key: 'close', value: function close() { if (this._worker) { this._worker.postmessage({ cmd: 'stop' }); } else { this._controller.stop(); } } }, { key: 'seek', value: function seek(milliseconds) { if (this._worker) { this._worker.postmessage({ cmd: 'seek', param: milliseconds }); } else { this._controller.seek(milliseconds); } } }, { key: 'pause', value: function pause() { if (this._worker) { this._worker.postmessage({ cmd: 'pause' }); } else { this._controller.pause(); } } }, { key: 'resume', value: function resume() { if (this._worker) { this._worker.postmessage({ cmd: 'resume' }); } else { this._controller.resume(); } } }, { key: '_oninitsegment', value: function _oninitsegment(type, initsegment) { var _this = this; // do async invoke promise.resolve().then(function () { _this._emitter.emit(_transmuxingevents2.default.init_segment, type, initsegment); }); } }, { key: '_onmediasegment', value: function _onmediasegment(type, mediasegment) { var _this2 = this; promise.resolve().then(function () { _this2._emitter.emit(_transmuxingevents2.default.media_segment, type, mediasegment); }); } }, { key: '_onloadingcomplete', value: function _onloadingcomplete() { var _this3 = this; promise.resolve().then(function () { _this3._emitter.emit(_transmuxingevents2.default.loading_complete); }); } }, { key: '_onrecoveredearlyeof', value: function _onrecoveredearlyeof() { var _this4 = this; promise.resolve().then(function () { _this4._emitter.emit(_transmuxingevents2.default.recovered_early_eof); }); } }, { key: '_onmediainfo', value: function _onmediainfo(mediainfo) { var _this5 = this; promise.resolve().then(function () { _this5._emitter.emit(_transmuxingevents2.default.media_info, mediainfo); }); } }, { key: '_onstatisticsinfo', value: function _onstatisticsinfo(statisticsinfo) { var _this6 = this; promise.resolve().then(function () { _this6._emitter.emit(_transmuxingevents2.default.statistics_info, statisticsinfo); }); } }, { key: '_onioerror', value: function _onioerror(type, info) { var _this7 = this; promise.resolve().then(function () { _this7._emitter.emit(_transmuxingevents2.default.io_error, type, info); }); } }, { key: '_ondemuxerror', value: function _ondemuxerror(type, info) { var _this8 = this; promise.resolve().then(function () { _this8._emitter.emit(_transmuxingevents2.default.demux_error, type, info); }); } }, { key: '_onrecommendseekpoint', value: function _onrecommendseekpoint(milliseconds) { var _this9 = this; promise.resolve().then(function () { _this9._emitter.emit(_transmuxingevents2.default.recommend_seekpoint, milliseconds); }); } }, { key: '_onloggingconfigchanged', value: function _onloggingconfigchanged(config) { if (this._worker) { this._worker.postmessage({ cmd: 'logging_config', param: config }); } } }, { key: '_onworkermessage', value: function _onworkermessage(e) { var message = e.data; var data = message.data; if (message.msg === 'destroyed' || this._workerdestroying) { this._workerdestroying = false; this._worker.terminate(); this._worker = null; return; } switch (message.msg) { case _transmuxingevents2.default.init_segment: case _transmuxingevents2.default.media_segment: this._emitter.emit(message.msg, data.type, data.data); break; case _transmuxingevents2.default.loading_complete: case _transmuxingevents2.default.recovered_early_eof: this._emitter.emit(message.msg); break; case _transmuxingevents2.default.media_info: object.setprototypeof(data, _mediainfo2.default.prototype); this._emitter.emit(message.msg, data); break; case _transmuxingevents2.default.statistics_info: this._emitter.emit(message.msg, data); break; case _transmuxingevents2.default.io_error: case _transmuxingevents2.default.demux_error: this._emitter.emit(message.msg, data.type, data.info); break; case _transmuxingevents2.default.recommend_seekpoint: this._emitter.emit(message.msg, data); break; case 'logcat_callback': _logger2.default.emitter.emit('log', data.type, data.logcat); break; default: break; } } }]); return transmuxer; }(); exports.default = transmuxer; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _browser = _dereq_('../utils/browser.js'); var _browser2 = _interoprequiredefault(_browser); var _mediainfo = _dereq_('./media-info.js'); var _mediainfo2 = _interoprequiredefault(_mediainfo); var _flvdemuxer = _dereq_('../demux/flv-demuxer.js'); var _flvdemuxer2 = _interoprequiredefault(_flvdemuxer); var _mp4remuxer = _dereq_('../remux/mp4-remuxer.js'); var _mp4remuxer2 = _interoprequiredefault(_mp4remuxer); var _demuxerrors = _dereq_('../demux/demux-errors.js'); var _demuxerrors2 = _interoprequiredefault(_demuxerrors); var _iocontroller = _dereq_('../io/io-controller.js'); var _iocontroller2 = _interoprequiredefault(_iocontroller); var _transmuxingevents = _dereq_('./transmuxing-events.js'); var _transmuxingevents2 = _interoprequiredefault(_transmuxingevents); var _loader = _dereq_('../io/loader.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } // transmuxing (io, demuxing, remuxing) controller, with multipart support var transmuxingcontroller = function () { function transmuxingcontroller(mediadatasource, config) { _classcallcheck(this, transmuxingcontroller); this.tag = 'transmuxingcontroller'; this._emitter = new _events2.default(); this._config = config; // treat single part media as multipart media, which has only one segment if (!mediadatasource.segments) { mediadatasource.segments = [{ duration: mediadatasource.duration, filesize: mediadatasource.filesize, url: mediadatasource.url }]; } // fill in default io params if not exists if (typeof mediadatasource.cors !== 'boolean') { mediadatasource.cors = true; } if (typeof mediadatasource.withcredentials !== 'boolean') { mediadatasource.withcredentials = false; } this._mediadatasource = mediadatasource; this._currentsegmentindex = 0; var totalduration = 0; this._mediadatasource.segments.foreach(function (segment) { // timestampbase for each segment, and calculate total duration segment.timestampbase = totalduration; totalduration += segment.duration; // params needed by iocontroller segment.cors = mediadatasource.cors; segment.withcredentials = mediadatasource.withcredentials; // referrer policy control, if exist if (config.referrerpolicy) { segment.referrerpolicy = config.referrerpolicy; } }); if (!isnan(totalduration) && this._mediadatasource.duration !== totalduration) { this._mediadatasource.duration = totalduration; } this._mediainfo = null; this._demuxer = null; this._remuxer = null; this._ioctl = null; this._pendingseektime = null; this._pendingresolveseekpoint = null; this._statisticsreporter = null; } _createclass(transmuxingcontroller, [{ key: 'destroy', value: function destroy() { this._mediainfo = null; this._mediadatasource = null; if (this._statisticsreporter) { this._disablestatisticsreporter(); } if (this._ioctl) { this._ioctl.destroy(); this._ioctl = null; } if (this._demuxer) { this._demuxer.destroy(); this._demuxer = null; } if (this._remuxer) { this._remuxer.destroy(); this._remuxer = null; } this._emitter.removealllisteners(); this._emitter = null; } }, { key: 'on', value: function on(event, listener) { this._emitter.addlistener(event, listener); } }, { key: 'off', value: function off(event, listener) { this._emitter.removelistener(event, listener); } }, { key: 'start', value: function start() { this._loadsegment(0); this._enablestatisticsreporter(); } }, { key: '_loadsegment', value: function _loadsegment(segmentindex, optionalfrom) { this._currentsegmentindex = segmentindex; var datasource = this._mediadatasource.segments[segmentindex]; var ioctl = this._ioctl = new _iocontroller2.default(datasource, this._config, segmentindex); ioctl.onerror = this._onioexception.bind(this); ioctl.onseeked = this._onioseeked.bind(this); ioctl.oncomplete = this._oniocomplete.bind(this); ioctl.onredirect = this._onioredirect.bind(this); ioctl.onrecoveredearlyeof = this._oniorecoveredearlyeof.bind(this); if (optionalfrom) { this._demuxer.binddatasource(this._ioctl); } else { ioctl.ondataarrival = this._oninitchunkarrival.bind(this); } ioctl.open(optionalfrom); } }, { key: 'stop', value: function stop() { this._internalabort(); this._disablestatisticsreporter(); } }, { key: '_internalabort', value: function _internalabort() { if (this._ioctl) { this._ioctl.destroy(); this._ioctl = null; } } }, { key: 'pause', value: function pause() { // take a rest if (this._ioctl && this._ioctl.isworking()) { this._ioctl.pause(); this._disablestatisticsreporter(); } } }, { key: 'resume', value: function resume() { if (this._ioctl && this._ioctl.ispaused()) { this._ioctl.resume(); this._enablestatisticsreporter(); } } }, { key: 'seek', value: function seek(milliseconds) { if (this._mediainfo == null || !this._mediainfo.isseekable()) { return; } var targetsegmentindex = this._searchsegmentindexcontains(milliseconds); if (targetsegmentindex === this._currentsegmentindex) { // intra-segment seeking var segmentinfo = this._mediainfo.segments[targetsegmentindex]; if (segmentinfo == undefined) { // current segment loading started, but mediainfo hasn't received yet // wait for the metadata loaded, then seek to expected position this._pendingseektime = milliseconds; } else { var keyframe = segmentinfo.getnearestkeyframe(milliseconds); this._remuxer.seek(keyframe.milliseconds); this._ioctl.seek(keyframe.fileposition); // will be resolved in _onremuxermediasegmentarrival() this._pendingresolveseekpoint = keyframe.milliseconds; } } else { // cross-segment seeking var targetsegmentinfo = this._mediainfo.segments[targetsegmentindex]; if (targetsegmentinfo == undefined) { // target segment hasn't been loaded. we need metadata then seek to expected time this._pendingseektime = milliseconds; this._internalabort(); this._remuxer.seek(); this._remuxer.insertdiscontinuity(); this._loadsegment(targetsegmentindex); // here we wait for the metadata loaded, then seek to expected position } else { // we have target segment's metadata, direct seek to target position var _keyframe = targetsegmentinfo.getnearestkeyframe(milliseconds); this._internalabort(); this._remuxer.seek(milliseconds); this._remuxer.insertdiscontinuity(); this._demuxer.resetmediainfo(); this._demuxer.timestampbase = this._mediadatasource.segments[targetsegmentindex].timestampbase; this._loadsegment(targetsegmentindex, _keyframe.fileposition); this._pendingresolveseekpoint = _keyframe.milliseconds; this._reportsegmentmediainfo(targetsegmentindex); } } this._enablestatisticsreporter(); } }, { key: '_searchsegmentindexcontains', value: function _searchsegmentindexcontains(milliseconds) { var segments = this._mediadatasource.segments; var idx = segments.length - 1; for (var i = 0; i < segments.length; i++) { if (milliseconds < segments[i].timestampbase) { idx = i - 1; break; } } return idx; } }, { key: '_oninitchunkarrival', value: function _oninitchunkarrival(data, bytestart) { var _this = this; var probedata = null; var consumed = 0; if (bytestart > 0) { // iocontroller seeked immediately after opened, bytestart > 0 callback may received this._demuxer.binddatasource(this._ioctl); this._demuxer.timestampbase = this._mediadatasource.segments[this._currentsegmentindex].timestampbase; consumed = this._demuxer.parsechunks(data, bytestart); } else if ((probedata = _flvdemuxer2.default.probe(data)).match) { // always create new flvdemuxer this._demuxer = new _flvdemuxer2.default(probedata, this._config); if (!this._remuxer) { this._remuxer = new _mp4remuxer2.default(this._config); } var mds = this._mediadatasource; if (mds.duration != undefined && !isnan(mds.duration)) { this._demuxer.overridedduration = mds.duration; } if (typeof mds.hasaudio === 'boolean') { this._demuxer.overridedhasaudio = mds.hasaudio; } if (typeof mds.hasvideo === 'boolean') { this._demuxer.overridedhasvideo = mds.hasvideo; } this._demuxer.timestampbase = mds.segments[this._currentsegmentindex].timestampbase; this._demuxer.onerror = this._ondemuxexception.bind(this); this._demuxer.onmediainfo = this._onmediainfo.bind(this); this._remuxer.binddatasource(this._demuxer.binddatasource(this._ioctl)); this._remuxer.oninitsegment = this._onremuxerinitsegmentarrival.bind(this); this._remuxer.onmediasegment = this._onremuxermediasegmentarrival.bind(this); consumed = this._demuxer.parsechunks(data, bytestart); } else { probedata = null; _logger2.default.e(this.tag, 'non-flv, unsupported media type!'); promise.resolve().then(function () { _this._internalabort(); }); this._emitter.emit(_transmuxingevents2.default.demux_error, _demuxerrors2.default.format_unsupported, 'non-flv, unsupported media type'); consumed = 0; } return consumed; } }, { key: '_onmediainfo', value: function _onmediainfo(mediainfo) { var _this2 = this; if (this._mediainfo == null) { // store first segment's mediainfo as global mediainfo this._mediainfo = object.assign({}, mediainfo); this._mediainfo.keyframesindex = null; this._mediainfo.segments = []; this._mediainfo.segmentcount = this._mediadatasource.segments.length; object.setprototypeof(this._mediainfo, _mediainfo2.default.prototype); } var segmentinfo = object.assign({}, mediainfo); object.setprototypeof(segmentinfo, _mediainfo2.default.prototype); this._mediainfo.segments[this._currentsegmentindex] = segmentinfo; // notify mediainfo update this._reportsegmentmediainfo(this._currentsegmentindex); if (this._pendingseektime != null) { promise.resolve().then(function () { var target = _this2._pendingseektime; _this2._pendingseektime = null; _this2.seek(target); }); } } }, { key: '_onioseeked', value: function _onioseeked() { this._remuxer.insertdiscontinuity(); } }, { key: '_oniocomplete', value: function _oniocomplete(extradata) { var segmentindex = extradata; var nextsegmentindex = segmentindex + 1; if (nextsegmentindex < this._mediadatasource.segments.length) { this._internalabort(); this._loadsegment(nextsegmentindex); } else { this._remuxer.flushstashedsamples(); this._emitter.emit(_transmuxingevents2.default.loading_complete); this._disablestatisticsreporter(); } } }, { key: '_onioredirect', value: function _onioredirect(redirectedurl) { var segmentindex = this._ioctl.extradata; this._mediadatasource.segments[segmentindex].redirectedurl = redirectedurl; } }, { key: '_oniorecoveredearlyeof', value: function _oniorecoveredearlyeof() { this._emitter.emit(_transmuxingevents2.default.recovered_early_eof); } }, { key: '_onioexception', value: function _onioexception(type, info) { _logger2.default.e(this.tag, 'ioexception: type = ' + type + ', code = ' + info.code + ', msg = ' + info.msg); this._emitter.emit(_transmuxingevents2.default.io_error, type, info); this._disablestatisticsreporter(); } }, { key: '_ondemuxexception', value: function _ondemuxexception(type, info) { _logger2.default.e(this.tag, 'demuxexception: type = ' + type + ', info = ' + info); this._emitter.emit(_transmuxingevents2.default.demux_error, type, info); } }, { key: '_onremuxerinitsegmentarrival', value: function _onremuxerinitsegmentarrival(type, initsegment) { this._emitter.emit(_transmuxingevents2.default.init_segment, type, initsegment); } }, { key: '_onremuxermediasegmentarrival', value: function _onremuxermediasegmentarrival(type, mediasegment) { if (this._pendingseektime != null) { // media segments after new-segment cross-seeking should be dropped. return; } this._emitter.emit(_transmuxingevents2.default.media_segment, type, mediasegment); // resolve pending seekpoint if (this._pendingresolveseekpoint != null && type === 'video') { var syncpoints = mediasegment.info.syncpoints; var seekpoint = this._pendingresolveseekpoint; this._pendingresolveseekpoint = null; // safari: pass pts for recommend_seekpoint if (_browser2.default.safari && syncpoints.length > 0 && syncpoints[0].originaldts === seekpoint) { seekpoint = syncpoints[0].pts; } // else: use original dts (keyframe.milliseconds) this._emitter.emit(_transmuxingevents2.default.recommend_seekpoint, seekpoint); } } }, { key: '_enablestatisticsreporter', value: function _enablestatisticsreporter() { if (this._statisticsreporter == null) { this._statisticsreporter = self.setinterval(this._reportstatisticsinfo.bind(this), this._config.statisticsinforeportinterval); } } }, { key: '_disablestatisticsreporter', value: function _disablestatisticsreporter() { if (this._statisticsreporter) { self.clearinterval(this._statisticsreporter); this._statisticsreporter = null; } } }, { key: '_reportsegmentmediainfo', value: function _reportsegmentmediainfo(segmentindex) { var segmentinfo = this._mediainfo.segments[segmentindex]; var exportinfo = object.assign({}, segmentinfo); exportinfo.duration = this._mediainfo.duration; exportinfo.segmentcount = this._mediainfo.segmentcount; delete exportinfo.segments; delete exportinfo.keyframesindex; this._emitter.emit(_transmuxingevents2.default.media_info, exportinfo); } }, { key: '_reportstatisticsinfo', value: function _reportstatisticsinfo() { var info = {}; info.url = this._ioctl.currenturl; info.hasredirect = this._ioctl.hasredirect; if (info.hasredirect) { info.redirectedurl = this._ioctl.currentredirectedurl; } info.speed = this._ioctl.currentspeed; info.loadertype = this._ioctl.loadertype; info.currentsegmentindex = this._currentsegmentindex; info.totalsegmentcount = this._mediadatasource.segments.length; this._emitter.emit(_transmuxingevents2.default.statistics_info, info); } }]); return transmuxingcontroller; }(); exports.default = transmuxingcontroller; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var transmuxingevents = { io_error: 'io_error', demux_error: 'demux_error', init_segment: 'init_segment', media_segment: 'media_segment', loading_complete: 'loading_complete', recovered_early_eof: 'recovered_early_eof', media_info: 'media_info', statistics_info: 'statistics_info', recommend_seekpoint: 'recommend_seekpoint' }; exports.default = transmuxingevents; },{}],14:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _loggingcontrol = _dereq_('../utils/logging-control.js'); var _loggingcontrol2 = _interoprequiredefault(_loggingcontrol); var _polyfill = _dereq_('../utils/polyfill.js'); var _polyfill2 = _interoprequiredefault(_polyfill); var _transmuxingcontroller = _dereq_('./transmuxing-controller.js'); var _transmuxingcontroller2 = _interoprequiredefault(_transmuxingcontroller); var _transmuxingevents = _dereq_('./transmuxing-events.js'); var _transmuxingevents2 = _interoprequiredefault(_transmuxingevents); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } /* post message to worker: data: { cmd: string param: any } receive message from worker: data: { msg: string, data: any } */ var transmuxingworker = function transmuxingworker(self) { var tag = 'transmuxingworker'; var controller = null; var logcatlistener = onlogcatcallback.bind(this); _polyfill2.default.install(); self.addeventlistener('message', function (e) { switch (e.data.cmd) { case 'init': controller = new _transmuxingcontroller2.default(e.data.param[0], e.data.param[1]); controller.on(_transmuxingevents2.default.io_error, onioerror.bind(this)); controller.on(_transmuxingevents2.default.demux_error, ondemuxerror.bind(this)); controller.on(_transmuxingevents2.default.init_segment, oninitsegment.bind(this)); controller.on(_transmuxingevents2.default.media_segment, onmediasegment.bind(this)); controller.on(_transmuxingevents2.default.loading_complete, onloadingcomplete.bind(this)); controller.on(_transmuxingevents2.default.recovered_early_eof, onrecoveredearlyeof.bind(this)); controller.on(_transmuxingevents2.default.media_info, onmediainfo.bind(this)); controller.on(_transmuxingevents2.default.statistics_info, onstatisticsinfo.bind(this)); controller.on(_transmuxingevents2.default.recommend_seekpoint, onrecommendseekpoint.bind(this)); break; case 'destroy': if (controller) { controller.destroy(); controller = null; } self.postmessage({ msg: 'destroyed' }); break; case 'start': controller.start(); break; case 'stop': controller.stop(); break; case 'seek': controller.seek(e.data.param); break; case 'pause': controller.pause(); break; case 'resume': controller.resume(); break; case 'logging_config': { var config = e.data.param; _loggingcontrol2.default.applyconfig(config); if (config.enablecallback === true) { _loggingcontrol2.default.addloglistener(logcatlistener); } else { _loggingcontrol2.default.removeloglistener(logcatlistener); } break; } } }); function oninitsegment(type, initsegment) { var obj = { msg: _transmuxingevents2.default.init_segment, data: { type: type, data: initsegment } }; self.postmessage(obj, [initsegment.data]); // data: arraybuffer } function onmediasegment(type, mediasegment) { var obj = { msg: _transmuxingevents2.default.media_segment, data: { type: type, data: mediasegment } }; self.postmessage(obj, [mediasegment.data]); // data: arraybuffer } function onloadingcomplete() { var obj = { msg: _transmuxingevents2.default.loading_complete }; self.postmessage(obj); } function onrecoveredearlyeof() { var obj = { msg: _transmuxingevents2.default.recovered_early_eof }; self.postmessage(obj); } function onmediainfo(mediainfo) { var obj = { msg: _transmuxingevents2.default.media_info, data: mediainfo }; self.postmessage(obj); } function onstatisticsinfo(statinfo) { var obj = { msg: _transmuxingevents2.default.statistics_info, data: statinfo }; self.postmessage(obj); } function onioerror(type, info) { self.postmessage({ msg: _transmuxingevents2.default.io_error, data: { type: type, info: info } }); } function ondemuxerror(type, info) { self.postmessage({ msg: _transmuxingevents2.default.demux_error, data: { type: type, info: info } }); } function onrecommendseekpoint(milliseconds) { self.postmessage({ msg: _transmuxingevents2.default.recommend_seekpoint, data: milliseconds }); } function onlogcatcallback(type, str) { self.postmessage({ msg: 'logcat_callback', data: { type: type, logcat: str } }); } }; /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ exports.default = transmuxingworker; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _utf8conv = _dereq_('../utils/utf8-conv.js'); var _utf8conv2 = _interoprequiredefault(_utf8conv); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var le = function () { var buf = new arraybuffer(2); new dataview(buf).setint16(0, 256, true); // little-endian write return new int16array(buf)[0] === 256; // platform-spec read, if equal then le }(); var amf = function () { function amf() { _classcallcheck(this, amf); } _createclass(amf, null, [{ key: 'parsescriptdata', value: function parsescriptdata(arraybuffer, dataoffset, datasize) { var data = {}; try { var name = amf.parsevalue(arraybuffer, dataoffset, datasize); var value = amf.parsevalue(arraybuffer, dataoffset + name.size, datasize - name.size); data[name.data] = value.data; } catch (e) { _logger2.default.e('amf', e.tostring()); } return data; } }, { key: 'parseobject', value: function parseobject(arraybuffer, dataoffset, datasize) { if (datasize < 3) { throw new _exception.illegalstateexception('data not enough when parse scriptdataobject'); } var name = amf.parsestring(arraybuffer, dataoffset, datasize); var value = amf.parsevalue(arraybuffer, dataoffset + name.size, datasize - name.size); var isobjectend = value.objectend; return { data: { name: name.data, value: value.data }, size: name.size + value.size, objectend: isobjectend }; } }, { key: 'parsevariable', value: function parsevariable(arraybuffer, dataoffset, datasize) { return amf.parseobject(arraybuffer, dataoffset, datasize); } }, { key: 'parsestring', value: function parsestring(arraybuffer, dataoffset, datasize) { if (datasize < 2) { throw new _exception.illegalstateexception('data not enough when parse string'); } var v = new dataview(arraybuffer, dataoffset, datasize); var length = v.getuint16(0, !le); var str = void 0; if (length > 0) { str = (0, _utf8conv2.default)(new uint8array(arraybuffer, dataoffset + 2, length)); } else { str = ''; } return { data: str, size: 2 + length }; } }, { key: 'parselongstring', value: function parselongstring(arraybuffer, dataoffset, datasize) { if (datasize < 4) { throw new _exception.illegalstateexception('data not enough when parse longstring'); } var v = new dataview(arraybuffer, dataoffset, datasize); var length = v.getuint32(0, !le); var str = void 0; if (length > 0) { str = (0, _utf8conv2.default)(new uint8array(arraybuffer, dataoffset + 4, length)); } else { str = ''; } return { data: str, size: 4 + length }; } }, { key: 'parsedate', value: function parsedate(arraybuffer, dataoffset, datasize) { if (datasize < 10) { throw new _exception.illegalstateexception('data size invalid when parse date'); } var v = new dataview(arraybuffer, dataoffset, datasize); var timestamp = v.getfloat64(0, !le); var localtimeoffset = v.getint16(8, !le); timestamp += localtimeoffset * 60 * 1000; // get utc time return { data: new date(timestamp), size: 8 + 2 }; } }, { key: 'parsevalue', value: function parsevalue(arraybuffer, dataoffset, datasize) { if (datasize < 1) { throw new _exception.illegalstateexception('data not enough when parse value'); } var v = new dataview(arraybuffer, dataoffset, datasize); var offset = 1; var type = v.getuint8(0); var value = void 0; var objectend = false; try { switch (type) { case 0: // number(double) type value = v.getfloat64(1, !le); offset += 8; break; case 1: { // boolean type var b = v.getuint8(1); value = b ? true : false; offset += 1; break; } case 2: { // string type var amfstr = amf.parsestring(arraybuffer, dataoffset + 1, datasize - 1); value = amfstr.data; offset += amfstr.size; break; } case 3: { // object(s) type value = {}; var terminal = 0; // workaround for malformed objects which has missing scriptdataobjectend if ((v.getuint32(datasize - 4, !le) & 0x00ffffff) === 9) { terminal = 3; } while (offset < datasize - 4) { // 4 === type(ui8) + scriptdataobjectend(ui24) var amfobj = amf.parseobject(arraybuffer, dataoffset + offset, datasize - offset - terminal); if (amfobj.objectend) break; value[amfobj.data.name] = amfobj.data.value; offset += amfobj.size; } if (offset <= datasize - 3) { var marker = v.getuint32(offset - 1, !le) & 0x00ffffff; if (marker === 9) { offset += 3; } } break; } case 8: { // ecma array type (mixed array) value = {}; offset += 4; // ecmaarraylength(ui32) var _terminal = 0; // workaround for malformed mixedarrays which has missing scriptdataobjectend if ((v.getuint32(datasize - 4, !le) & 0x00ffffff) === 9) { _terminal = 3; } while (offset < datasize - 8) { // 8 === type(ui8) + ecmaarraylength(ui32) + scriptdatavariableend(ui24) var amfvar = amf.parsevariable(arraybuffer, dataoffset + offset, datasize - offset - _terminal); if (amfvar.objectend) break; value[amfvar.data.name] = amfvar.data.value; offset += amfvar.size; } if (offset <= datasize - 3) { var _marker = v.getuint32(offset - 1, !le) & 0x00ffffff; if (_marker === 9) { offset += 3; } } break; } case 9: // scriptdataobjectend value = undefined; offset = 1; objectend = true; break; case 10: { // strict array type // scriptdatavalue[n]. note: according to video_file_format_spec_v10_1.pdf value = []; var strictarraylength = v.getuint32(1, !le); offset += 4; for (var i = 0; i < strictarraylength; i++) { var val = amf.parsevalue(arraybuffer, dataoffset + offset, datasize - offset); value.push(val.data); offset += val.size; } break; } case 11: { // date type var date = amf.parsedate(arraybuffer, dataoffset + 1, datasize - 1); value = date.data; offset += date.size; break; } case 12: { // long string type var amflongstr = amf.parsestring(arraybuffer, dataoffset + 1, datasize - 1); value = amflongstr.data; offset += amflongstr.size; break; } default: // ignore and skip offset = datasize; _logger2.default.w('amf', 'unsupported amf value type ' + type); } } catch (e) { _logger2.default.e('amf', e.tostring()); } return { data: value, size: offset, objectend: objectend }; } }]); return amf; }(); exports.default = amf; },{"../utils/exception.js":40,"../utils/logger.js":41,"../utils/utf8-conv.js":44}],16:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var demuxerrors = { ok: 'ok', format_error: 'formaterror', format_unsupported: 'formatunsupported', codec_unsupported: 'codecunsupported' }; exports.default = demuxerrors; },{}],17:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _exception = _dereq_('../utils/exception.js'); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } // exponential-golomb buffer decoder var expgolomb = function () { function expgolomb(uint8array) { _classcallcheck(this, expgolomb); this.tag = 'expgolomb'; this._buffer = uint8array; this._buffer_index = 0; this._total_bytes = uint8array.bytelength; this._total_bits = uint8array.bytelength * 8; this._current_word = 0; this._current_word_bits_left = 0; } _createclass(expgolomb, [{ key: 'destroy', value: function destroy() { this._buffer = null; } }, { key: '_fillcurrentword', value: function _fillcurrentword() { var buffer_bytes_left = this._total_bytes - this._buffer_index; if (buffer_bytes_left <= 0) throw new _exception.illegalstateexception('expgolomb: _fillcurrentword() but no bytes available'); var bytes_read = math.min(4, buffer_bytes_left); var word = new uint8array(4); word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read)); this._current_word = new dataview(word.buffer).getuint32(0, false); this._buffer_index += bytes_read; this._current_word_bits_left = bytes_read * 8; } }, { key: 'readbits', value: function readbits(bits) { if (bits > 32) throw new _exception.invalidargumentexception('expgolomb: readbits() bits exceeded max 32bits!'); if (bits <= this._current_word_bits_left) { var _result = this._current_word >>> 32 - bits; this._current_word <<= bits; this._current_word_bits_left -= bits; return _result; } var result = this._current_word_bits_left ? this._current_word : 0; result = result >>> 32 - this._current_word_bits_left; var bits_need_left = bits - this._current_word_bits_left; this._fillcurrentword(); var bits_read_next = math.min(bits_need_left, this._current_word_bits_left); var result2 = this._current_word >>> 32 - bits_read_next; this._current_word <<= bits_read_next; this._current_word_bits_left -= bits_read_next; result = result << bits_read_next | result2; return result; } }, { key: 'readbool', value: function readbool() { return this.readbits(1) === 1; } }, { key: 'readbyte', value: function readbyte() { return this.readbits(8); } }, { key: '_skipleadingzero', value: function _skipleadingzero() { var zero_count = void 0; for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) { if (0 !== (this._current_word & 0x80000000 >>> zero_count)) { this._current_word <<= zero_count; this._current_word_bits_left -= zero_count; return zero_count; } } this._fillcurrentword(); return zero_count + this._skipleadingzero(); } }, { key: 'readueg', value: function readueg() { // unsigned exponential golomb var leading_zeros = this._skipleadingzero(); return this.readbits(leading_zeros + 1) - 1; } }, { key: 'readseg', value: function readseg() { // signed exponential golomb var value = this.readueg(); if (value & 0x01) { return value + 1 >>> 1; } else { return -1 * (value >>> 1); } } }]); return expgolomb; }(); exports.default = expgolomb; },{"../utils/exception.js":40}],18:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _amfparser = _dereq_('./amf-parser.js'); var _amfparser2 = _interoprequiredefault(_amfparser); var _spsparser = _dereq_('./sps-parser.js'); var _spsparser2 = _interoprequiredefault(_spsparser); var _demuxerrors = _dereq_('./demux-errors.js'); var _demuxerrors2 = _interoprequiredefault(_demuxerrors); var _mediainfo = _dereq_('../core/media-info.js'); var _mediainfo2 = _interoprequiredefault(_mediainfo); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } function swap16(src) { return src >>> 8 & 0xff | (src & 0xff) << 8; } function swap32(src) { return (src & 0xff000000) >>> 24 | (src & 0x00ff0000) >>> 8 | (src & 0x0000ff00) << 8 | (src & 0x000000ff) << 24; } function readbig32(array, index) { return array[index] << 24 | array[index + 1] << 16 | array[index + 2] << 8 | array[index + 3]; } var flvdemuxer = function () { function flvdemuxer(probedata, config) { _classcallcheck(this, flvdemuxer); this.tag = 'flvdemuxer'; this._config = config; this._onerror = null; this._onmediainfo = null; this._ontrackmetadata = null; this._ondataavailable = null; this._dataoffset = probedata.dataoffset; this._firstparse = true; this._dispatch = false; this._hasaudio = probedata.hasaudiotrack; this._hasvideo = probedata.hasvideotrack; this._hasaudioflagoverrided = false; this._hasvideoflagoverrided = false; this._audioinitialmetadatadispatched = false; this._videoinitialmetadatadispatched = false; this._mediainfo = new _mediainfo2.default(); this._mediainfo.hasaudio = this._hasaudio; this._mediainfo.hasvideo = this._hasvideo; this._metadata = null; this._audiometadata = null; this._videometadata = null; this._nalulengthsize = 4; this._timestampbase = 0; // int32, in milliseconds this._timescale = 1000; this._duration = 0; // int32, in milliseconds this._durationoverrided = false; this._referenceframerate = { fixed: true, fps: 23.976, fps_num: 23976, fps_den: 1000 }; this._flvsoundratetable = [5500, 11025, 22050, 44100, 48000]; this._mpegsamplingrates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; this._mpegaudiov10sampleratetable = [44100, 48000, 32000, 0]; this._mpegaudiov20sampleratetable = [22050, 24000, 16000, 0]; this._mpegaudiov25sampleratetable = [11025, 12000, 8000, 0]; this._mpegaudiol1bitratetable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1]; this._mpegaudiol2bitratetable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1]; this._mpegaudiol3bitratetable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1]; this._videotrack = { type: 'video', id: 1, sequencenumber: 0, samples: [], length: 0 }; this._audiotrack = { type: 'audio', id: 2, sequencenumber: 0, samples: [], length: 0 }; this._littleendian = function () { var buf = new arraybuffer(2); new dataview(buf).setint16(0, 256, true); // little-endian write return new int16array(buf)[0] === 256; // platform-spec read, if equal then le }(); } _createclass(flvdemuxer, [{ key: 'destroy', value: function destroy() { this._mediainfo = null; this._metadata = null; this._audiometadata = null; this._videometadata = null; this._videotrack = null; this._audiotrack = null; this._onerror = null; this._onmediainfo = null; this._ontrackmetadata = null; this._ondataavailable = null; } }, { key: 'binddatasource', value: function binddatasource(loader) { loader.ondataarrival = this.parsechunks.bind(this); return this; } // prototype: function(type: string, metadata: any): void }, { key: 'resetmediainfo', value: function resetmediainfo() { this._mediainfo = new _mediainfo2.default(); } }, { key: '_isinitialmetadatadispatched', value: function _isinitialmetadatadispatched() { if (this._hasaudio && this._hasvideo) { // both audio & video return this._audioinitialmetadatadispatched && this._videoinitialmetadatadispatched; } if (this._hasaudio && !this._hasvideo) { // audio only return this._audioinitialmetadatadispatched; } if (!this._hasaudio && this._hasvideo) { // video only return this._videoinitialmetadatadispatched; } return false; } // function parsechunks(chunk: arraybuffer, bytestart: number): number; }, { key: 'parsechunks', value: function parsechunks(chunk, bytestart) { if (!this._onerror || !this._onmediainfo || !this._ontrackmetadata || !this._ondataavailable) { throw new _exception.illegalstateexception('flv: onerror & onmediainfo & ontrackmetadata & ondataavailable callback must be specified'); } var offset = 0; var le = this._littleendian; if (bytestart === 0) { // buffer with flv header if (chunk.bytelength > 13) { var probedata = flvdemuxer.probe(chunk); offset = probedata.dataoffset; } else { return 0; } } if (this._firstparse) { // handle previoustagsize0 before tag1 this._firstparse = false; if (bytestart + offset !== this._dataoffset) { _logger2.default.w(this.tag, 'first time parsing but chunk bytestart invalid!'); } var v = new dataview(chunk, offset); var prevtagsize0 = v.getuint32(0, !le); if (prevtagsize0 !== 0) { _logger2.default.w(this.tag, 'prevtagsize0 !== 0 !!!'); } offset += 4; } while (offset < chunk.bytelength) { this._dispatch = true; var _v = new dataview(chunk, offset); if (offset + 11 + 4 > chunk.bytelength) { // data not enough for parsing an flv tag break; } var tagtype = _v.getuint8(0); var datasize = _v.getuint32(0, !le) & 0x00ffffff; if (offset + 11 + datasize + 4 > chunk.bytelength) { // data not enough for parsing actual data body break; } if (tagtype !== 8 && tagtype !== 9 && tagtype !== 18) { _logger2.default.w(this.tag, 'unsupported tag type ' + tagtype + ', skipped'); // consume the whole tag (skip it) offset += 11 + datasize + 4; continue; } var ts2 = _v.getuint8(4); var ts1 = _v.getuint8(5); var ts0 = _v.getuint8(6); var ts3 = _v.getuint8(7); var timestamp = ts0 | ts1 << 8 | ts2 << 16 | ts3 << 24; var streamid = _v.getuint32(7, !le) & 0x00ffffff; if (streamid !== 0) { _logger2.default.w(this.tag, 'meet tag which has streamid != 0!'); } var dataoffset = offset + 11; switch (tagtype) { case 8: // audio this._parseaudiodata(chunk, dataoffset, datasize, timestamp); break; case 9: // video this._parsevideodata(chunk, dataoffset, datasize, timestamp, bytestart + offset); break; case 18: // scriptdataobject this._parsescriptdata(chunk, dataoffset, datasize); break; } var prevtagsize = _v.getuint32(11 + datasize, !le); if (prevtagsize !== 11 + datasize) { _logger2.default.w(this.tag, 'invalid prevtagsize ' + prevtagsize); } offset += 11 + datasize + 4; // tagbody + datasize + prevtagsize } // dispatch parsed frames to consumer (typically, the remuxer) if (this._isinitialmetadatadispatched()) { if (this._dispatch && (this._audiotrack.length || this._videotrack.length)) { this._ondataavailable(this._audiotrack, this._videotrack); } } return offset; // consumed bytes, just equals latest offset index } }, { key: '_parsescriptdata', value: function _parsescriptdata(arraybuffer, dataoffset, datasize) { var scriptdata = _amfparser2.default.parsescriptdata(arraybuffer, dataoffset, datasize); if (scriptdata.hasownproperty('onmetadata')) { if (scriptdata.onmetadata == null || _typeof(scriptdata.onmetadata) !== 'object') { _logger2.default.w(this.tag, 'invalid onmetadata structure!'); return; } if (this._metadata) { _logger2.default.w(this.tag, 'found another onmetadata tag!'); } this._metadata = scriptdata; var onmetadata = this._metadata.onmetadata; if (typeof onmetadata.hasaudio === 'boolean') { // hasaudio if (this._hasaudioflagoverrided === false) { this._hasaudio = onmetadata.hasaudio; this._mediainfo.hasaudio = this._hasaudio; } } if (typeof onmetadata.hasvideo === 'boolean') { // hasvideo if (this._hasvideoflagoverrided === false) { this._hasvideo = onmetadata.hasvideo; this._mediainfo.hasvideo = this._hasvideo; } } if (typeof onmetadata.audiodatarate === 'number') { // audiodatarate this._mediainfo.audiodatarate = onmetadata.audiodatarate; } if (typeof onmetadata.videodatarate === 'number') { // videodatarate this._mediainfo.videodatarate = onmetadata.videodatarate; } if (typeof onmetadata.width === 'number') { // width this._mediainfo.width = onmetadata.width; } if (typeof onmetadata.height === 'number') { // height this._mediainfo.height = onmetadata.height; } if (typeof onmetadata.duration === 'number') { // duration if (!this._durationoverrided) { var duration = math.floor(onmetadata.duration * this._timescale); this._duration = duration; this._mediainfo.duration = duration; } } else { this._mediainfo.duration = 0; } if (typeof onmetadata.framerate === 'number') { // framerate var fps_num = math.floor(onmetadata.framerate * 1000); if (fps_num > 0) { var fps = fps_num / 1000; this._referenceframerate.fixed = true; this._referenceframerate.fps = fps; this._referenceframerate.fps_num = fps_num; this._referenceframerate.fps_den = 1000; this._mediainfo.fps = fps; } } if (_typeof(onmetadata.keyframes) === 'object') { // keyframes this._mediainfo.haskeyframesindex = true; var keyframes = onmetadata.keyframes; this._mediainfo.keyframesindex = this._parsekeyframesindex(keyframes); onmetadata.keyframes = null; // keyframes has been extracted, remove it } else { this._mediainfo.haskeyframesindex = false; } this._dispatch = false; this._mediainfo.metadata = onmetadata; _logger2.default.v(this.tag, 'parsed onmetadata'); if (this._mediainfo.iscomplete()) { this._onmediainfo(this._mediainfo); } } } }, { key: '_parsekeyframesindex', value: function _parsekeyframesindex(keyframes) { var times = []; var filepositions = []; // ignore first keyframe which is actually avc sequence header (avcdecoderconfigurationrecord) for (var i = 1; i < keyframes.times.length; i++) { var time = this._timestampbase + math.floor(keyframes.times[i] * 1000); times.push(time); filepositions.push(keyframes.filepositions[i]); } return { times: times, filepositions: filepositions }; } }, { key: '_parseaudiodata', value: function _parseaudiodata(arraybuffer, dataoffset, datasize, tagtimestamp) { if (datasize <= 1) { _logger2.default.w(this.tag, 'flv: invalid audio packet, missing sounddata payload!'); return; } if (this._hasaudioflagoverrided === true && this._hasaudio === false) { // if hasaudio: false indicated explicitly in mediadatasource, // ignore all the audio packets return; } var le = this._littleendian; var v = new dataview(arraybuffer, dataoffset, datasize); var soundspec = v.getuint8(0); var soundformat = soundspec >>> 4; if (soundformat !== 2 && soundformat !== 10) { // mp3 or aac this._onerror(_demuxerrors2.default.codec_unsupported, 'flv: unsupported audio codec idx: ' + soundformat); return; } var soundrate = 0; var soundrateindex = (soundspec & 12) >>> 2; if (soundrateindex >= 0 && soundrateindex <= 4) { soundrate = this._flvsoundratetable[soundrateindex]; } else { this._onerror(_demuxerrors2.default.format_error, 'flv: invalid audio sample rate idx: ' + soundrateindex); return; } var soundsize = (soundspec & 2) >>> 1; // unused var soundtype = soundspec & 1; var meta = this._audiometadata; var track = this._audiotrack; if (!meta) { if (this._hasaudio === false && this._hasaudioflagoverrided === false) { this._hasaudio = true; this._mediainfo.hasaudio = true; } // initial metadata meta = this._audiometadata = {}; meta.type = 'audio'; meta.id = track.id; meta.timescale = this._timescale; meta.duration = this._duration; meta.audiosamplerate = soundrate; meta.channelcount = soundtype === 0 ? 1 : 2; } if (soundformat === 10) { // aac var aacdata = this._parseaacaudiodata(arraybuffer, dataoffset + 1, datasize - 1); if (aacdata == undefined) { return; } if (aacdata.packettype === 0) { // aac sequence header (audiospecificconfig) if (meta.config) { _logger2.default.w(this.tag, 'found another audiospecificconfig!'); } var misc = aacdata.data; meta.audiosamplerate = misc.samplingrate; meta.channelcount = misc.channelcount; meta.codec = misc.codec; meta.originalcodec = misc.originalcodec; meta.config = misc.config; // the decode result of an aac sample is 1024 pcm samples meta.refsampleduration = 1024 / meta.audiosamplerate * meta.timescale; _logger2.default.v(this.tag, 'parsed audiospecificconfig'); if (this._isinitialmetadatadispatched()) { // non-initial metadata, force dispatch (or flush) parsed frames to remuxer if (this._dispatch && (this._audiotrack.length || this._videotrack.length)) { this._ondataavailable(this._audiotrack, this._videotrack); } } else { this._audioinitialmetadatadispatched = true; } // then notify new metadata this._dispatch = false; this._ontrackmetadata('audio', meta); var mi = this._mediainfo; mi.audiocodec = meta.originalcodec; mi.audiosamplerate = meta.audiosamplerate; mi.audiochannelcount = meta.channelcount; if (mi.hasvideo) { if (mi.videocodec != null) { mi.mimetype = 'video/x-flv; codecs="' + mi.videocodec + ',' + mi.audiocodec + '"'; } } else { mi.mimetype = 'video/x-flv; codecs="' + mi.audiocodec + '"'; } if (mi.iscomplete()) { this._onmediainfo(mi); } } else if (aacdata.packettype === 1) { // aac raw frame data var dts = this._timestampbase + tagtimestamp; var aacsample = { unit: aacdata.data, length: aacdata.data.bytelength, dts: dts, pts: dts }; track.samples.push(aacsample); track.length += aacdata.data.length; } else { _logger2.default.e(this.tag, 'flv: unsupported aac data type ' + aacdata.packettype); } } else if (soundformat === 2) { // mp3 if (!meta.codec) { // we need metadata for mp3 audio track, extract info from frame header var _misc = this._parsemp3audiodata(arraybuffer, dataoffset + 1, datasize - 1, true); if (_misc == undefined) { return; } meta.audiosamplerate = _misc.samplingrate; meta.channelcount = _misc.channelcount; meta.codec = _misc.codec; meta.originalcodec = _misc.originalcodec; // the decode result of an mp3 sample is 1152 pcm samples meta.refsampleduration = 1152 / meta.audiosamplerate * meta.timescale; _logger2.default.v(this.tag, 'parsed mpeg audio frame header'); this._audioinitialmetadatadispatched = true; this._ontrackmetadata('audio', meta); var _mi = this._mediainfo; _mi.audiocodec = meta.codec; _mi.audiosamplerate = meta.audiosamplerate; _mi.audiochannelcount = meta.channelcount; _mi.audiodatarate = _misc.bitrate; if (_mi.hasvideo) { if (_mi.videocodec != null) { _mi.mimetype = 'video/x-flv; codecs="' + _mi.videocodec + ',' + _mi.audiocodec + '"'; } } else { _mi.mimetype = 'video/x-flv; codecs="' + _mi.audiocodec + '"'; } if (_mi.iscomplete()) { this._onmediainfo(_mi); } } // this packet is always a valid audio packet, extract it var data = this._parsemp3audiodata(arraybuffer, dataoffset + 1, datasize - 1, false); if (data == undefined) { return; } var _dts = this._timestampbase + tagtimestamp; var mp3sample = { unit: data, length: data.bytelength, dts: _dts, pts: _dts }; track.samples.push(mp3sample); track.length += data.length; } } }, { key: '_parseaacaudiodata', value: function _parseaacaudiodata(arraybuffer, dataoffset, datasize) { if (datasize <= 1) { _logger2.default.w(this.tag, 'flv: invalid aac packet, missing aacpackettype or/and data!'); return; } var result = {}; var array = new uint8array(arraybuffer, dataoffset, datasize); result.packettype = array[0]; if (array[0] === 0) { result.data = this._parseaacaudiospecificconfig(arraybuffer, dataoffset + 1, datasize - 1); } else { result.data = array.subarray(1); } return result; } }, { key: '_parseaacaudiospecificconfig', value: function _parseaacaudiospecificconfig(arraybuffer, dataoffset, datasize) { var array = new uint8array(arraybuffer, dataoffset, datasize); var config = null; /* audio object type: 0: null 1: aac main 2: aac lc 3: aac ssr (scalable sample rate) 4: aac ltp (long term prediction) 5: he-aac / sbr (spectral band replication) 6: aac scalable */ var audioobjecttype = 0; var originalaudioobjecttype = 0; var audioextensionobjecttype = null; var samplingindex = 0; var extensionsamplingindex = null; // 5 bits audioobjecttype = originalaudioobjecttype = array[0] >>> 3; // 4 bits samplingindex = (array[0] & 0x07) << 1 | array[1] >>> 7; if (samplingindex < 0 || samplingindex >= this._mpegsamplingrates.length) { this._onerror(_demuxerrors2.default.format_error, 'flv: aac invalid sampling frequency index!'); return; } var samplingfrequence = this._mpegsamplingrates[samplingindex]; // 4 bits var channelconfig = (array[1] & 0x78) >>> 3; if (channelconfig < 0 || channelconfig >= 8) { this._onerror(_demuxerrors2.default.format_error, 'flv: aac invalid channel configuration'); return; } if (audioobjecttype === 5) { // he-aac? // 4 bits extensionsamplingindex = (array[1] & 0x07) << 1 | array[2] >>> 7; // 5 bits audioextensionobjecttype = (array[2] & 0x7c) >>> 2; } // workarounds for various browsers var useragent = self.navigator.useragent.tolowercase(); if (useragent.indexof('firefox') !== -1) { // firefox: use sbr (he-aac) if freq less than 24khz if (samplingindex >= 6) { audioobjecttype = 5; config = new array(4); extensionsamplingindex = samplingindex - 3; } else { // use lc-aac audioobjecttype = 2; config = new array(2); extensionsamplingindex = samplingindex; } } else if (useragent.indexof('android') !== -1) { // android: always use lc-aac audioobjecttype = 2; config = new array(2); extensionsamplingindex = samplingindex; } else { // for other browsers, e.g. chrome... // always use he-aac to make it easier to switch aac codec profile audioobjecttype = 5; extensionsamplingindex = samplingindex; config = new array(4); if (samplingindex >= 6) { extensionsamplingindex = samplingindex - 3; } else if (channelconfig === 1) { // mono channel audioobjecttype = 2; config = new array(2); extensionsamplingindex = samplingindex; } } config[0] = audioobjecttype << 3; config[0] |= (samplingindex & 0x0f) >>> 1; config[1] = (samplingindex & 0x0f) << 7; config[1] |= (channelconfig & 0x0f) << 3; if (audioobjecttype === 5) { config[1] |= (extensionsamplingindex & 0x0f) >>> 1; config[2] = (extensionsamplingindex & 0x01) << 7; // extended audio object type: force to 2 (lc-aac) config[2] |= 2 << 2; config[3] = 0; } return { config: config, samplingrate: samplingfrequence, channelcount: channelconfig, codec: 'mp4a.40.' + audioobjecttype, originalcodec: 'mp4a.40.' + originalaudioobjecttype }; } }, { key: '_parsemp3audiodata', value: function _parsemp3audiodata(arraybuffer, dataoffset, datasize, requestheader) { if (datasize < 4) { _logger2.default.w(this.tag, 'flv: invalid mp3 packet, header missing!'); return; } var le = this._littleendian; var array = new uint8array(arraybuffer, dataoffset, datasize); var result = null; if (requestheader) { if (array[0] !== 0xff) { return; } var ver = array[1] >>> 3 & 0x03; var layer = (array[1] & 0x06) >> 1; var bitrate_index = (array[2] & 0xf0) >>> 4; var sampling_freq_index = (array[2] & 0x0c) >>> 2; var channel_mode = array[3] >>> 6 & 0x03; var channel_count = channel_mode !== 3 ? 2 : 1; var sample_rate = 0; var bit_rate = 0; var object_type = 34; // layer-3, listed in mpeg-4 audio object types var codec = 'mp3'; switch (ver) { case 0: // mpeg 2.5 sample_rate = this._mpegaudiov25sampleratetable[sampling_freq_index]; break; case 2: // mpeg 2 sample_rate = this._mpegaudiov20sampleratetable[sampling_freq_index]; break; case 3: // mpeg 1 sample_rate = this._mpegaudiov10sampleratetable[sampling_freq_index]; break; } switch (layer) { case 1: // layer 3 object_type = 34; if (bitrate_index < this._mpegaudiol3bitratetable.length) { bit_rate = this._mpegaudiol3bitratetable[bitrate_index]; } break; case 2: // layer 2 object_type = 33; if (bitrate_index < this._mpegaudiol2bitratetable.length) { bit_rate = this._mpegaudiol2bitratetable[bitrate_index]; } break; case 3: // layer 1 object_type = 32; if (bitrate_index < this._mpegaudiol1bitratetable.length) { bit_rate = this._mpegaudiol1bitratetable[bitrate_index]; } break; } result = { bitrate: bit_rate, samplingrate: sample_rate, channelcount: channel_count, codec: codec, originalcodec: codec }; } else { result = array; } return result; } }, { key: '_parsevideodata', value: function _parsevideodata(arraybuffer, dataoffset, datasize, tagtimestamp, tagposition) { if (datasize <= 1) { _logger2.default.w(this.tag, 'flv: invalid video packet, missing videodata payload!'); return; } if (this._hasvideoflagoverrided === true && this._hasvideo === false) { // if hasvideo: false indicated explicitly in mediadatasource, // ignore all the video packets return; } var spec = new uint8array(arraybuffer, dataoffset, datasize)[0]; var frametype = (spec & 240) >>> 4; var codecid = spec & 15; if (codecid !== 7) { this._onerror(_demuxerrors2.default.codec_unsupported, 'flv: unsupported codec in video frame: ' + codecid); return; } this._parseavcvideopacket(arraybuffer, dataoffset + 1, datasize - 1, tagtimestamp, tagposition, frametype); } }, { key: '_parseavcvideopacket', value: function _parseavcvideopacket(arraybuffer, dataoffset, datasize, tagtimestamp, tagposition, frametype) { if (datasize < 4) { _logger2.default.w(this.tag, 'flv: invalid avc packet, missing avcpackettype or/and compositiontime'); return; } var le = this._littleendian; var v = new dataview(arraybuffer, dataoffset, datasize); var packettype = v.getuint8(0); var cts_unsigned = v.getuint32(0, !le) & 0x00ffffff; var cts = cts_unsigned << 8 >> 8; // convert to 24-bit signed int if (packettype === 0) { // avcdecoderconfigurationrecord this._parseavcdecoderconfigurationrecord(arraybuffer, dataoffset + 4, datasize - 4); } else if (packettype === 1) { // one or more nalus this._parseavcvideodata(arraybuffer, dataoffset + 4, datasize - 4, tagtimestamp, tagposition, frametype, cts); } else if (packettype === 2) { // empty, avc end of sequence } else { this._onerror(_demuxerrors2.default.format_error, 'flv: invalid video packet type ' + packettype); return; } } }, { key: '_parseavcdecoderconfigurationrecord', value: function _parseavcdecoderconfigurationrecord(arraybuffer, dataoffset, datasize) { if (datasize < 7) { _logger2.default.w(this.tag, 'flv: invalid avcdecoderconfigurationrecord, lack of data!'); return; } var meta = this._videometadata; var track = this._videotrack; var le = this._littleendian; var v = new dataview(arraybuffer, dataoffset, datasize); if (!meta) { if (this._hasvideo === false && this._hasvideoflagoverrided === false) { this._hasvideo = true; this._mediainfo.hasvideo = true; } meta = this._videometadata = {}; meta.type = 'video'; meta.id = track.id; meta.timescale = this._timescale; meta.duration = this._duration; } else { if (typeof meta.avcc !== 'undefined') { _logger2.default.w(this.tag, 'found another avcdecoderconfigurationrecord!'); } } var version = v.getuint8(0); // configurationversion var avcprofile = v.getuint8(1); // avcprofileindication var profilecompatibility = v.getuint8(2); // profile_compatibility var avclevel = v.getuint8(3); // avclevelindication if (version !== 1 || avcprofile === 0) { this._onerror(_demuxerrors2.default.format_error, 'flv: invalid avcdecoderconfigurationrecord'); return; } this._nalulengthsize = (v.getuint8(4) & 3) + 1; // lengthsizeminusone if (this._nalulengthsize !== 3 && this._nalulengthsize !== 4) { // holy shit!!! this._onerror(_demuxerrors2.default.format_error, 'flv: strange nalulengthsizeminusone: ' + (this._nalulengthsize - 1)); return; } var spscount = v.getuint8(5) & 31; // numofsequenceparametersets if (spscount === 0) { this._onerror(_demuxerrors2.default.format_error, 'flv: invalid avcdecoderconfigurationrecord: no sps'); return; } else if (spscount > 1) { _logger2.default.w(this.tag, 'flv: strange avcdecoderconfigurationrecord: sps count = ' + spscount); } var offset = 6; for (var i = 0; i < spscount; i++) { var len = v.getuint16(offset, !le); // sequenceparametersetlength offset += 2; if (len === 0) { continue; } // notice: nalu without startcode header (00 00 00 01) var sps = new uint8array(arraybuffer, dataoffset + offset, len); offset += len; var config = _spsparser2.default.parsesps(sps); if (i !== 0) { // ignore other sps's config continue; } meta.codecwidth = config.codec_size.width; meta.codecheight = config.codec_size.height; meta.presentwidth = config.present_size.width; meta.presentheight = config.present_size.height; meta.profile = config.profile_string; meta.level = config.level_string; meta.bitdepth = config.bit_depth; meta.chromaformat = config.chroma_format; meta.sarratio = config.sar_ratio; meta.framerate = config.frame_rate; if (config.frame_rate.fixed === false || config.frame_rate.fps_num === 0 || config.frame_rate.fps_den === 0) { meta.framerate = this._referenceframerate; } var fps_den = meta.framerate.fps_den; var fps_num = meta.framerate.fps_num; meta.refsampleduration = meta.timescale * (fps_den / fps_num); var codecarray = sps.subarray(1, 4); var codecstring = 'avc1.'; for (var j = 0; j < 3; j++) { var h = codecarray[j].tostring(16); if (h.length < 2) { h = '0' + h; } codecstring += h; } meta.codec = codecstring; var mi = this._mediainfo; mi.width = meta.codecwidth; mi.height = meta.codecheight; mi.fps = meta.framerate.fps; mi.profile = meta.profile; mi.level = meta.level; mi.refframes = config.ref_frames; mi.chromaformat = config.chroma_format_string; mi.sarnum = meta.sarratio.width; mi.sarden = meta.sarratio.height; mi.videocodec = codecstring; if (mi.hasaudio) { if (mi.audiocodec != null) { mi.mimetype = 'video/x-flv; codecs="' + mi.videocodec + ',' + mi.audiocodec + '"'; } } else { mi.mimetype = 'video/x-flv; codecs="' + mi.videocodec + '"'; } if (mi.iscomplete()) { this._onmediainfo(mi); } } var ppscount = v.getuint8(offset); // numofpictureparametersets if (ppscount === 0) { this._onerror(_demuxerrors2.default.format_error, 'flv: invalid avcdecoderconfigurationrecord: no pps'); return; } else if (ppscount > 1) { _logger2.default.w(this.tag, 'flv: strange avcdecoderconfigurationrecord: pps count = ' + ppscount); } offset++; for (var _i = 0; _i < ppscount; _i++) { var _len = v.getuint16(offset, !le); // pictureparametersetlength offset += 2; if (_len === 0) { continue; } // pps is useless for extracting video information offset += _len; } meta.avcc = new uint8array(datasize); meta.avcc.set(new uint8array(arraybuffer, dataoffset, datasize), 0); _logger2.default.v(this.tag, 'parsed avcdecoderconfigurationrecord'); if (this._isinitialmetadatadispatched()) { // flush parsed frames if (this._dispatch && (this._audiotrack.length || this._videotrack.length)) { this._ondataavailable(this._audiotrack, this._videotrack); } } else { this._videoinitialmetadatadispatched = true; } // notify new metadata this._dispatch = false; this._ontrackmetadata('video', meta); } }, { key: '_parseavcvideodata', value: function _parseavcvideodata(arraybuffer, dataoffset, datasize, tagtimestamp, tagposition, frametype, cts) { var le = this._littleendian; var v = new dataview(arraybuffer, dataoffset, datasize); var units = [], length = 0; var offset = 0; var lengthsize = this._nalulengthsize; var dts = this._timestampbase + tagtimestamp; var keyframe = frametype === 1; // from flv frame type constants while (offset < datasize) { if (offset + 4 >= datasize) { _logger2.default.w(this.tag, 'malformed nalu near timestamp ' + dts + ', offset = ' + offset + ', datasize = ' + datasize); break; // data not enough for next nalu } // nalu with length-header (avc1) var nalusize = v.getuint32(offset, !le); // big-endian read if (lengthsize === 3) { nalusize >>>= 8; } if (nalusize > datasize - lengthsize) { _logger2.default.w(this.tag, 'malformed nalus near timestamp ' + dts + ', nalusize > datasize!'); return; } var unittype = v.getuint8(offset + lengthsize) & 0x1f; if (unittype === 5) { // idr keyframe = true; } var data = new uint8array(arraybuffer, dataoffset + offset, lengthsize + nalusize); var unit = { type: unittype, data: data }; units.push(unit); length += data.bytelength; offset += lengthsize + nalusize; } if (units.length) { var track = this._videotrack; var avcsample = { units: units, length: length, iskeyframe: keyframe, dts: dts, cts: cts, pts: dts + cts }; if (keyframe) { avcsample.fileposition = tagposition; } track.samples.push(avcsample); track.length += length; } } }, { key: 'ontrackmetadata', get: function get() { return this._ontrackmetadata; }, set: function set(callback) { this._ontrackmetadata = callback; } // prototype: function(mediainfo: mediainfo): void }, { key: 'onmediainfo', get: function get() { return this._onmediainfo; }, set: function set(callback) { this._onmediainfo = callback; } // prototype: function(type: number, info: string): void }, { key: 'onerror', get: function get() { return this._onerror; }, set: function set(callback) { this._onerror = callback; } // prototype: function(videotrack: any, audiotrack: any): void }, { key: 'ondataavailable', get: function get() { return this._ondataavailable; }, set: function set(callback) { this._ondataavailable = callback; } // timestamp base for output samples, must be in milliseconds }, { key: 'timestampbase', get: function get() { return this._timestampbase; }, set: function set(base) { this._timestampbase = base; } }, { key: 'overridedduration', get: function get() { return this._duration; } // force-override media duration. must be in milliseconds, int32 , set: function set(duration) { this._durationoverrided = true; this._duration = duration; this._mediainfo.duration = duration; } // force-override audio track present flag, boolean }, { key: 'overridedhasaudio', set: function set(hasaudio) { this._hasaudioflagoverrided = true; this._hasaudio = hasaudio; this._mediainfo.hasaudio = hasaudio; } // force-override video track present flag, boolean }, { key: 'overridedhasvideo', set: function set(hasvideo) { this._hasvideoflagoverrided = true; this._hasvideo = hasvideo; this._mediainfo.hasvideo = hasvideo; } }], [{ key: 'probe', value: function probe(buffer) { var data = new uint8array(buffer); var mismatch = { match: false }; if (data[0] !== 0x46 || data[1] !== 0x4c || data[2] !== 0x56 || data[3] !== 0x01) { return mismatch; } var hasaudio = (data[4] & 4) >>> 2 !== 0; var hasvideo = (data[4] & 1) !== 0; var offset = readbig32(data, 5); if (offset < 9) { return mismatch; } return { match: true, consumed: offset, dataoffset: offset, hasaudiotrack: hasaudio, hasvideotrack: hasvideo }; } }]); return flvdemuxer; }(); exports.default = flvdemuxer; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _expgolomb = _dereq_('./exp-golomb.js'); var _expgolomb2 = _interoprequiredefault(_expgolomb); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var spsparser = function () { function spsparser() { _classcallcheck(this, spsparser); } _createclass(spsparser, null, [{ key: '_ebsp2rbsp', value: function _ebsp2rbsp(uint8array) { var src = uint8array; var src_length = src.bytelength; var dst = new uint8array(src_length); var dst_idx = 0; for (var i = 0; i < src_length; i++) { if (i >= 2) { // unescape: skip 0x03 after 00 00 if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) { continue; } } dst[dst_idx] = src[i]; dst_idx++; } return new uint8array(dst.buffer, 0, dst_idx); } }, { key: 'parsesps', value: function parsesps(uint8array) { var rbsp = spsparser._ebsp2rbsp(uint8array); var gb = new _expgolomb2.default(rbsp); gb.readbyte(); var profile_idc = gb.readbyte(); // profile_idc gb.readbyte(); // constraint_set_flags[5] + reserved_zero[3] var level_idc = gb.readbyte(); // level_idc gb.readueg(); // seq_parameter_set_id var profile_string = spsparser.getprofilestring(profile_idc); var level_string = spsparser.getlevelstring(level_idc); var chroma_format_idc = 1; var chroma_format = 420; var chroma_format_table = [0, 420, 422, 444]; var bit_depth = 8; 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) { chroma_format_idc = gb.readueg(); if (chroma_format_idc === 3) { gb.readbits(1); // separate_colour_plane_flag } if (chroma_format_idc <= 3) { chroma_format = chroma_format_table[chroma_format_idc]; } bit_depth = gb.readueg() + 8; // bit_depth_luma_minus8 gb.readueg(); // bit_depth_chroma_minus8 gb.readbits(1); // qpprime_y_zero_transform_bypass_flag if (gb.readbool()) { // seq_scaling_matrix_present_flag var scaling_list_count = chroma_format_idc !== 3 ? 8 : 12; for (var i = 0; i < scaling_list_count; i++) { if (gb.readbool()) { // seq_scaling_list_present_flag if (i < 6) { spsparser._skipscalinglist(gb, 16); } else { spsparser._skipscalinglist(gb, 64); } } } } } gb.readueg(); // log2_max_frame_num_minus4 var pic_order_cnt_type = gb.readueg(); if (pic_order_cnt_type === 0) { gb.readueg(); // log2_max_pic_order_cnt_lsb_minus_4 } else if (pic_order_cnt_type === 1) { gb.readbits(1); // delta_pic_order_always_zero_flag gb.readseg(); // offset_for_non_ref_pic gb.readseg(); // offset_for_top_to_bottom_field var num_ref_frames_in_pic_order_cnt_cycle = gb.readueg(); for (var _i = 0; _i < num_ref_frames_in_pic_order_cnt_cycle; _i++) { gb.readseg(); // offset_for_ref_frame } } var ref_frames = gb.readueg(); // max_num_ref_frames gb.readbits(1); // gaps_in_frame_num_value_allowed_flag var pic_width_in_mbs_minus1 = gb.readueg(); var pic_height_in_map_units_minus1 = gb.readueg(); var frame_mbs_only_flag = gb.readbits(1); if (frame_mbs_only_flag === 0) { gb.readbits(1); // mb_adaptive_frame_field_flag } gb.readbits(1); // direct_8x8_inference_flag var frame_crop_left_offset = 0; var frame_crop_right_offset = 0; var frame_crop_top_offset = 0; var frame_crop_bottom_offset = 0; var frame_cropping_flag = gb.readbool(); if (frame_cropping_flag) { frame_crop_left_offset = gb.readueg(); frame_crop_right_offset = gb.readueg(); frame_crop_top_offset = gb.readueg(); frame_crop_bottom_offset = gb.readueg(); } var sar_width = 1, sar_height = 1; var fps = 0, fps_fixed = true, fps_num = 0, fps_den = 0; var vui_parameters_present_flag = gb.readbool(); if (vui_parameters_present_flag) { if (gb.readbool()) { // aspect_ratio_info_present_flag var aspect_ratio_idc = gb.readbyte(); var sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2]; var sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1]; if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) { sar_width = sar_w_table[aspect_ratio_idc - 1]; sar_height = sar_h_table[aspect_ratio_idc - 1]; } else if (aspect_ratio_idc === 255) { sar_width = gb.readbyte() << 8 | gb.readbyte(); sar_height = gb.readbyte() << 8 | gb.readbyte(); } } if (gb.readbool()) { // overscan_info_present_flag gb.readbool(); // overscan_appropriate_flag } if (gb.readbool()) { // video_signal_type_present_flag gb.readbits(4); // video_format & video_full_range_flag if (gb.readbool()) { // colour_description_present_flag gb.readbits(24); // colour_primaries & transfer_characteristics & matrix_coefficients } } if (gb.readbool()) { // chroma_loc_info_present_flag gb.readueg(); // chroma_sample_loc_type_top_field gb.readueg(); // chroma_sample_loc_type_bottom_field } if (gb.readbool()) { // timing_info_present_flag var num_units_in_tick = gb.readbits(32); var time_scale = gb.readbits(32); fps_fixed = gb.readbool(); // fixed_frame_rate_flag fps_num = time_scale; fps_den = num_units_in_tick * 2; fps = fps_num / fps_den; } } var sarscale = 1; if (sar_width !== 1 || sar_height !== 1) { sarscale = sar_width / sar_height; } var crop_unit_x = 0, crop_unit_y = 0; if (chroma_format_idc === 0) { crop_unit_x = 1; crop_unit_y = 2 - frame_mbs_only_flag; } else { var sub_wc = chroma_format_idc === 3 ? 1 : 2; var sub_hc = chroma_format_idc === 1 ? 2 : 1; crop_unit_x = sub_wc; crop_unit_y = sub_hc * (2 - frame_mbs_only_flag); } var codec_width = (pic_width_in_mbs_minus1 + 1) * 16; var codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16); codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x; codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y; var present_width = math.ceil(codec_width * sarscale); gb.destroy(); gb = null; return { profile_string: profile_string, // baseline, high, high10, ... level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ... bit_depth: bit_depth, // 8bit, 10bit, ... ref_frames: ref_frames, chroma_format: chroma_format, // 4:2:0, 4:2:2, ... chroma_format_string: spsparser.getchromaformatstring(chroma_format), frame_rate: { fixed: fps_fixed, fps: fps, fps_den: fps_den, fps_num: fps_num }, sar_ratio: { width: sar_width, height: sar_height }, codec_size: { width: codec_width, height: codec_height }, present_size: { width: present_width, height: codec_height } }; } }, { key: '_skipscalinglist', value: function _skipscalinglist(gb, count) { var last_scale = 8, next_scale = 8; var delta_scale = 0; for (var i = 0; i < count; i++) { if (next_scale !== 0) { delta_scale = gb.readseg(); next_scale = (last_scale + delta_scale + 256) % 256; } last_scale = next_scale === 0 ? last_scale : next_scale; } } }, { key: 'getprofilestring', value: function getprofilestring(profile_idc) { switch (profile_idc) { case 66: return 'baseline'; case 77: return 'main'; case 88: return 'extended'; case 100: return 'high'; case 110: return 'high10'; case 122: return 'high422'; case 244: return 'high444'; default: return 'unknown'; } } }, { key: 'getlevelstring', value: function getlevelstring(level_idc) { return (level_idc / 10).tofixed(1); } }, { key: 'getchromaformatstring', value: function getchromaformatstring(chroma) { switch (chroma) { case 420: return '4:2:0'; case 422: return '4:2:2'; case 444: return '4:4:4'; default: return 'unknown'; } } }]); return spsparser; }(); exports.default = spsparser; },{"./exp-golomb.js":17}],20:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _polyfill = _dereq_('./utils/polyfill.js'); var _polyfill2 = _interoprequiredefault(_polyfill); var _features = _dereq_('./core/features.js'); var _features2 = _interoprequiredefault(_features); var _flvplayer = _dereq_('./player/flv-player.js'); var _flvplayer2 = _interoprequiredefault(_flvplayer); var _nativeplayer = _dereq_('./player/native-player.js'); var _nativeplayer2 = _interoprequiredefault(_nativeplayer); var _playerevents = _dereq_('./player/player-events.js'); var _playerevents2 = _interoprequiredefault(_playerevents); var _playererrors = _dereq_('./player/player-errors.js'); var _loggingcontrol = _dereq_('./utils/logging-control.js'); var _loggingcontrol2 = _interoprequiredefault(_loggingcontrol); var _exception = _dereq_('./utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } // here are all the interfaces // install polyfills _polyfill2.default.install(); // factory method function createplayer(mediadatasource, optionalconfig) { var mds = mediadatasource; if (mds == null || (typeof mds === 'undefined' ? 'undefined' : _typeof(mds)) !== 'object') { throw new _exception.invalidargumentexception('mediadatasource must be an javascript object!'); } if (!mds.hasownproperty('type')) { throw new _exception.invalidargumentexception('mediadatasource must has type field to indicate video file type!'); } switch (mds.type) { case 'flv': return new _flvplayer2.default(mds, optionalconfig); default: return new _nativeplayer2.default(mds, optionalconfig); } } // feature detection function issupported() { return _features2.default.supportmseh264playback(); } function getfeaturelist() { return _features2.default.getfeaturelist(); } // interfaces var flvjs = {}; flvjs.createplayer = createplayer; flvjs.issupported = issupported; flvjs.getfeaturelist = getfeaturelist; flvjs.events = _playerevents2.default; flvjs.errortypes = _playererrors.errortypes; flvjs.errordetails = _playererrors.errordetails; flvjs.flvplayer = _flvplayer2.default; flvjs.nativeplayer = _nativeplayer2.default; flvjs.loggingcontrol = _loggingcontrol2.default; object.defineproperty(flvjs, 'version', { enumerable: true, get: function get() { // replaced by browserify-versionify transform return '1.4.0'; } }); exports.default = flvjs; },{"./core/features.js":6,"./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){ 'use strict'; // entry/index file // make it compatible with browserify's umd wrapper module.exports = _dereq_('./flv.js').default; },{"./flv.js":20}],22:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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); } }; 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; }; }(); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _browser = _dereq_('../utils/browser.js'); var _browser2 = _interoprequiredefault(_browser); var _loader = _dereq_('./loader.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } 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; } 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; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ /* fetch + stream io loader. currently working on chrome 43+. * fetch provides a better alternative http api to xmlhttprequest * * fetch spec https://fetch.spec.whatwg.org/ * stream spec https://streams.spec.whatwg.org/ */ var fetchstreamloader = function (_baseloader) { _inherits(fetchstreamloader, _baseloader); _createclass(fetchstreamloader, null, [{ key: 'issupported', value: function issupported() { try { // fetch + stream is broken on microsoft edge. disable before build 15048. // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/ // fixed in jan 10, 2017. build 15048+ removed from blacklist. var isworkwelledge = _browser2.default.msedge && _browser2.default.version.minor >= 15048; var browsernotblacklisted = _browser2.default.msedge ? isworkwelledge : true; return self.fetch && self.readablestream && browsernotblacklisted; } catch (e) { return false; } } }]); function fetchstreamloader(seekhandler, config) { _classcallcheck(this, fetchstreamloader); var _this = _possibleconstructorreturn(this, (fetchstreamloader.__proto__ || object.getprototypeof(fetchstreamloader)).call(this, 'fetch-stream-loader')); _this.tag = 'fetchstreamloader'; _this._seekhandler = seekhandler; _this._config = config; _this._needstash = true; _this._requestabort = false; _this._contentlength = null; _this._receivedlength = 0; return _this; } _createclass(fetchstreamloader, [{ key: 'destroy', value: function destroy() { if (this.isworking()) { this.abort(); } _get(fetchstreamloader.prototype.__proto__ || object.getprototypeof(fetchstreamloader.prototype), 'destroy', this).call(this); } }, { key: 'open', value: function open(datasource, range) { var _this2 = this; this._datasource = datasource; this._range = range; var sourceurl = datasource.url; if (this._config.reuseredirectedurl && datasource.redirectedurl != undefined) { sourceurl = datasource.redirectedurl; } var seekconfig = this._seekhandler.getconfig(sourceurl, range); var headers = new self.headers(); if (_typeof(seekconfig.headers) === 'object') { var configheaders = seekconfig.headers; for (var key in configheaders) { if (configheaders.hasownproperty(key)) { headers.append(key, configheaders[key]); } } } var params = { method: 'get', headers: headers, mode: 'cors', cache: 'default', // the default policy of fetch api in the whatwg standard // safari incorrectly indicates 'no-referrer' as default policy, fuck it referrerpolicy: 'no-referrer-when-downgrade' }; // cors is enabled by default if (datasource.cors === false) { // no-cors means 'disregard cors policy', which can only be used in serviceworker params.mode = 'same-origin'; } // withcredentials is disabled by default if (datasource.withcredentials) { params.credentials = 'include'; } // referrerpolicy from config if (datasource.referrerpolicy) { params.referrerpolicy = datasource.referrerpolicy; } this._status = _loader.loaderstatus.kconnecting; self.fetch(seekconfig.url, params).then(function (res) { if (_this2._requestabort) { _this2._requestabort = false; _this2._status = _loader.loaderstatus.kidle; return; } if (res.ok && res.status >= 200 && res.status <= 299) { if (res.url !== seekconfig.url) { if (_this2._onurlredirect) { var redirectedurl = _this2._seekhandler.removeurlparameters(res.url); _this2._onurlredirect(redirectedurl); } } var lengthheader = res.headers.get('content-length'); if (lengthheader != null) { _this2._contentlength = parseint(lengthheader); if (_this2._contentlength !== 0) { if (_this2._oncontentlengthknown) { _this2._oncontentlengthknown(_this2._contentlength); } } } return _this2._pump.call(_this2, res.body.getreader()); } else { _this2._status = _loader.loaderstatus.kerror; if (_this2._onerror) { _this2._onerror(_loader.loadererrors.http_status_code_invalid, { code: res.status, msg: res.statustext }); } else { throw new _exception.runtimeexception('fetchstreamloader: http code invalid, ' + res.status + ' ' + res.statustext); } } }).catch(function (e) { _this2._status = _loader.loaderstatus.kerror; if (_this2._onerror) { _this2._onerror(_loader.loadererrors.exception, { code: -1, msg: e.message }); } else { throw e; } }); } }, { key: 'abort', value: function abort() { this._requestabort = true; } }, { key: '_pump', value: function _pump(reader) { var _this3 = this; // readablestreamreader return reader.read().then(function (result) { if (result.done) { _this3._status = _loader.loaderstatus.kcomplete; if (_this3._oncomplete) { _this3._oncomplete(_this3._range.from, _this3._range.from + _this3._receivedlength - 1); } } else { if (_this3._requestabort === true) { _this3._requestabort = false; _this3._status = _loader.loaderstatus.kcomplete; return reader.cancel(); } _this3._status = _loader.loaderstatus.kbuffering; var chunk = result.value.buffer; var bytestart = _this3._range.from + _this3._receivedlength; _this3._receivedlength += chunk.bytelength; if (_this3._ondataarrival) { _this3._ondataarrival(chunk, bytestart, _this3._receivedlength); } _this3._pump(reader); } }).catch(function (e) { if (e.code === 11 && _browser2.default.msedge) { // invalidstateerror on microsoft edge // workaround: edge may throw invalidstateerror after readablestreamreader.cancel() call // ignore the unknown exception. // related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/ return; } _this3._status = _loader.loaderstatus.kerror; var type = 0; var info = null; if ((e.code === 19 || e.message === 'network error') && ( // network_err _this3._contentlength === null || _this3._contentlength !== null && _this3._receivedlength < _this3._contentlength)) { type = _loader.loadererrors.early_eof; info = { code: e.code, msg: 'fetch stream meet early-eof' }; } else { type = _loader.loadererrors.exception; info = { code: e.code, msg: e.message }; } if (_this3._onerror) { _this3._onerror(type, info); } else { throw new _exception.runtimeexception(info.msg); } }); } }]); return fetchstreamloader; }(_loader.baseloader); exports.default = fetchstreamloader; },{"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],23:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _speedsampler = _dereq_('./speed-sampler.js'); var _speedsampler2 = _interoprequiredefault(_speedsampler); var _loader = _dereq_('./loader.js'); var _fetchstreamloader = _dereq_('./fetch-stream-loader.js'); var _fetchstreamloader2 = _interoprequiredefault(_fetchstreamloader); var _xhrmozchunkedloader = _dereq_('./xhr-moz-chunked-loader.js'); var _xhrmozchunkedloader2 = _interoprequiredefault(_xhrmozchunkedloader); var _xhrmsstreamloader = _dereq_('./xhr-msstream-loader.js'); var _xhrmsstreamloader2 = _interoprequiredefault(_xhrmsstreamloader); var _xhrrangeloader = _dereq_('./xhr-range-loader.js'); var _xhrrangeloader2 = _interoprequiredefault(_xhrrangeloader); var _websocketloader = _dereq_('./websocket-loader.js'); var _websocketloader2 = _interoprequiredefault(_websocketloader); var _rangeseekhandler = _dereq_('./range-seek-handler.js'); var _rangeseekhandler2 = _interoprequiredefault(_rangeseekhandler); var _paramseekhandler = _dereq_('./param-seek-handler.js'); var _paramseekhandler2 = _interoprequiredefault(_paramseekhandler); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /** * datasource: { * url: string, * filesize: number, * cors: boolean, * withcredentials: boolean * } * */ // manage io loaders var iocontroller = function () { function iocontroller(datasource, config, extradata) { _classcallcheck(this, iocontroller); this.tag = 'iocontroller'; this._config = config; this._extradata = extradata; this._stashinitialsize = 1024 * 384; // default initial size: 384kb if (config.stashinitialsize != undefined && config.stashinitialsize > 0) { // apply from config this._stashinitialsize = config.stashinitialsize; } this._stashused = 0; this._stashsize = this._stashinitialsize; this._buffersize = 1024 * 1024 * 3; // initial size: 3mb this._stashbuffer = new arraybuffer(this._buffersize); this._stashbytestart = 0; this._enablestash = true; if (config.enablestashbuffer === false) { this._enablestash = false; } this._loader = null; this._loaderclass = null; this._seekhandler = null; this._datasource = datasource; this._iswebsocketurl = /wss?:\/\/(.+?)/.test(datasource.url); this._reftotallength = datasource.filesize ? datasource.filesize : null; this._totallength = this._reftotallength; this._fullrequestflag = false; this._currentrange = null; this._redirectedurl = null; this._speednormalized = 0; this._speedsampler = new _speedsampler2.default(); this._speednormalizelist = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096]; this._isearlyeofreconnecting = false; this._paused = false; this._resumefrom = 0; this._ondataarrival = null; this._onseeked = null; this._onerror = null; this._oncomplete = null; this._onredirect = null; this._onrecoveredearlyeof = null; this._selectseekhandler(); this._selectloader(); this._createloader(); } _createclass(iocontroller, [{ key: 'destroy', value: function destroy() { if (this._loader.isworking()) { this._loader.abort(); } this._loader.destroy(); this._loader = null; this._loaderclass = null; this._datasource = null; this._stashbuffer = null; this._stashused = this._stashsize = this._buffersize = this._stashbytestart = 0; this._currentrange = null; this._speedsampler = null; this._isearlyeofreconnecting = false; this._ondataarrival = null; this._onseeked = null; this._onerror = null; this._oncomplete = null; this._onredirect = null; this._onrecoveredearlyeof = null; this._extradata = null; } }, { key: 'isworking', value: function isworking() { return this._loader && this._loader.isworking() && !this._paused; } }, { key: 'ispaused', value: function ispaused() { return this._paused; } }, { key: '_selectseekhandler', value: function _selectseekhandler() { var config = this._config; if (config.seektype === 'range') { this._seekhandler = new _rangeseekhandler2.default(this._config.rangeloadzerostart); } else if (config.seektype === 'param') { var paramstart = config.seekparamstart || 'bstart'; var paramend = config.seekparamend || 'bend'; this._seekhandler = new _paramseekhandler2.default(paramstart, paramend); } else if (config.seektype === 'custom') { if (typeof config.customseekhandler !== 'function') { throw new _exception.invalidargumentexception('custom seektype specified in config but invalid customseekhandler!'); } this._seekhandler = new config.customseekhandler(); } else { throw new _exception.invalidargumentexception('invalid seektype in config: ' + config.seektype); } } }, { key: '_selectloader', value: function _selectloader() { if (this._iswebsocketurl) { this._loaderclass = _websocketloader2.default; } else if (_fetchstreamloader2.default.issupported()) { this._loaderclass = _fetchstreamloader2.default; } else if (_xhrmozchunkedloader2.default.issupported()) { this._loaderclass = _xhrmozchunkedloader2.default; } else if (_xhrrangeloader2.default.issupported()) { this._loaderclass = _xhrrangeloader2.default; } else { throw new _exception.runtimeexception('your browser doesn\'t support xhr with arraybuffer responsetype!'); } } }, { key: '_createloader', value: function _createloader() { this._loader = new this._loaderclass(this._seekhandler, this._config); if (this._loader.needstashbuffer === false) { this._enablestash = false; } this._loader.oncontentlengthknown = this._oncontentlengthknown.bind(this); this._loader.onurlredirect = this._onurlredirect.bind(this); this._loader.ondataarrival = this._onloaderchunkarrival.bind(this); this._loader.oncomplete = this._onloadercomplete.bind(this); this._loader.onerror = this._onloadererror.bind(this); } }, { key: 'open', value: function open(optionalfrom) { this._currentrange = { from: 0, to: -1 }; if (optionalfrom) { this._currentrange.from = optionalfrom; } this._speedsampler.reset(); if (!optionalfrom) { this._fullrequestflag = true; } this._loader.open(this._datasource, object.assign({}, this._currentrange)); } }, { key: 'abort', value: function abort() { this._loader.abort(); if (this._paused) { this._paused = false; this._resumefrom = 0; } } }, { key: 'pause', value: function pause() { if (this.isworking()) { this._loader.abort(); if (this._stashused !== 0) { this._resumefrom = this._stashbytestart; this._currentrange.to = this._stashbytestart - 1; } else { this._resumefrom = this._currentrange.to + 1; } this._stashused = 0; this._stashbytestart = 0; this._paused = true; } } }, { key: 'resume', value: function resume() { if (this._paused) { this._paused = false; var bytes = this._resumefrom; this._resumefrom = 0; this._internalseek(bytes, true); } } }, { key: 'seek', value: function seek(bytes) { this._paused = false; this._stashused = 0; this._stashbytestart = 0; this._internalseek(bytes, true); } /** * when seeking request is from media seeking, unconsumed stash data should be dropped * however, stash data shouldn't be dropped if seeking requested from http reconnection * * @dropunconsumed: ignore and discard all unconsumed data in stash buffer */ }, { key: '_internalseek', value: function _internalseek(bytes, dropunconsumed) { if (this._loader.isworking()) { this._loader.abort(); } // dispatch & flush stash buffer before seek this._flushstashbuffer(dropunconsumed); this._loader.destroy(); this._loader = null; var requestrange = { from: bytes, to: -1 }; this._currentrange = { from: requestrange.from, to: -1 }; this._speedsampler.reset(); this._stashsize = this._stashinitialsize; this._createloader(); this._loader.open(this._datasource, requestrange); if (this._onseeked) { this._onseeked(); } } }, { key: 'updateurl', value: function updateurl(url) { if (!url || typeof url !== 'string' || url.length === 0) { throw new _exception.invalidargumentexception('url must be a non-empty string!'); } this._datasource.url = url; // todo: replace with new url } }, { key: '_expandbuffer', value: function _expandbuffer(expectedbytes) { var buffernewsize = this._stashsize; while (buffernewsize + 1024 * 1024 * 1 < expectedbytes) { buffernewsize *= 2; } buffernewsize += 1024 * 1024 * 1; // buffersize = stashsize + 1mb if (buffernewsize === this._buffersize) { return; } var newbuffer = new arraybuffer(buffernewsize); if (this._stashused > 0) { // copy existing data into new buffer var stasholdarray = new uint8array(this._stashbuffer, 0, this._stashused); var stashnewarray = new uint8array(newbuffer, 0, buffernewsize); stashnewarray.set(stasholdarray, 0); } this._stashbuffer = newbuffer; this._buffersize = buffernewsize; } }, { key: '_normalizespeed', value: function _normalizespeed(input) { var list = this._speednormalizelist; var last = list.length - 1; var mid = 0; var lbound = 0; var ubound = last; if (input < list[0]) { return list[0]; } // binary search while (lbound <= ubound) { mid = lbound + math.floor((ubound - lbound) / 2); if (mid === last || input >= list[mid] && input < list[mid + 1]) { return list[mid]; } else if (list[mid] < input) { lbound = mid + 1; } else { ubound = mid - 1; } } } }, { key: '_adjuststashsize', value: function _adjuststashsize(normalized) { var stashsizekb = 0; if (this._config.islive) { // live stream: always use single normalized speed for size of stashsizekb stashsizekb = normalized; } else { if (normalized < 512) { stashsizekb = normalized; } else if (normalized >= 512 && normalized <= 1024) { stashsizekb = math.floor(normalized * 1.5); } else { stashsizekb = normalized * 2; } } if (stashsizekb > 8192) { stashsizekb = 8192; } var buffersize = stashsizekb * 1024 + 1024 * 1024 * 1; // stashsize + 1mb if (this._buffersize < buffersize) { this._expandbuffer(buffersize); } this._stashsize = stashsizekb * 1024; } }, { key: '_dispatchchunks', value: function _dispatchchunks(chunks, bytestart) { this._currentrange.to = bytestart + chunks.bytelength - 1; return this._ondataarrival(chunks, bytestart); } }, { key: '_onurlredirect', value: function _onurlredirect(redirectedurl) { this._redirectedurl = redirectedurl; if (this._onredirect) { this._onredirect(redirectedurl); } } }, { key: '_oncontentlengthknown', value: function _oncontentlengthknown(contentlength) { if (contentlength && this._fullrequestflag) { this._totallength = contentlength; this._fullrequestflag = false; } } }, { key: '_onloaderchunkarrival', value: function _onloaderchunkarrival(chunk, bytestart, receivedlength) { if (!this._ondataarrival) { throw new _exception.illegalstateexception('iocontroller: no existing consumer (ondataarrival) callback!'); } if (this._paused) { return; } if (this._isearlyeofreconnecting) { // auto-reconnect for earlyeof succeed, notify to upper-layer by callback this._isearlyeofreconnecting = false; if (this._onrecoveredearlyeof) { this._onrecoveredearlyeof(); } } this._speedsampler.addbytes(chunk.bytelength); // adjust stash buffer size according to network speed dynamically var kbps = this._speedsampler.lastsecondkbps; if (kbps !== 0) { var normalized = this._normalizespeed(kbps); if (this._speednormalized !== normalized) { this._speednormalized = normalized; this._adjuststashsize(normalized); } } if (!this._enablestash) { // disable stash if (this._stashused === 0) { // dispatch chunk directly to consumer; // check ret value (consumed bytes) and stash unconsumed to stashbuffer var consumed = this._dispatchchunks(chunk, bytestart); if (consumed < chunk.bytelength) { // unconsumed data remain. var remain = chunk.bytelength - consumed; if (remain > this._buffersize) { this._expandbuffer(remain); } var stasharray = new uint8array(this._stashbuffer, 0, this._buffersize); stasharray.set(new uint8array(chunk, consumed), 0); this._stashused += remain; this._stashbytestart = bytestart + consumed; } } else { // else: merge chunk into stashbuffer, and dispatch stashbuffer to consumer. if (this._stashused + chunk.bytelength > this._buffersize) { this._expandbuffer(this._stashused + chunk.bytelength); } var _stasharray = new uint8array(this._stashbuffer, 0, this._buffersize); _stasharray.set(new uint8array(chunk), this._stashused); this._stashused += chunk.bytelength; var _consumed = this._dispatchchunks(this._stashbuffer.slice(0, this._stashused), this._stashbytestart); if (_consumed < this._stashused && _consumed > 0) { // unconsumed data remain var remainarray = new uint8array(this._stashbuffer, _consumed); _stasharray.set(remainarray, 0); } this._stashused -= _consumed; this._stashbytestart += _consumed; } } else { // enable stash if (this._stashused === 0 && this._stashbytestart === 0) { // seeked? or init chunk? // this is the first chunk after seek action this._stashbytestart = bytestart; } if (this._stashused + chunk.bytelength <= this._stashsize) { // just stash var _stasharray2 = new uint8array(this._stashbuffer, 0, this._stashsize); _stasharray2.set(new uint8array(chunk), this._stashused); this._stashused += chunk.bytelength; } else { // stashused + chunksize > stashsize, size limit exceeded var _stasharray3 = new uint8array(this._stashbuffer, 0, this._buffersize); if (this._stashused > 0) { // there're stash datas in buffer // dispatch the whole stashbuffer, and stash remain data // then append chunk to stashbuffer (stash) var buffer = this._stashbuffer.slice(0, this._stashused); var _consumed2 = this._dispatchchunks(buffer, this._stashbytestart); if (_consumed2 < buffer.bytelength) { if (_consumed2 > 0) { var _remainarray = new uint8array(buffer, _consumed2); _stasharray3.set(_remainarray, 0); this._stashused = _remainarray.bytelength; this._stashbytestart += _consumed2; } } else { this._stashused = 0; this._stashbytestart += _consumed2; } if (this._stashused + chunk.bytelength > this._buffersize) { this._expandbuffer(this._stashused + chunk.bytelength); _stasharray3 = new uint8array(this._stashbuffer, 0, this._buffersize); } _stasharray3.set(new uint8array(chunk), this._stashused); this._stashused += chunk.bytelength; } else { // stash buffer empty, but chunksize > stashsize (oh, holy shit) // dispatch chunk directly and stash remain data var _consumed3 = this._dispatchchunks(chunk, bytestart); if (_consumed3 < chunk.bytelength) { var _remain = chunk.bytelength - _consumed3; if (_remain > this._buffersize) { this._expandbuffer(_remain); _stasharray3 = new uint8array(this._stashbuffer, 0, this._buffersize); } _stasharray3.set(new uint8array(chunk, _consumed3), 0); this._stashused += _remain; this._stashbytestart = bytestart + _consumed3; } } } } } }, { key: '_flushstashbuffer', value: function _flushstashbuffer(dropunconsumed) { if (this._stashused > 0) { var buffer = this._stashbuffer.slice(0, this._stashused); var consumed = this._dispatchchunks(buffer, this._stashbytestart); var remain = buffer.bytelength - consumed; if (consumed < buffer.bytelength) { if (dropunconsumed) { _logger2.default.w(this.tag, remain + ' bytes unconsumed data remain when flush buffer, dropped'); } else { if (consumed > 0) { var stasharray = new uint8array(this._stashbuffer, 0, this._buffersize); var remainarray = new uint8array(buffer, consumed); stasharray.set(remainarray, 0); this._stashused = remainarray.bytelength; this._stashbytestart += consumed; } return 0; } } this._stashused = 0; this._stashbytestart = 0; return remain; } return 0; } }, { key: '_onloadercomplete', value: function _onloadercomplete(from, to) { // force-flush stash buffer, and drop unconsumed data this._flushstashbuffer(true); if (this._oncomplete) { this._oncomplete(this._extradata); } } }, { key: '_onloadererror', value: function _onloadererror(type, data) { _logger2.default.e(this.tag, 'loader error, code = ' + data.code + ', msg = ' + data.msg); this._flushstashbuffer(false); if (this._isearlyeofreconnecting) { // auto-reconnect for earlyeof failed, throw unrecoverableearlyeof error to upper-layer this._isearlyeofreconnecting = false; type = _loader.loadererrors.unrecoverable_early_eof; } switch (type) { case _loader.loadererrors.early_eof: { if (!this._config.islive) { // do internal http reconnect if not live stream if (this._totallength) { var nextfrom = this._currentrange.to + 1; if (nextfrom < this._totallength) { _logger2.default.w(this.tag, 'connection lost, trying reconnect...'); this._isearlyeofreconnecting = true; this._internalseek(nextfrom, false); } return; } // else: we don't know totallength, throw unrecoverableearlyeof } // live stream: throw unrecoverableearlyeof error to upper-layer type = _loader.loadererrors.unrecoverable_early_eof; break; } case _loader.loadererrors.unrecoverable_early_eof: case _loader.loadererrors.connecting_timeout: case _loader.loadererrors.http_status_code_invalid: case _loader.loadererrors.exception: break; } if (this._onerror) { this._onerror(type, data); } else { throw new _exception.runtimeexception('ioexception: ' + data.msg); } } }, { key: 'status', get: function get() { return this._loader.status; } }, { key: 'extradata', get: function get() { return this._extradata; }, set: function set(data) { this._extradata = data; } // prototype: function ondataarrival(chunks: arraybuffer, bytestart: number): number }, { key: 'ondataarrival', get: function get() { return this._ondataarrival; }, set: function set(callback) { this._ondataarrival = callback; } }, { key: 'onseeked', get: function get() { return this._onseeked; }, set: function set(callback) { this._onseeked = callback; } // prototype: function onerror(type: number, info: {code: number, msg: string}): void }, { key: 'onerror', get: function get() { return this._onerror; }, set: function set(callback) { this._onerror = callback; } }, { key: 'oncomplete', get: function get() { return this._oncomplete; }, set: function set(callback) { this._oncomplete = callback; } }, { key: 'onredirect', get: function get() { return this._onredirect; }, set: function set(callback) { this._onredirect = callback; } }, { key: 'onrecoveredearlyeof', get: function get() { return this._onrecoveredearlyeof; }, set: function set(callback) { this._onrecoveredearlyeof = callback; } }, { key: 'currenturl', get: function get() { return this._datasource.url; } }, { key: 'hasredirect', get: function get() { return this._redirectedurl != null || this._datasource.redirectedurl != undefined; } }, { key: 'currentredirectedurl', get: function get() { return this._redirectedurl || this._datasource.redirectedurl; } // in kb/s }, { key: 'currentspeed', get: function get() { if (this._loaderclass === _xhrrangeloader2.default) { // speedsampler is inaccuracy if loader is rangeloader return this._loader.currentspeed; } return this._speedsampler.lastsecondkbps; } }, { key: 'loadertype', get: function get() { return this._loader.type; } }]); return iocontroller; }(); exports.default = iocontroller; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); exports.baseloader = exports.loadererrors = exports.loaderstatus = undefined; 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _exception = _dereq_('../utils/exception.js'); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var loaderstatus = exports.loaderstatus = { kidle: 0, kconnecting: 1, kbuffering: 2, kerror: 3, kcomplete: 4 }; var loadererrors = exports.loadererrors = { ok: 'ok', exception: 'exception', http_status_code_invalid: 'httpstatuscodeinvalid', connecting_timeout: 'connectingtimeout', early_eof: 'earlyeof', unrecoverable_early_eof: 'unrecoverableearlyeof' }; /* loader has callbacks which have following prototypes: * function oncontentlengthknown(contentlength: number): void * function onurlredirect(url: string): void * function ondataarrival(chunk: arraybuffer, bytestart: number, receivedlength: number): void * function onerror(errortype: number, errorinfo: {code: number, msg: string}): void * function oncomplete(rangefrom: number, rangeto: number): void */ var baseloader = exports.baseloader = function () { function baseloader(typename) { _classcallcheck(this, baseloader); this._type = typename || 'undefined'; this._status = loaderstatus.kidle; this._needstash = false; // callbacks this._oncontentlengthknown = null; this._onurlredirect = null; this._ondataarrival = null; this._onerror = null; this._oncomplete = null; } _createclass(baseloader, [{ key: 'destroy', value: function destroy() { this._status = loaderstatus.kidle; this._oncontentlengthknown = null; this._onurlredirect = null; this._ondataarrival = null; this._onerror = null; this._oncomplete = null; } }, { key: 'isworking', value: function isworking() { return this._status === loaderstatus.kconnecting || this._status === loaderstatus.kbuffering; } }, { key: 'open', // pure virtual value: function open(datasource, range) { throw new _exception.notimplementedexception('unimplemented abstract function!'); } }, { key: 'abort', value: function abort() { throw new _exception.notimplementedexception('unimplemented abstract function!'); } }, { key: 'type', get: function get() { return this._type; } }, { key: 'status', get: function get() { return this._status; } }, { key: 'needstashbuffer', get: function get() { return this._needstash; } }, { key: 'oncontentlengthknown', get: function get() { return this._oncontentlengthknown; }, set: function set(callback) { this._oncontentlengthknown = callback; } }, { key: 'onurlredirect', get: function get() { return this._onurlredirect; }, set: function set(callback) { this._onurlredirect = callback; } }, { key: 'ondataarrival', get: function get() { return this._ondataarrival; }, set: function set(callback) { this._ondataarrival = callback; } }, { key: 'onerror', get: function get() { return this._onerror; }, set: function set(callback) { this._onerror = callback; } }, { key: 'oncomplete', get: function get() { return this._oncomplete; }, set: function set(callback) { this._oncomplete = callback; } }]); return baseloader; }(); },{"../utils/exception.js":40}],25:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var paramseekhandler = function () { function paramseekhandler(paramstart, paramend) { _classcallcheck(this, paramseekhandler); this._startname = paramstart; this._endname = paramend; } _createclass(paramseekhandler, [{ key: 'getconfig', value: function getconfig(baseurl, range) { var url = baseurl; if (range.from !== 0 || range.to !== -1) { var needand = true; if (url.indexof('?') === -1) { url += '?'; needand = false; } if (needand) { url += '&'; } url += this._startname + '=' + range.from.tostring(); if (range.to !== -1) { url += '&' + this._endname + '=' + range.to.tostring(); } } return { url: url, headers: {} }; } }, { key: 'removeurlparameters', value: function removeurlparameters(seekedurl) { var baseurl = seekedurl.split('?')[0]; var params = undefined; var queryindex = seekedurl.indexof('?'); if (queryindex !== -1) { params = seekedurl.substring(queryindex + 1); } var resultparams = ''; if (params != undefined && params.length > 0) { var pairs = params.split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); var requireand = i > 0; if (pair[0] !== this._startname && pair[0] !== this._endname) { if (requireand) { resultparams += '&'; } resultparams += pairs[i]; } } } return resultparams.length === 0 ? baseurl : baseurl + '?' + resultparams; } }]); return paramseekhandler; }(); exports.default = paramseekhandler; },{}],26:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var rangeseekhandler = function () { function rangeseekhandler(zerostart) { _classcallcheck(this, rangeseekhandler); this._zerostart = zerostart || false; } _createclass(rangeseekhandler, [{ key: 'getconfig', value: function getconfig(url, range) { var headers = {}; if (range.from !== 0 || range.to !== -1) { var param = void 0; if (range.to !== -1) { param = 'bytes=' + range.from.tostring() + '-' + range.to.tostring(); } else { param = 'bytes=' + range.from.tostring() + '-'; } headers['range'] = param; } else if (this._zerostart) { headers['range'] = 'bytes=0-'; } return { url: url, headers: headers }; } }, { key: 'removeurlparameters', value: function removeurlparameters(seekedurl) { return seekedurl; } }]); return rangeseekhandler; }(); exports.default = rangeseekhandler; },{}],27:[function(_dereq_,module,exports){ "use strict"; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // utility class to calculate realtime network i/o speed var speedsampler = function () { function speedsampler() { _classcallcheck(this, speedsampler); // milliseconds this._firstcheckpoint = 0; this._lastcheckpoint = 0; this._intervalbytes = 0; this._totalbytes = 0; this._lastsecondbytes = 0; // compatibility detection if (self.performance && self.performance.now) { this._now = self.performance.now.bind(self.performance); } else { this._now = date.now; } } _createclass(speedsampler, [{ key: "reset", value: function reset() { this._firstcheckpoint = this._lastcheckpoint = 0; this._totalbytes = this._intervalbytes = 0; this._lastsecondbytes = 0; } }, { key: "addbytes", value: function addbytes(bytes) { if (this._firstcheckpoint === 0) { this._firstcheckpoint = this._now(); this._lastcheckpoint = this._firstcheckpoint; this._intervalbytes += bytes; this._totalbytes += bytes; } else if (this._now() - this._lastcheckpoint < 1000) { this._intervalbytes += bytes; this._totalbytes += bytes; } else { // duration >= 1000 this._lastsecondbytes = this._intervalbytes; this._intervalbytes = bytes; this._totalbytes += bytes; this._lastcheckpoint = this._now(); } } }, { key: "currentkbps", get: function get() { this.addbytes(0); var durationseconds = (this._now() - this._lastcheckpoint) / 1000; if (durationseconds == 0) durationseconds = 1; return this._intervalbytes / durationseconds / 1024; } }, { key: "lastsecondkbps", get: function get() { this.addbytes(0); if (this._lastsecondbytes !== 0) { return this._lastsecondbytes / 1024; } else { // lastsecondbytes === 0 if (this._now() - this._lastcheckpoint >= 500) { // if time interval since last checkpoint has exceeded 500ms // the speed is nearly accurate return this.currentkbps; } else { // we don't know return 0; } } } }, { key: "averagekbps", get: function get() { var durationseconds = (this._now() - this._firstcheckpoint) / 1000; return this._totalbytes / durationseconds / 1024; } }]); return speedsampler; }(); exports.default = speedsampler; },{}],28:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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); } }; 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; }; }(); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _loader = _dereq_('./loader.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } 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; } 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; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // for flv over websocket live stream var websocketloader = function (_baseloader) { _inherits(websocketloader, _baseloader); _createclass(websocketloader, null, [{ key: 'issupported', value: function issupported() { try { return typeof self.websocket !== 'undefined'; } catch (e) { return false; } } }]); function websocketloader() { _classcallcheck(this, websocketloader); var _this = _possibleconstructorreturn(this, (websocketloader.__proto__ || object.getprototypeof(websocketloader)).call(this, 'websocket-loader')); _this.tag = 'websocketloader'; _this._needstash = true; _this._ws = null; _this._requestabort = false; _this._receivedlength = 0; return _this; } _createclass(websocketloader, [{ key: 'destroy', value: function destroy() { if (this._ws) { this.abort(); } _get(websocketloader.prototype.__proto__ || object.getprototypeof(websocketloader.prototype), 'destroy', this).call(this); } }, { key: 'open', value: function open(datasource) { try { var ws = this._ws = new self.websocket(datasource.url); ws.binarytype = 'arraybuffer'; ws.onopen = this._onwebsocketopen.bind(this); ws.onclose = this._onwebsocketclose.bind(this); ws.onmessage = this._onwebsocketmessage.bind(this); ws.onerror = this._onwebsocketerror.bind(this); this._status = _loader.loaderstatus.kconnecting; } catch (e) { this._status = _loader.loaderstatus.kerror; var info = { code: e.code, msg: e.message }; if (this._onerror) { this._onerror(_loader.loadererrors.exception, info); } else { throw new _exception.runtimeexception(info.msg); } } } }, { key: 'abort', value: function abort() { var ws = this._ws; if (ws && (ws.readystate === 0 || ws.readystate === 1)) { // connecting || open this._requestabort = true; ws.close(); } this._ws = null; this._status = _loader.loaderstatus.kcomplete; } }, { key: '_onwebsocketopen', value: function _onwebsocketopen(e) { this._status = _loader.loaderstatus.kbuffering; } }, { key: '_onwebsocketclose', value: function _onwebsocketclose(e) { if (this._requestabort === true) { this._requestabort = false; return; } this._status = _loader.loaderstatus.kcomplete; if (this._oncomplete) { this._oncomplete(0, this._receivedlength - 1); } } }, { key: '_onwebsocketmessage', value: function _onwebsocketmessage(e) { var _this2 = this; if (e.data instanceof arraybuffer) { this._dispatcharraybuffer(e.data); } else if (e.data instanceof blob) { var reader = new filereader(); reader.onload = function () { _this2._dispatcharraybuffer(reader.result); }; reader.readasarraybuffer(e.data); } else { this._status = _loader.loaderstatus.kerror; var info = { code: -1, msg: 'unsupported websocket message type: ' + e.data.constructor.name }; if (this._onerror) { this._onerror(_loader.loadererrors.exception, info); } else { throw new _exception.runtimeexception(info.msg); } } } }, { key: '_dispatcharraybuffer', value: function _dispatcharraybuffer(arraybuffer) { var chunk = arraybuffer; var bytestart = this._receivedlength; this._receivedlength += chunk.bytelength; if (this._ondataarrival) { this._ondataarrival(chunk, bytestart, this._receivedlength); } } }, { key: '_onwebsocketerror', value: function _onwebsocketerror(e) { this._status = _loader.loaderstatus.kerror; var info = { code: e.code, msg: e.message }; if (this._onerror) { this._onerror(_loader.loadererrors.exception, info); } else { throw new _exception.runtimeexception(info.msg); } } }]); return websocketloader; }(_loader.baseloader); exports.default = websocketloader; },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],29:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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); } }; 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; }; }(); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _loader = _dereq_('./loader.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } 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; } 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; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // for firefox browser which supports `xhr.responsetype = 'moz-chunked-arraybuffer'` var mozchunkedloader = function (_baseloader) { _inherits(mozchunkedloader, _baseloader); _createclass(mozchunkedloader, null, [{ key: 'issupported', value: function issupported() { try { var xhr = new xmlhttprequest(); // firefox 37- requires .open() to be called before setting responsetype xhr.open('get', 'https://example.com', true); xhr.responsetype = 'moz-chunked-arraybuffer'; return xhr.responsetype === 'moz-chunked-arraybuffer'; } catch (e) { _logger2.default.w('mozchunkedloader', e.message); return false; } } }]); function mozchunkedloader(seekhandler, config) { _classcallcheck(this, mozchunkedloader); var _this = _possibleconstructorreturn(this, (mozchunkedloader.__proto__ || object.getprototypeof(mozchunkedloader)).call(this, 'xhr-moz-chunked-loader')); _this.tag = 'mozchunkedloader'; _this._seekhandler = seekhandler; _this._config = config; _this._needstash = true; _this._xhr = null; _this._requestabort = false; _this._contentlength = null; _this._receivedlength = 0; return _this; } _createclass(mozchunkedloader, [{ key: 'destroy', value: function destroy() { if (this.isworking()) { this.abort(); } if (this._xhr) { this._xhr.onreadystatechange = null; this._xhr.onprogress = null; this._xhr.onloadend = null; this._xhr.onerror = null; this._xhr = null; } _get(mozchunkedloader.prototype.__proto__ || object.getprototypeof(mozchunkedloader.prototype), 'destroy', this).call(this); } }, { key: 'open', value: function open(datasource, range) { this._datasource = datasource; this._range = range; var sourceurl = datasource.url; if (this._config.reuseredirectedurl && datasource.redirectedurl != undefined) { sourceurl = datasource.redirectedurl; } var seekconfig = this._seekhandler.getconfig(sourceurl, range); this._requesturl = seekconfig.url; var xhr = this._xhr = new xmlhttprequest(); xhr.open('get', seekconfig.url, true); xhr.responsetype = 'moz-chunked-arraybuffer'; xhr.onreadystatechange = this._onreadystatechange.bind(this); xhr.onprogress = this._onprogress.bind(this); xhr.onloadend = this._onloadend.bind(this); xhr.onerror = this._onxhrerror.bind(this); // cors is auto detected and enabled by xhr // withcredentials is disabled by default if (datasource.withcredentials) { xhr.withcredentials = true; } if (_typeof(seekconfig.headers) === 'object') { var headers = seekconfig.headers; for (var key in headers) { if (headers.hasownproperty(key)) { xhr.setrequestheader(key, headers[key]); } } } this._status = _loader.loaderstatus.kconnecting; xhr.send(); } }, { key: 'abort', value: function abort() { this._requestabort = true; if (this._xhr) { this._xhr.abort(); } this._status = _loader.loaderstatus.kcomplete; } }, { key: '_onreadystatechange', value: function _onreadystatechange(e) { var xhr = e.target; if (xhr.readystate === 2) { // headers_received if (xhr.responseurl != undefined && xhr.responseurl !== this._requesturl) { if (this._onurlredirect) { var redirectedurl = this._seekhandler.removeurlparameters(xhr.responseurl); this._onurlredirect(redirectedurl); } } if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) { this._status = _loader.loaderstatus.kerror; if (this._onerror) { this._onerror(_loader.loadererrors.http_status_code_invalid, { code: xhr.status, msg: xhr.statustext }); } else { throw new _exception.runtimeexception('mozchunkedloader: http code invalid, ' + xhr.status + ' ' + xhr.statustext); } } else { this._status = _loader.loaderstatus.kbuffering; } } } }, { key: '_onprogress', value: function _onprogress(e) { if (this._status === _loader.loaderstatus.kerror) { // ignore error response return; } if (this._contentlength === null) { if (e.total !== null && e.total !== 0) { this._contentlength = e.total; if (this._oncontentlengthknown) { this._oncontentlengthknown(this._contentlength); } } } var chunk = e.target.response; var bytestart = this._range.from + this._receivedlength; this._receivedlength += chunk.bytelength; if (this._ondataarrival) { this._ondataarrival(chunk, bytestart, this._receivedlength); } } }, { key: '_onloadend', value: function _onloadend(e) { if (this._requestabort === true) { this._requestabort = false; return; } else if (this._status === _loader.loaderstatus.kerror) { return; } this._status = _loader.loaderstatus.kcomplete; if (this._oncomplete) { this._oncomplete(this._range.from, this._range.from + this._receivedlength - 1); } } }, { key: '_onxhrerror', value: function _onxhrerror(e) { this._status = _loader.loaderstatus.kerror; var type = 0; var info = null; if (this._contentlength && e.loaded < this._contentlength) { type = _loader.loadererrors.early_eof; info = { code: -1, msg: 'moz-chunked stream meet early-eof' }; } else { type = _loader.loadererrors.exception; info = { code: -1, msg: e.constructor.name + ' ' + e.type }; } if (this._onerror) { this._onerror(type, info); } else { throw new _exception.runtimeexception(info.msg); } } }]); return mozchunkedloader; }(_loader.baseloader); exports.default = mozchunkedloader; },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],30:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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); } }; 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; }; }(); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _loader = _dereq_('./loader.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } 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; } 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; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ /* notice: ms-stream may cause ie/edge browser crash if seek too frequently!!! * the browser may crash in wininet.dll. disable for now. * * for ie11/edge browser by microsoft which supports `xhr.responsetype = 'ms-stream'` * notice that ms-stream api sucks. the buffer is always expanding along with downloading. * * we need to abort the xhr if buffer size exceeded limit size (e.g. 16 mib), then do reconnect. * in order to release previous arraybuffer to avoid memory leak * * otherwise, the arraybuffer will increase to a terrible size that equals final file size. */ var msstreamloader = function (_baseloader) { _inherits(msstreamloader, _baseloader); _createclass(msstreamloader, null, [{ key: 'issupported', value: function issupported() { try { if (typeof self.msstream === 'undefined' || typeof self.msstreamreader === 'undefined') { return false; } var xhr = new xmlhttprequest(); xhr.open('get', 'https://example.com', true); xhr.responsetype = 'ms-stream'; return xhr.responsetype === 'ms-stream'; } catch (e) { _logger2.default.w('msstreamloader', e.message); return false; } } }]); function msstreamloader(seekhandler, config) { _classcallcheck(this, msstreamloader); var _this = _possibleconstructorreturn(this, (msstreamloader.__proto__ || object.getprototypeof(msstreamloader)).call(this, 'xhr-msstream-loader')); _this.tag = 'msstreamloader'; _this._seekhandler = seekhandler; _this._config = config; _this._needstash = true; _this._xhr = null; _this._reader = null; // msstreamreader _this._totalrange = null; _this._currentrange = null; _this._currentrequesturl = null; _this._currentredirectedurl = null; _this._contentlength = null; _this._receivedlength = 0; _this._bufferlimit = 16 * 1024 * 1024; // 16mb _this._lasttimebuffersize = 0; _this._isreconnecting = false; return _this; } _createclass(msstreamloader, [{ key: 'destroy', value: function destroy() { if (this.isworking()) { this.abort(); } if (this._reader) { this._reader.onprogress = null; this._reader.onload = null; this._reader.onerror = null; this._reader = null; } if (this._xhr) { this._xhr.onreadystatechange = null; this._xhr = null; } _get(msstreamloader.prototype.__proto__ || object.getprototypeof(msstreamloader.prototype), 'destroy', this).call(this); } }, { key: 'open', value: function open(datasource, range) { this._internalopen(datasource, range, false); } }, { key: '_internalopen', value: function _internalopen(datasource, range, issubrange) { this._datasource = datasource; if (!issubrange) { this._totalrange = range; } else { this._currentrange = range; } var sourceurl = datasource.url; if (this._config.reuseredirectedurl) { if (this._currentredirectedurl != undefined) { sourceurl = this._currentredirectedurl; } else if (datasource.redirectedurl != undefined) { sourceurl = datasource.redirectedurl; } } var seekconfig = this._seekhandler.getconfig(sourceurl, range); this._currentrequesturl = seekconfig.url; var reader = this._reader = new self.msstreamreader(); reader.onprogress = this._msronprogress.bind(this); reader.onload = this._msronload.bind(this); reader.onerror = this._msronerror.bind(this); var xhr = this._xhr = new xmlhttprequest(); xhr.open('get', seekconfig.url, true); xhr.responsetype = 'ms-stream'; xhr.onreadystatechange = this._xhronreadystatechange.bind(this); xhr.onerror = this._xhronerror.bind(this); if (datasource.withcredentials) { xhr.withcredentials = true; } if (_typeof(seekconfig.headers) === 'object') { var headers = seekconfig.headers; for (var key in headers) { if (headers.hasownproperty(key)) { xhr.setrequestheader(key, headers[key]); } } } if (this._isreconnecting) { this._isreconnecting = false; } else { this._status = _loader.loaderstatus.kconnecting; } xhr.send(); } }, { key: 'abort', value: function abort() { this._internalabort(); this._status = _loader.loaderstatus.kcomplete; } }, { key: '_internalabort', value: function _internalabort() { if (this._reader) { if (this._reader.readystate === 1) { // loading this._reader.abort(); } this._reader.onprogress = null; this._reader.onload = null; this._reader.onerror = null; this._reader = null; } if (this._xhr) { this._xhr.abort(); this._xhr.onreadystatechange = null; this._xhr = null; } } }, { key: '_xhronreadystatechange', value: function _xhronreadystatechange(e) { var xhr = e.target; if (xhr.readystate === 2) { // headers_received if (xhr.status >= 200 && xhr.status <= 299) { this._status = _loader.loaderstatus.kbuffering; if (xhr.responseurl != undefined) { var redirectedurl = this._seekhandler.removeurlparameters(xhr.responseurl); if (xhr.responseurl !== this._currentrequesturl && redirectedurl !== this._currentredirectedurl) { this._currentredirectedurl = redirectedurl; if (this._onurlredirect) { this._onurlredirect(redirectedurl); } } } var lengthheader = xhr.getresponseheader('content-length'); if (lengthheader != null && this._contentlength == null) { var length = parseint(lengthheader); if (length > 0) { this._contentlength = length; if (this._oncontentlengthknown) { this._oncontentlengthknown(this._contentlength); } } } } else { this._status = _loader.loaderstatus.kerror; if (this._onerror) { this._onerror(_loader.loadererrors.http_status_code_invalid, { code: xhr.status, msg: xhr.statustext }); } else { throw new _exception.runtimeexception('msstreamloader: http code invalid, ' + xhr.status + ' ' + xhr.statustext); } } } else if (xhr.readystate === 3) { // loading if (xhr.status >= 200 && xhr.status <= 299) { this._status = _loader.loaderstatus.kbuffering; var msstream = xhr.response; this._reader.readasarraybuffer(msstream); } } } }, { key: '_xhronerror', value: function _xhronerror(e) { this._status = _loader.loaderstatus.kerror; var type = _loader.loadererrors.exception; var info = { code: -1, msg: e.constructor.name + ' ' + e.type }; if (this._onerror) { this._onerror(type, info); } else { throw new _exception.runtimeexception(info.msg); } } }, { key: '_msronprogress', value: function _msronprogress(e) { var reader = e.target; var bigbuffer = reader.result; if (bigbuffer == null) { // result may be null, workaround for buggy m$ this._doreconnectifneeded(); return; } var slice = bigbuffer.slice(this._lasttimebuffersize); this._lasttimebuffersize = bigbuffer.bytelength; var bytestart = this._totalrange.from + this._receivedlength; this._receivedlength += slice.bytelength; if (this._ondataarrival) { this._ondataarrival(slice, bytestart, this._receivedlength); } if (bigbuffer.bytelength >= this._bufferlimit) { _logger2.default.v(this.tag, 'msstream buffer exceeded max size near ' + (bytestart + slice.bytelength) + ', reconnecting...'); this._doreconnectifneeded(); } } }, { key: '_doreconnectifneeded', value: function _doreconnectifneeded() { if (this._contentlength == null || this._receivedlength < this._contentlength) { this._isreconnecting = true; this._lasttimebuffersize = 0; this._internalabort(); var range = { from: this._totalrange.from + this._receivedlength, to: -1 }; this._internalopen(this._datasource, range, true); } } }, { key: '_msronload', value: function _msronload(e) { // actually it is oncomplete event this._status = _loader.loaderstatus.kcomplete; if (this._oncomplete) { this._oncomplete(this._totalrange.from, this._totalrange.from + this._receivedlength - 1); } } }, { key: '_msronerror', value: function _msronerror(e) { this._status = _loader.loaderstatus.kerror; var type = 0; var info = null; if (this._contentlength && this._receivedlength < this._contentlength) { type = _loader.loadererrors.early_eof; info = { code: -1, msg: 'msstream meet early-eof' }; } else { type = _loader.loadererrors.early_eof; info = { code: -1, msg: e.constructor.name + ' ' + e.type }; } if (this._onerror) { this._onerror(type, info); } else { throw new _exception.runtimeexception(info.msg); } } }]); return msstreamloader; }(_loader.baseloader); exports.default = msstreamloader; },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],31:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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); } }; 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; }; }(); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _speedsampler = _dereq_('./speed-sampler.js'); var _speedsampler2 = _interoprequiredefault(_speedsampler); var _loader = _dereq_('./loader.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } 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; } 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; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // universal io loader, implemented by adding range header in xhr's request header var rangeloader = function (_baseloader) { _inherits(rangeloader, _baseloader); _createclass(rangeloader, null, [{ key: 'issupported', value: function issupported() { try { var xhr = new xmlhttprequest(); xhr.open('get', 'https://example.com', true); xhr.responsetype = 'arraybuffer'; return xhr.responsetype === 'arraybuffer'; } catch (e) { _logger2.default.w('rangeloader', e.message); return false; } } }]); function rangeloader(seekhandler, config) { _classcallcheck(this, rangeloader); var _this = _possibleconstructorreturn(this, (rangeloader.__proto__ || object.getprototypeof(rangeloader)).call(this, 'xhr-range-loader')); _this.tag = 'rangeloader'; _this._seekhandler = seekhandler; _this._config = config; _this._needstash = false; _this._chunksizekblist = [128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192]; _this._currentchunksizekb = 384; _this._currentspeednormalized = 0; _this._zerospeedchunkcount = 0; _this._xhr = null; _this._speedsampler = new _speedsampler2.default(); _this._requestabort = false; _this._waitfortotallength = false; _this._totallengthreceived = false; _this._currentrequesturl = null; _this._currentredirectedurl = null; _this._currentrequestrange = null; _this._totallength = null; // size of the entire file _this._contentlength = null; // content-length of entire request range _this._receivedlength = 0; // total received bytes _this._lasttimeloaded = 0; // received bytes of current request sub-range return _this; } _createclass(rangeloader, [{ key: 'destroy', value: function destroy() { if (this.isworking()) { this.abort(); } if (this._xhr) { this._xhr.onreadystatechange = null; this._xhr.onprogress = null; this._xhr.onload = null; this._xhr.onerror = null; this._xhr = null; } _get(rangeloader.prototype.__proto__ || object.getprototypeof(rangeloader.prototype), 'destroy', this).call(this); } }, { key: 'open', value: function open(datasource, range) { this._datasource = datasource; this._range = range; this._status = _loader.loaderstatus.kconnecting; var usereftotallength = false; if (this._datasource.filesize != undefined && this._datasource.filesize !== 0) { usereftotallength = true; this._totallength = this._datasource.filesize; } if (!this._totallengthreceived && !usereftotallength) { // we need total filesize this._waitfortotallength = true; this._internalopen(this._datasource, { from: 0, to: -1 }); } else { // we have filesize, start loading this._opensubrange(); } } }, { key: '_opensubrange', value: function _opensubrange() { var chunksize = this._currentchunksizekb * 1024; var from = this._range.from + this._receivedlength; var to = from + chunksize; if (this._contentlength != null) { if (to - this._range.from >= this._contentlength) { to = this._range.from + this._contentlength - 1; } } this._currentrequestrange = { from: from, to: to }; this._internalopen(this._datasource, this._currentrequestrange); } }, { key: '_internalopen', value: function _internalopen(datasource, range) { this._lasttimeloaded = 0; var sourceurl = datasource.url; if (this._config.reuseredirectedurl) { if (this._currentredirectedurl != undefined) { sourceurl = this._currentredirectedurl; } else if (datasource.redirectedurl != undefined) { sourceurl = datasource.redirectedurl; } } var seekconfig = this._seekhandler.getconfig(sourceurl, range); this._currentrequesturl = seekconfig.url; var xhr = this._xhr = new xmlhttprequest(); xhr.open('get', seekconfig.url, true); xhr.responsetype = 'arraybuffer'; xhr.onreadystatechange = this._onreadystatechange.bind(this); xhr.onprogress = this._onprogress.bind(this); xhr.onload = this._onload.bind(this); xhr.onerror = this._onxhrerror.bind(this); if (datasource.withcredentials) { xhr.withcredentials = true; } if (_typeof(seekconfig.headers) === 'object') { var headers = seekconfig.headers; for (var key in headers) { if (headers.hasownproperty(key)) { xhr.setrequestheader(key, headers[key]); } } } xhr.send(); } }, { key: 'abort', value: function abort() { this._requestabort = true; this._internalabort(); this._status = _loader.loaderstatus.kcomplete; } }, { key: '_internalabort', value: function _internalabort() { if (this._xhr) { this._xhr.onreadystatechange = null; this._xhr.onprogress = null; this._xhr.onload = null; this._xhr.onerror = null; this._xhr.abort(); this._xhr = null; } } }, { key: '_onreadystatechange', value: function _onreadystatechange(e) { var xhr = e.target; if (xhr.readystate === 2) { // headers_received if (xhr.responseurl != undefined) { // if the browser support this property var redirectedurl = this._seekhandler.removeurlparameters(xhr.responseurl); if (xhr.responseurl !== this._currentrequesturl && redirectedurl !== this._currentredirectedurl) { this._currentredirectedurl = redirectedurl; if (this._onurlredirect) { this._onurlredirect(redirectedurl); } } } if (xhr.status >= 200 && xhr.status <= 299) { if (this._waitfortotallength) { return; } this._status = _loader.loaderstatus.kbuffering; } else { this._status = _loader.loaderstatus.kerror; if (this._onerror) { this._onerror(_loader.loadererrors.http_status_code_invalid, { code: xhr.status, msg: xhr.statustext }); } else { throw new _exception.runtimeexception('rangeloader: http code invalid, ' + xhr.status + ' ' + xhr.statustext); } } } } }, { key: '_onprogress', value: function _onprogress(e) { if (this._status === _loader.loaderstatus.kerror) { // ignore error response return; } if (this._contentlength === null) { var opennextrange = false; if (this._waitfortotallength) { this._waitfortotallength = false; this._totallengthreceived = true; opennextrange = true; var total = e.total; this._internalabort(); if (total != null & total !== 0) { this._totallength = total; } } // calculate currrent request range's contentlength if (this._range.to === -1) { this._contentlength = this._totallength - this._range.from; } else { // to !== -1 this._contentlength = this._range.to - this._range.from + 1; } if (opennextrange) { this._opensubrange(); return; } if (this._oncontentlengthknown) { this._oncontentlengthknown(this._contentlength); } } var delta = e.loaded - this._lasttimeloaded; this._lasttimeloaded = e.loaded; this._speedsampler.addbytes(delta); } }, { key: '_normalizespeed', value: function _normalizespeed(input) { var list = this._chunksizekblist; var last = list.length - 1; var mid = 0; var lbound = 0; var ubound = last; if (input < list[0]) { return list[0]; } while (lbound <= ubound) { mid = lbound + math.floor((ubound - lbound) / 2); if (mid === last || input >= list[mid] && input < list[mid + 1]) { return list[mid]; } else if (list[mid] < input) { lbound = mid + 1; } else { ubound = mid - 1; } } } }, { key: '_onload', value: function _onload(e) { if (this._status === _loader.loaderstatus.kerror) { // ignore error response return; } if (this._waitfortotallength) { this._waitfortotallength = false; return; } this._lasttimeloaded = 0; var kbps = this._speedsampler.lastsecondkbps; if (kbps === 0) { this._zerospeedchunkcount++; if (this._zerospeedchunkcount >= 3) { // try get currentkbps after 3 chunks kbps = this._speedsampler.currentkbps; } } if (kbps !== 0) { var normalized = this._normalizespeed(kbps); if (this._currentspeednormalized !== normalized) { this._currentspeednormalized = normalized; this._currentchunksizekb = normalized; } } var chunk = e.target.response; var bytestart = this._range.from + this._receivedlength; this._receivedlength += chunk.bytelength; var reportcomplete = false; if (this._contentlength != null && this._receivedlength < this._contentlength) { // continue load next chunk this._opensubrange(); } else { reportcomplete = true; } // dispatch received chunk if (this._ondataarrival) { this._ondataarrival(chunk, bytestart, this._receivedlength); } if (reportcomplete) { this._status = _loader.loaderstatus.kcomplete; if (this._oncomplete) { this._oncomplete(this._range.from, this._range.from + this._receivedlength - 1); } } } }, { key: '_onxhrerror', value: function _onxhrerror(e) { this._status = _loader.loaderstatus.kerror; var type = 0; var info = null; if (this._contentlength && this._receivedlength > 0 && this._receivedlength < this._contentlength) { type = _loader.loadererrors.early_eof; info = { code: -1, msg: 'rangeloader meet early-eof' }; } else { type = _loader.loadererrors.exception; info = { code: -1, msg: e.constructor.name + ' ' + e.type }; } if (this._onerror) { this._onerror(type, info); } else { throw new _exception.runtimeexception(info.msg); } } }, { key: 'currentspeed', get: function get() { return this._speedsampler.lastsecondkbps; } }]); return rangeloader; }(_loader.baseloader); exports.default = rangeloader; },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24,"./speed-sampler.js":27}],32:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _browser = _dereq_('../utils/browser.js'); var _browser2 = _interoprequiredefault(_browser); var _playerevents = _dereq_('./player-events.js'); var _playerevents2 = _interoprequiredefault(_playerevents); var _transmuxer = _dereq_('../core/transmuxer.js'); var _transmuxer2 = _interoprequiredefault(_transmuxer); var _transmuxingevents = _dereq_('../core/transmuxing-events.js'); var _transmuxingevents2 = _interoprequiredefault(_transmuxingevents); var _msecontroller = _dereq_('../core/mse-controller.js'); var _msecontroller2 = _interoprequiredefault(_msecontroller); var _mseevents = _dereq_('../core/mse-events.js'); var _mseevents2 = _interoprequiredefault(_mseevents); var _playererrors = _dereq_('./player-errors.js'); var _config = _dereq_('../config.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var flvplayer = function () { function flvplayer(mediadatasource, config) { _classcallcheck(this, flvplayer); this.tag = 'flvplayer'; this._type = 'flvplayer'; this._emitter = new _events2.default(); this._config = (0, _config.createdefaultconfig)(); if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { object.assign(this._config, config); } if (mediadatasource.type.tolowercase() !== 'flv') { throw new _exception.invalidargumentexception('flvplayer requires an flv mediadatasource input!'); } if (mediadatasource.islive === true) { this._config.islive = true; } this.e = { onvloadedmetadata: this._onvloadedmetadata.bind(this), onvseeking: this._onvseeking.bind(this), onvcanplay: this._onvcanplay.bind(this), onvstalled: this._onvstalled.bind(this), onvprogress: this._onvprogress.bind(this) }; if (self.performance && self.performance.now) { this._now = self.performance.now.bind(self.performance); } else { this._now = date.now; } this._pendingseektime = null; // in seconds this._requestsettime = false; this._seekpointrecord = null; this._progresschecker = null; this._mediadatasource = mediadatasource; this._mediaelement = null; this._msectl = null; this._transmuxer = null; this._msesourceopened = false; this._haspendingload = false; this._receivedcanplay = false; this._mediainfo = null; this._statisticsinfo = null; var chromeneedidrfix = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661); this._alwaysseekkeyframe = chromeneedidrfix || _browser2.default.msedge || _browser2.default.msie ? true : false; if (this._alwaysseekkeyframe) { this._config.accurateseek = false; } } _createclass(flvplayer, [{ key: 'destroy', value: function destroy() { if (this._progresschecker != null) { window.clearinterval(this._progresschecker); this._progresschecker = null; } if (this._transmuxer) { this.unload(); } if (this._mediaelement) { this.detachmediaelement(); } this.e = null; this._mediadatasource = null; this._emitter.removealllisteners(); this._emitter = null; } }, { key: 'on', value: function on(event, listener) { var _this = this; if (event === _playerevents2.default.media_info) { if (this._mediainfo != null) { promise.resolve().then(function () { _this._emitter.emit(_playerevents2.default.media_info, _this.mediainfo); }); } } else if (event === _playerevents2.default.statistics_info) { if (this._statisticsinfo != null) { promise.resolve().then(function () { _this._emitter.emit(_playerevents2.default.statistics_info, _this.statisticsinfo); }); } } this._emitter.addlistener(event, listener); } }, { key: 'off', value: function off(event, listener) { this._emitter.removelistener(event, listener); } }, { key: 'attachmediaelement', value: function attachmediaelement(mediaelement) { var _this2 = this; this._mediaelement = mediaelement; mediaelement.addeventlistener('loadedmetadata', this.e.onvloadedmetadata); mediaelement.addeventlistener('seeking', this.e.onvseeking); mediaelement.addeventlistener('canplay', this.e.onvcanplay); mediaelement.addeventlistener('stalled', this.e.onvstalled); mediaelement.addeventlistener('progress', this.e.onvprogress); this._msectl = new _msecontroller2.default(this._config); this._msectl.on(_mseevents2.default.update_end, this._onmseupdateend.bind(this)); this._msectl.on(_mseevents2.default.buffer_full, this._onmsebufferfull.bind(this)); this._msectl.on(_mseevents2.default.source_open, function () { _this2._msesourceopened = true; if (_this2._haspendingload) { _this2._haspendingload = false; _this2.load(); } }); this._msectl.on(_mseevents2.default.error, function (info) { _this2._emitter.emit(_playerevents2.default.error, _playererrors.errortypes.media_error, _playererrors.errordetails.media_mse_error, info); }); this._msectl.attachmediaelement(mediaelement); if (this._pendingseektime != null) { try { mediaelement.currenttime = this._pendingseektime; this._pendingseektime = null; } catch (e) { // ie11 may throw invalidstateerror if readystate === 0 // we can defer set currenttime operation after loadedmetadata } } } }, { key: 'detachmediaelement', value: function detachmediaelement() { if (this._mediaelement) { this._msectl.detachmediaelement(); this._mediaelement.removeeventlistener('loadedmetadata', this.e.onvloadedmetadata); this._mediaelement.removeeventlistener('seeking', this.e.onvseeking); this._mediaelement.removeeventlistener('canplay', this.e.onvcanplay); this._mediaelement.removeeventlistener('stalled', this.e.onvstalled); this._mediaelement.removeeventlistener('progress', this.e.onvprogress); this._mediaelement = null; } if (this._msectl) { this._msectl.destroy(); this._msectl = null; } } }, { key: 'load', value: function load() { var _this3 = this; if (!this._mediaelement) { throw new _exception.illegalstateexception('htmlmediaelement must be attached before load()!'); } if (this._transmuxer) { throw new _exception.illegalstateexception('flvplayer.load() has been called, please call unload() first!'); } if (this._haspendingload) { return; } if (this._config.deferloadaftersourceopen && this._msesourceopened === false) { this._haspendingload = true; return; } if (this._mediaelement.readystate > 0) { this._requestsettime = true; // ie11 may throw invalidstateerror if readystate === 0 this._mediaelement.currenttime = 0; } this._transmuxer = new _transmuxer2.default(this._mediadatasource, this._config); this._transmuxer.on(_transmuxingevents2.default.init_segment, function (type, is) { _this3._msectl.appendinitsegment(is); }); this._transmuxer.on(_transmuxingevents2.default.media_segment, function (type, ms) { _this3._msectl.appendmediasegment(ms); // lazyload check if (_this3._config.lazyload && !_this3._config.islive) { var currenttime = _this3._mediaelement.currenttime; if (ms.info.enddts >= (currenttime + _this3._config.lazyloadmaxduration) * 1000) { if (_this3._progresschecker == null) { _logger2.default.v(_this3.tag, 'maximum buffering duration exceeded, suspend transmuxing task'); _this3._suspendtransmuxer(); } } } }); this._transmuxer.on(_transmuxingevents2.default.loading_complete, function () { _this3._msectl.endofstream(); _this3._emitter.emit(_playerevents2.default.loading_complete); }); this._transmuxer.on(_transmuxingevents2.default.recovered_early_eof, function () { _this3._emitter.emit(_playerevents2.default.recovered_early_eof); }); this._transmuxer.on(_transmuxingevents2.default.io_error, function (detail, info) { _this3._emitter.emit(_playerevents2.default.error, _playererrors.errortypes.network_error, detail, info); }); this._transmuxer.on(_transmuxingevents2.default.demux_error, function (detail, info) { _this3._emitter.emit(_playerevents2.default.error, _playererrors.errortypes.media_error, detail, { code: -1, msg: info }); }); this._transmuxer.on(_transmuxingevents2.default.media_info, function (mediainfo) { _this3._mediainfo = mediainfo; _this3._emitter.emit(_playerevents2.default.media_info, object.assign({}, mediainfo)); }); this._transmuxer.on(_transmuxingevents2.default.statistics_info, function (statinfo) { _this3._statisticsinfo = _this3._fillstatisticsinfo(statinfo); _this3._emitter.emit(_playerevents2.default.statistics_info, object.assign({}, _this3._statisticsinfo)); }); this._transmuxer.on(_transmuxingevents2.default.recommend_seekpoint, function (milliseconds) { if (_this3._mediaelement && !_this3._config.accurateseek) { _this3._requestsettime = true; _this3._mediaelement.currenttime = milliseconds / 1000; } }); this._transmuxer.open(); } }, { key: 'unload', value: function unload() { if (this._mediaelement) { this._mediaelement.pause(); } if (this._msectl) { this._msectl.seek(0); } if (this._transmuxer) { this._transmuxer.close(); this._transmuxer.destroy(); this._transmuxer = null; } } }, { key: 'play', value: function play() { return this._mediaelement.play(); } }, { key: 'pause', value: function pause() { this._mediaelement.pause(); } }, { key: '_fillstatisticsinfo', value: function _fillstatisticsinfo(statinfo) { statinfo.playertype = this._type; if (!(this._mediaelement instanceof htmlvideoelement)) { return statinfo; } var hasqualityinfo = true; var decoded = 0; var dropped = 0; if (this._mediaelement.getvideoplaybackquality) { var quality = this._mediaelement.getvideoplaybackquality(); decoded = quality.totalvideoframes; dropped = quality.droppedvideoframes; } else if (this._mediaelement.webkitdecodedframecount != undefined) { decoded = this._mediaelement.webkitdecodedframecount; dropped = this._mediaelement.webkitdroppedframecount; } else { hasqualityinfo = false; } if (hasqualityinfo) { statinfo.decodedframes = decoded; statinfo.droppedframes = dropped; } return statinfo; } }, { key: '_onmseupdateend', value: function _onmseupdateend() { if (!this._config.lazyload || this._config.islive) { return; } var buffered = this._mediaelement.buffered; var currenttime = this._mediaelement.currenttime; var currentrangestart = 0; var currentrangeend = 0; for (var i = 0; i < buffered.length; i++) { var start = buffered.start(i); var end = buffered.end(i); if (start <= currenttime && currenttime < end) { currentrangestart = start; currentrangeend = end; break; } } if (currentrangeend >= currenttime + this._config.lazyloadmaxduration && this._progresschecker == null) { _logger2.default.v(this.tag, 'maximum buffering duration exceeded, suspend transmuxing task'); this._suspendtransmuxer(); } } }, { key: '_onmsebufferfull', value: function _onmsebufferfull() { _logger2.default.v(this.tag, 'mse sourcebuffer is full, suspend transmuxing task'); if (this._progresschecker == null) { this._suspendtransmuxer(); } } }, { key: '_suspendtransmuxer', value: function _suspendtransmuxer() { if (this._transmuxer) { this._transmuxer.pause(); if (this._progresschecker == null) { this._progresschecker = window.setinterval(this._checkprogressandresume.bind(this), 1000); } } } }, { key: '_checkprogressandresume', value: function _checkprogressandresume() { var currenttime = this._mediaelement.currenttime; var buffered = this._mediaelement.buffered; var needresume = false; for (var i = 0; i < buffered.length; i++) { var from = buffered.start(i); var to = buffered.end(i); if (currenttime >= from && currenttime < to) { if (currenttime >= to - this._config.lazyloadrecoverduration) { needresume = true; } break; } } if (needresume) { window.clearinterval(this._progresschecker); this._progresschecker = null; if (needresume) { _logger2.default.v(this.tag, 'continue loading from paused position'); this._transmuxer.resume(); } } } }, { key: '_istimepointbuffered', value: function _istimepointbuffered(seconds) { var buffered = this._mediaelement.buffered; for (var i = 0; i < buffered.length; i++) { var from = buffered.start(i); var to = buffered.end(i); if (seconds >= from && seconds < to) { return true; } } return false; } }, { key: '_internalseek', value: function _internalseek(seconds) { var directseek = this._istimepointbuffered(seconds); var directseekbegin = false; var directseekbegintime = 0; if (seconds < 1.0 && this._mediaelement.buffered.length > 0) { var videobegintime = this._mediaelement.buffered.start(0); if (videobegintime < 1.0 && seconds < videobegintime || _browser2.default.safari) { directseekbegin = true; // also workaround for safari: seek to 0 may cause video stuck, use 0.1 to avoid directseekbegintime = _browser2.default.safari ? 0.1 : videobegintime; } } if (directseekbegin) { // seek to video begin, set currenttime directly if beginpts buffered this._requestsettime = true; this._mediaelement.currenttime = directseekbegintime; } else if (directseek) { // buffered position if (!this._alwaysseekkeyframe) { this._requestsettime = true; this._mediaelement.currenttime = seconds; } else { var idr = this._msectl.getnearestkeyframe(math.floor(seconds * 1000)); this._requestsettime = true; if (idr != null) { this._mediaelement.currenttime = idr.dts / 1000; } else { this._mediaelement.currenttime = seconds; } } if (this._progresschecker != null) { this._checkprogressandresume(); } } else { if (this._progresschecker != null) { window.clearinterval(this._progresschecker); this._progresschecker = null; } this._msectl.seek(seconds); this._transmuxer.seek(math.floor(seconds * 1000)); // in milliseconds // no need to set mediaelement.currenttime if non-accurateseek, // just wait for the recommend_seekpoint callback if (this._config.accurateseek) { this._requestsettime = true; this._mediaelement.currenttime = seconds; } } } }, { key: '_checkandapplyunbufferedseekpoint', value: function _checkandapplyunbufferedseekpoint() { if (this._seekpointrecord) { if (this._seekpointrecord.recordtime <= this._now() - 100) { var target = this._mediaelement.currenttime; this._seekpointrecord = null; if (!this._istimepointbuffered(target)) { if (this._progresschecker != null) { window.cleartimeout(this._progresschecker); this._progresschecker = null; } // .currenttime is consists with .buffered timestamp // chrome/edge use dts, while firefox/safari use pts this._msectl.seek(target); this._transmuxer.seek(math.floor(target * 1000)); // set currenttime if accurateseek, or wait for recommend_seekpoint callback if (this._config.accurateseek) { this._requestsettime = true; this._mediaelement.currenttime = target; } } } else { window.settimeout(this._checkandapplyunbufferedseekpoint.bind(this), 50); } } } }, { key: '_checkandresumestuckplayback', value: function _checkandresumestuckplayback(stalled) { var media = this._mediaelement; if (stalled || !this._receivedcanplay || media.readystate < 2) { // have_current_data var buffered = media.buffered; if (buffered.length > 0 && media.currenttime < buffered.start(0)) { _logger2.default.w(this.tag, 'playback seems stuck at ' + media.currenttime + ', seek to ' + buffered.start(0)); this._requestsettime = true; this._mediaelement.currenttime = buffered.start(0); this._mediaelement.removeeventlistener('progress', this.e.onvprogress); } } else { // playback didn't stuck, remove progress event listener this._mediaelement.removeeventlistener('progress', this.e.onvprogress); } } }, { key: '_onvloadedmetadata', value: function _onvloadedmetadata(e) { if (this._pendingseektime != null) { this._mediaelement.currenttime = this._pendingseektime; this._pendingseektime = null; } } }, { key: '_onvseeking', value: function _onvseeking(e) { // handle seeking request from browser's progress bar var target = this._mediaelement.currenttime; var buffered = this._mediaelement.buffered; if (this._requestsettime) { this._requestsettime = false; return; } if (target < 1.0 && buffered.length > 0) { // seek to video begin, set currenttime directly if beginpts buffered var videobegintime = buffered.start(0); if (videobegintime < 1.0 && target < videobegintime || _browser2.default.safari) { this._requestsettime = true; // also workaround for safari: seek to 0 may cause video stuck, use 0.1 to avoid this._mediaelement.currenttime = _browser2.default.safari ? 0.1 : videobegintime; return; } } if (this._istimepointbuffered(target)) { if (this._alwaysseekkeyframe) { var idr = this._msectl.getnearestkeyframe(math.floor(target * 1000)); if (idr != null) { this._requestsettime = true; this._mediaelement.currenttime = idr.dts / 1000; } } if (this._progresschecker != null) { this._checkprogressandresume(); } return; } this._seekpointrecord = { seekpoint: target, recordtime: this._now() }; window.settimeout(this._checkandapplyunbufferedseekpoint.bind(this), 50); } }, { key: '_onvcanplay', value: function _onvcanplay(e) { this._receivedcanplay = true; this._mediaelement.removeeventlistener('canplay', this.e.onvcanplay); } }, { key: '_onvstalled', value: function _onvstalled(e) { this._checkandresumestuckplayback(true); } }, { key: '_onvprogress', value: function _onvprogress(e) { this._checkandresumestuckplayback(); } }, { key: 'type', get: function get() { return this._type; } }, { key: 'buffered', get: function get() { return this._mediaelement.buffered; } }, { key: 'duration', get: function get() { return this._mediaelement.duration; } }, { key: 'volume', get: function get() { return this._mediaelement.volume; }, set: function set(value) { this._mediaelement.volume = value; } }, { key: 'muted', get: function get() { return this._mediaelement.muted; }, set: function set(muted) { this._mediaelement.muted = muted; } }, { key: 'currenttime', get: function get() { if (this._mediaelement) { return this._mediaelement.currenttime; } return 0; }, set: function set(seconds) { if (this._mediaelement) { this._internalseek(seconds); } else { this._pendingseektime = seconds; } } }, { key: 'mediainfo', get: function get() { return object.assign({}, this._mediainfo); } }, { key: 'statisticsinfo', get: function get() { if (this._statisticsinfo == null) { this._statisticsinfo = {}; } this._statisticsinfo = this._fillstatisticsinfo(this._statisticsinfo); return object.assign({}, this._statisticsinfo); } }]); return flvplayer; }(); exports.default = flvplayer; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _playerevents = _dereq_('./player-events.js'); var _playerevents2 = _interoprequiredefault(_playerevents); var _config = _dereq_('../config.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } // player wrapper for browser's native player (htmlvideoelement) without mediasource src. var nativeplayer = function () { function nativeplayer(mediadatasource, config) { _classcallcheck(this, nativeplayer); this.tag = 'nativeplayer'; this._type = 'nativeplayer'; this._emitter = new _events2.default(); this._config = (0, _config.createdefaultconfig)(); if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { object.assign(this._config, config); } if (mediadatasource.type.tolowercase() === 'flv') { throw new _exception.invalidargumentexception('nativeplayer does\'t support flv mediadatasource input!'); } if (mediadatasource.hasownproperty('segments')) { throw new _exception.invalidargumentexception('nativeplayer(' + mediadatasource.type + ') doesn\'t support multipart playback!'); } this.e = { onvloadedmetadata: this._onvloadedmetadata.bind(this) }; this._pendingseektime = null; this._statisticsreporter = null; this._mediadatasource = mediadatasource; this._mediaelement = null; } _createclass(nativeplayer, [{ key: 'destroy', value: function destroy() { if (this._mediaelement) { this.unload(); this.detachmediaelement(); } this.e = null; this._mediadatasource = null; this._emitter.removealllisteners(); this._emitter = null; } }, { key: 'on', value: function on(event, listener) { var _this = this; if (event === _playerevents2.default.media_info) { if (this._mediaelement != null && this._mediaelement.readystate !== 0) { // have_nothing promise.resolve().then(function () { _this._emitter.emit(_playerevents2.default.media_info, _this.mediainfo); }); } } else if (event === _playerevents2.default.statistics_info) { if (this._mediaelement != null && this._mediaelement.readystate !== 0) { promise.resolve().then(function () { _this._emitter.emit(_playerevents2.default.statistics_info, _this.statisticsinfo); }); } } this._emitter.addlistener(event, listener); } }, { key: 'off', value: function off(event, listener) { this._emitter.removelistener(event, listener); } }, { key: 'attachmediaelement', value: function attachmediaelement(mediaelement) { this._mediaelement = mediaelement; mediaelement.addeventlistener('loadedmetadata', this.e.onvloadedmetadata); if (this._pendingseektime != null) { try { mediaelement.currenttime = this._pendingseektime; this._pendingseektime = null; } catch (e) { // ie11 may throw invalidstateerror if readystate === 0 // defer set currenttime operation after loadedmetadata } } } }, { key: 'detachmediaelement', value: function detachmediaelement() { if (this._mediaelement) { this._mediaelement.src = ''; this._mediaelement.removeattribute('src'); this._mediaelement.removeeventlistener('loadedmetadata', this.e.onvloadedmetadata); this._mediaelement = null; } if (this._statisticsreporter != null) { window.clearinterval(this._statisticsreporter); this._statisticsreporter = null; } } }, { key: 'load', value: function load() { if (!this._mediaelement) { throw new _exception.illegalstateexception('htmlmediaelement must be attached before load()!'); } this._mediaelement.src = this._mediadatasource.url; if (this._mediaelement.readystate > 0) { this._mediaelement.currenttime = 0; } this._mediaelement.preload = 'auto'; this._mediaelement.load(); this._statisticsreporter = window.setinterval(this._reportstatisticsinfo.bind(this), this._config.statisticsinforeportinterval); } }, { key: 'unload', value: function unload() { if (this._mediaelement) { this._mediaelement.src = ''; this._mediaelement.removeattribute('src'); } if (this._statisticsreporter != null) { window.clearinterval(this._statisticsreporter); this._statisticsreporter = null; } } }, { key: 'play', value: function play() { return this._mediaelement.play(); } }, { key: 'pause', value: function pause() { this._mediaelement.pause(); } }, { key: '_onvloadedmetadata', value: function _onvloadedmetadata(e) { if (this._pendingseektime != null) { this._mediaelement.currenttime = this._pendingseektime; this._pendingseektime = null; } this._emitter.emit(_playerevents2.default.media_info, this.mediainfo); } }, { key: '_reportstatisticsinfo', value: function _reportstatisticsinfo() { this._emitter.emit(_playerevents2.default.statistics_info, this.statisticsinfo); } }, { key: 'type', get: function get() { return this._type; } }, { key: 'buffered', get: function get() { return this._mediaelement.buffered; } }, { key: 'duration', get: function get() { return this._mediaelement.duration; } }, { key: 'volume', get: function get() { return this._mediaelement.volume; }, set: function set(value) { this._mediaelement.volume = value; } }, { key: 'muted', get: function get() { return this._mediaelement.muted; }, set: function set(muted) { this._mediaelement.muted = muted; } }, { key: 'currenttime', get: function get() { if (this._mediaelement) { return this._mediaelement.currenttime; } return 0; }, set: function set(seconds) { if (this._mediaelement) { this._mediaelement.currenttime = seconds; } else { this._pendingseektime = seconds; } } }, { key: 'mediainfo', get: function get() { var mediaprefix = this._mediaelement instanceof htmlaudioelement ? 'audio/' : 'video/'; var info = { mimetype: mediaprefix + this._mediadatasource.type }; if (this._mediaelement) { info.duration = math.floor(this._mediaelement.duration * 1000); if (this._mediaelement instanceof htmlvideoelement) { info.width = this._mediaelement.videowidth; info.height = this._mediaelement.videoheight; } } return info; } }, { key: 'statisticsinfo', get: function get() { var info = { playertype: this._type, url: this._mediadatasource.url }; if (!(this._mediaelement instanceof htmlvideoelement)) { return info; } var hasqualityinfo = true; var decoded = 0; var dropped = 0; if (this._mediaelement.getvideoplaybackquality) { var quality = this._mediaelement.getvideoplaybackquality(); decoded = quality.totalvideoframes; dropped = quality.droppedvideoframes; } else if (this._mediaelement.webkitdecodedframecount != undefined) { decoded = this._mediaelement.webkitdecodedframecount; dropped = this._mediaelement.webkitdroppedframecount; } else { hasqualityinfo = false; } if (hasqualityinfo) { info.decodedframes = decoded; info.droppedframes = dropped; } return info; } }]); return nativeplayer; }(); exports.default = nativeplayer; },{"../config.js":5,"../utils/exception.js":40,"./player-events.js":35,"events":2}],34:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); exports.errordetails = exports.errortypes = undefined; var _loader = _dereq_('../io/loader.js'); var _demuxerrors = _dereq_('../demux/demux-errors.js'); var _demuxerrors2 = _interoprequiredefault(_demuxerrors); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var errortypes = exports.errortypes = { network_error: 'networkerror', media_error: 'mediaerror', other_error: 'othererror' }; var errordetails = exports.errordetails = { network_exception: _loader.loadererrors.exception, network_status_code_invalid: _loader.loadererrors.http_status_code_invalid, network_timeout: _loader.loadererrors.connecting_timeout, network_unrecoverable_early_eof: _loader.loadererrors.unrecoverable_early_eof, media_mse_error: 'mediamseerror', media_format_error: _demuxerrors2.default.format_error, media_format_unsupported: _demuxerrors2.default.format_unsupported, media_codec_unsupported: _demuxerrors2.default.codec_unsupported }; },{"../demux/demux-errors.js":16,"../io/loader.js":24}],35:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var playerevents = { error: 'error', loading_complete: 'loading_complete', recovered_early_eof: 'recovered_early_eof', media_info: 'media_info', statistics_info: 'statistics_info' }; exports.default = playerevents; },{}],36:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * this file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js) * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var aac = function () { function aac() { _classcallcheck(this, aac); } _createclass(aac, null, [{ key: 'getsilentframe', value: function getsilentframe(codec, channelcount) { if (codec === 'mp4a.40.2') { // handle lc-aac if (channelcount === 1) { return new uint8array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); } else if (channelcount === 2) { return new uint8array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); } else if (channelcount === 3) { return new uint8array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); } else if (channelcount === 4) { return new uint8array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); } else if (channelcount === 5) { return new uint8array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); } else if (channelcount === 6) { 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]); } } else { // handle he-aac (mp4a.40.5 / mp4a.40.29) if (channelcount === 1) { // 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 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]); } else if (channelcount === 2) { // 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 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]); } else if (channelcount === 3) { // 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 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]); } } return null; } }]); return aac; }(); exports.default = aac; },{}],37:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * this file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js) * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ // mp4 boxes generator for iso bmff (iso base media file format, defined in iso/iec 14496-12) var mp4 = function () { function mp4() { _classcallcheck(this, mp4); } _createclass(mp4, null, [{ key: 'init', value: function init() { mp4.types = { avc1: [], avcc: [], btrt: [], dinf: [], dref: [], esds: [], ftyp: [], hdlr: [], mdat: [], mdhd: [], mdia: [], mfhd: [], minf: [], moof: [], moov: [], mp4a: [], mvex: [], mvhd: [], sdtp: [], stbl: [], stco: [], stsc: [], stsd: [], stsz: [], stts: [], tfdt: [], tfhd: [], traf: [], trak: [], trun: [], trex: [], tkhd: [], vmhd: [], smhd: [], '.mp3': [] }; for (var name in mp4.types) { if (mp4.types.hasownproperty(name)) { mp4.types[name] = [name.charcodeat(0), name.charcodeat(1), name.charcodeat(2), name.charcodeat(3)]; } } var constants = mp4.constants = {}; constants.ftyp = new uint8array([0x69, 0x73, 0x6f, 0x6d, // major_brand: isom 0x0, 0x0, 0x0, 0x1, // minor_version: 0x01 0x69, 0x73, 0x6f, 0x6d, // isom 0x61, 0x76, 0x63, 0x31 // avc1 ]); constants.stsd_prefix = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x01 // entry_count ]); constants.stts = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00 // entry_count ]); constants.stsc = constants.stco = constants.stts; constants.stsz = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // sample_size 0x00, 0x00, 0x00, 0x00 // sample_count ]); constants.hdlr_video = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: videohandler ]); constants.hdlr_audio = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun' 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: soundhandler ]); constants.dref = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x01, // entry_count 0x00, 0x00, 0x00, 0x0c, // entry_size 0x75, 0x72, 0x6c, 0x20, // type 'url ' 0x00, 0x00, 0x00, 0x01 // version(0) + flags ]); // sound media header constants.smhd = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2) ]); // video media header constants.vmhd = new uint8array([0x00, 0x00, 0x00, 0x01, // version(0) + flags 0x00, 0x00, // graphicsmode: 2 bytes 0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes 0x00, 0x00]); } // generate a box }, { key: 'box', value: function box(type) { var size = 8; var result = null; var datas = array.prototype.slice.call(arguments, 1); var arraycount = datas.length; for (var i = 0; i < arraycount; i++) { size += datas[i].bytelength; } result = new uint8array(size); result[0] = size >>> 24 & 0xff; // size result[1] = size >>> 16 & 0xff; result[2] = size >>> 8 & 0xff; result[3] = size & 0xff; result.set(type, 4); // type var offset = 8; for (var _i = 0; _i < arraycount; _i++) { // data body result.set(datas[_i], offset); offset += datas[_i].bytelength; } return result; } // emit ftyp & moov }, { key: 'generateinitsegment', value: function generateinitsegment(meta) { var ftyp = mp4.box(mp4.types.ftyp, mp4.constants.ftyp); var moov = mp4.moov(meta); var result = new uint8array(ftyp.bytelength + moov.bytelength); result.set(ftyp, 0); result.set(moov, ftyp.bytelength); return result; } // movie metadata box }, { key: 'moov', value: function moov(meta) { var mvhd = mp4.mvhd(meta.timescale, meta.duration); var trak = mp4.trak(meta); var mvex = mp4.mvex(meta); return mp4.box(mp4.types.moov, mvhd, trak, mvex); } // movie header box }, { key: 'mvhd', value: function mvhd(timescale, duration) { return mp4.box(mp4.types.mvhd, new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time timescale >>> 24 & 0xff, // timescale: 4 bytes timescale >>> 16 & 0xff, timescale >>> 8 & 0xff, timescale & 0xff, duration >>> 24 & 0xff, // duration: 4 bytes duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, 0x00, 0x01, 0x00, 0x00, // preferred rate: 1.0 0x01, 0x00, 0x00, 0x00, // preferredvolume(1.0, 2bytes) + reserved(2bytes) 0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 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---- 0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes---- 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---- 0xff, 0xff, 0xff, 0xff // next_track_id ])); } // track box }, { key: 'trak', value: function trak(meta) { return mp4.box(mp4.types.trak, mp4.tkhd(meta), mp4.mdia(meta)); } // track header box }, { key: 'tkhd', value: function tkhd(meta) { var trackid = meta.id, duration = meta.duration; var width = meta.presentwidth, height = meta.presentheight; return mp4.box(mp4.types.tkhd, new uint8array([0x00, 0x00, 0x00, 0x07, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time trackid >>> 24 & 0xff, // track_id: 4 bytes trackid >>> 16 & 0xff, trackid >>> 8 & 0xff, trackid & 0xff, 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes duration >>> 24 & 0xff, // duration: 4 bytes duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes) 0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes) 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 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---- width >>> 8 & 0xff, // width and height width & 0xff, 0x00, 0x00, height >>> 8 & 0xff, height & 0xff, 0x00, 0x00])); } // media box }, { key: 'mdia', value: function mdia(meta) { return mp4.box(mp4.types.mdia, mp4.mdhd(meta), mp4.hdlr(meta), mp4.minf(meta)); } // media header box }, { key: 'mdhd', value: function mdhd(meta) { var timescale = meta.timescale; var duration = meta.duration; return mp4.box(mp4.types.mdhd, new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time timescale >>> 24 & 0xff, // timescale: 4 bytes timescale >>> 16 & 0xff, timescale >>> 8 & 0xff, timescale & 0xff, duration >>> 24 & 0xff, // duration: 4 bytes duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, 0x55, 0xc4, // language: und (undetermined) 0x00, 0x00 // pre_defined = 0 ])); } // media handler reference box }, { key: 'hdlr', value: function hdlr(meta) { var data = null; if (meta.type === 'audio') { data = mp4.constants.hdlr_audio; } else { data = mp4.constants.hdlr_video; } return mp4.box(mp4.types.hdlr, data); } // media infomation box }, { key: 'minf', value: function minf(meta) { var xmhd = null; if (meta.type === 'audio') { xmhd = mp4.box(mp4.types.smhd, mp4.constants.smhd); } else { xmhd = mp4.box(mp4.types.vmhd, mp4.constants.vmhd); } return mp4.box(mp4.types.minf, xmhd, mp4.dinf(), mp4.stbl(meta)); } // data infomation box }, { key: 'dinf', value: function dinf() { var result = mp4.box(mp4.types.dinf, mp4.box(mp4.types.dref, mp4.constants.dref)); return result; } // sample table box }, { key: 'stbl', value: function stbl(meta) { var result = mp4.box(mp4.types.stbl, // type: stbl mp4.stsd(meta), // sample description table mp4.box(mp4.types.stts, mp4.constants.stts), // time-to-sample mp4.box(mp4.types.stsc, mp4.constants.stsc), // sample-to-chunk mp4.box(mp4.types.stsz, mp4.constants.stsz), // sample size mp4.box(mp4.types.stco, mp4.constants.stco // chunk offset )); return result; } // sample description box }, { key: 'stsd', value: function stsd(meta) { if (meta.type === 'audio') { if (meta.codec === 'mp3') { return mp4.box(mp4.types.stsd, mp4.constants.stsd_prefix, mp4.mp3(meta)); } // else: aac -> mp4a return mp4.box(mp4.types.stsd, mp4.constants.stsd_prefix, mp4.mp4a(meta)); } else { return mp4.box(mp4.types.stsd, mp4.constants.stsd_prefix, mp4.avc1(meta)); } } }, { key: 'mp3', value: function mp3(meta) { var channelcount = meta.channelcount; var samplerate = meta.audiosamplerate; var data = new uint8array([0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, channelcount, // channelcount(2) 0x00, 0x10, // samplesize(2) 0x00, 0x00, 0x00, 0x00, // reserved(4) samplerate >>> 8 & 0xff, // audio sample rate samplerate & 0xff, 0x00, 0x00]); return mp4.box(mp4.types['.mp3'], data); } }, { key: 'mp4a', value: function mp4a(meta) { var channelcount = meta.channelcount; var samplerate = meta.audiosamplerate; var data = new uint8array([0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, channelcount, // channelcount(2) 0x00, 0x10, // samplesize(2) 0x00, 0x00, 0x00, 0x00, // reserved(4) samplerate >>> 8 & 0xff, // audio sample rate samplerate & 0xff, 0x00, 0x00]); return mp4.box(mp4.types.mp4a, data, mp4.esds(meta)); } }, { key: 'esds', value: function esds(meta) { var config = meta.config || []; var configsize = config.length; var data = new uint8array([0x00, 0x00, 0x00, 0x00, // version 0 + flags 0x03, // descriptor_type 0x17 + configsize, // length3 0x00, 0x01, // es_id 0x00, // stream_priority 0x04, // descriptor_type 0x0f + configsize, // length 0x40, // codec: mpeg4_audio 0x15, // stream_type: audio 0x00, 0x00, 0x00, // buffer_size 0x00, 0x00, 0x00, 0x00, // maxbitrate 0x00, 0x00, 0x00, 0x00, // avgbitrate 0x05 // descriptor_type ].concat([configsize]).concat(config).concat([0x06, 0x01, 0x02 // gaspecificconfig ])); return mp4.box(mp4.types.esds, data); } }, { key: 'avc1', value: function avc1(meta) { var avcc = meta.avcc; var width = meta.codecwidth, height = meta.codecheight; var data = new uint8array([0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2) 0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, width >>> 8 & 0xff, // width: 2 bytes width & 0xff, height >>> 8 & 0xff, // height: 2 bytes height & 0xff, 0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes 0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes 0x00, 0x01, // frame_count 0x0a, // strlen 0x78, 0x71, 0x71, 0x2f, // compressorname: 32 bytes 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 0xff, 0xff // pre_defined = -1 ]); return mp4.box(mp4.types.avc1, data, mp4.box(mp4.types.avcc, avcc)); } // movie extends box }, { key: 'mvex', value: function mvex(meta) { return mp4.box(mp4.types.mvex, mp4.trex(meta)); } // track extends box }, { key: 'trex', value: function trex(meta) { var trackid = meta.id; var data = new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) + flags trackid >>> 24 & 0xff, // track_id trackid >>> 16 & 0xff, trackid >>> 8 & 0xff, trackid & 0xff, 0x00, 0x00, 0x00, 0x01, // default_sample_description_index 0x00, 0x00, 0x00, 0x00, // default_sample_duration 0x00, 0x00, 0x00, 0x00, // default_sample_size 0x00, 0x01, 0x00, 0x01 // default_sample_flags ]); return mp4.box(mp4.types.trex, data); } // movie fragment box }, { key: 'moof', value: function moof(track, basemediadecodetime) { return mp4.box(mp4.types.moof, mp4.mfhd(track.sequencenumber), mp4.traf(track, basemediadecodetime)); } }, { key: 'mfhd', value: function mfhd(sequencenumber) { var data = new uint8array([0x00, 0x00, 0x00, 0x00, sequencenumber >>> 24 & 0xff, // sequence_number: int32 sequencenumber >>> 16 & 0xff, sequencenumber >>> 8 & 0xff, sequencenumber & 0xff]); return mp4.box(mp4.types.mfhd, data); } // track fragment box }, { key: 'traf', value: function traf(track, basemediadecodetime) { var trackid = track.id; // track fragment header box var tfhd = mp4.box(mp4.types.tfhd, new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) & flags trackid >>> 24 & 0xff, // track_id trackid >>> 16 & 0xff, trackid >>> 8 & 0xff, trackid & 0xff])); // track fragment decode time var tfdt = mp4.box(mp4.types.tfdt, new uint8array([0x00, 0x00, 0x00, 0x00, // version(0) & flags basemediadecodetime >>> 24 & 0xff, // basemediadecodetime: int32 basemediadecodetime >>> 16 & 0xff, basemediadecodetime >>> 8 & 0xff, basemediadecodetime & 0xff])); var sdtp = mp4.sdtp(track); var trun = mp4.trun(track, sdtp.bytelength + 16 + 16 + 8 + 16 + 8 + 8); return mp4.box(mp4.types.traf, tfhd, tfdt, trun, sdtp); } // sample dependency type box }, { key: 'sdtp', value: function sdtp(track) { var samples = track.samples || []; var samplecount = samples.length; var data = new uint8array(4 + samplecount); // 0~4 bytes: version(0) & flags for (var i = 0; i < samplecount; i++) { var flags = samples[i].flags; data[i + 4] = flags.isleading << 6 | // is_leading: 2 (bit) flags.dependson << 4 // sample_depends_on | flags.isdependedon << 2 // sample_is_depended_on | flags.hasredundancy; // sample_has_redundancy } return mp4.box(mp4.types.sdtp, data); } // track fragment run box }, { key: 'trun', value: function trun(track, offset) { var samples = track.samples || []; var samplecount = samples.length; var datasize = 12 + 16 * samplecount; var data = new uint8array(datasize); offset += 8 + datasize; data.set([0x00, 0x00, 0x0f, 0x01, // version(0) & flags samplecount >>> 24 & 0xff, // sample_count samplecount >>> 16 & 0xff, samplecount >>> 8 & 0xff, samplecount & 0xff, offset >>> 24 & 0xff, // data_offset offset >>> 16 & 0xff, offset >>> 8 & 0xff, offset & 0xff], 0); for (var i = 0; i < samplecount; i++) { var duration = samples[i].duration; var size = samples[i].size; var flags = samples[i].flags; var cts = samples[i].cts; data.set([duration >>> 24 & 0xff, // sample_duration duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, size >>> 24 & 0xff, // sample_size size >>> 16 & 0xff, size >>> 8 & 0xff, size & 0xff, flags.isleading << 2 | flags.dependson, // sample_flags flags.isdependedon << 6 | flags.hasredundancy << 4 | flags.isnonsync, 0x00, 0x00, // sample_degradation_priority cts >>> 24 & 0xff, // sample_composition_time_offset cts >>> 16 & 0xff, cts >>> 8 & 0xff, cts & 0xff], 12 + 16 * i); } return mp4.box(mp4.types.trun, data); } }, { key: 'mdat', value: function mdat(data) { return mp4.box(mp4.types.mdat, data); } }]); return mp4; }(); mp4.init(); exports.default = mp4; },{}],38:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _logger = _dereq_('../utils/logger.js'); var _logger2 = _interoprequiredefault(_logger); var _mp4generator = _dereq_('./mp4-generator.js'); var _mp4generator2 = _interoprequiredefault(_mp4generator); var _aacsilent = _dereq_('./aac-silent.js'); var _aacsilent2 = _interoprequiredefault(_aacsilent); var _browser = _dereq_('../utils/browser.js'); var _browser2 = _interoprequiredefault(_browser); var _mediasegmentinfo = _dereq_('../core/media-segment-info.js'); var _exception = _dereq_('../utils/exception.js'); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } // fragmented mp4 remuxer var mp4remuxer = function () { function mp4remuxer(config) { _classcallcheck(this, mp4remuxer); this.tag = 'mp4remuxer'; this._config = config; this._islive = config.islive === true ? true : false; this._dtsbase = -1; this._dtsbaseinited = false; this._audiodtsbase = infinity; this._videodtsbase = infinity; this._audionextdts = undefined; this._videonextdts = undefined; this._audiostashedlastsample = null; this._videostashedlastsample = null; this._audiometa = null; this._videometa = null; this._audiosegmentinfolist = new _mediasegmentinfo.mediasegmentinfolist('audio'); this._videosegmentinfolist = new _mediasegmentinfo.mediasegmentinfolist('video'); this._oninitsegment = null; this._onmediasegment = null; // workaround for chrome < 50: always force first sample as a random access point in media segment // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 this._forcefirstidr = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661) ? true : false; // workaround for ie11/edge: fill silent aac frame after keyframe-seeking // make audio begindts equals with video begindts, in order to fix seek freeze this._fillsilentafterseek = _browser2.default.msedge || _browser2.default.msie; // while only firefox supports 'audio/mp4, codecs="mp3"', use 'audio/mpeg' for chrome, safari, ... this._mp3usempegaudio = !_browser2.default.firefox; this._fillaudiotimestampgap = this._config.fixaudiotimestampgap; } _createclass(mp4remuxer, [{ key: 'destroy', value: function destroy() { this._dtsbase = -1; this._dtsbaseinited = false; this._audiometa = null; this._videometa = null; this._audiosegmentinfolist.clear(); this._audiosegmentinfolist = null; this._videosegmentinfolist.clear(); this._videosegmentinfolist = null; this._oninitsegment = null; this._onmediasegment = null; } }, { key: 'binddatasource', value: function binddatasource(producer) { producer.ondataavailable = this.remux.bind(this); producer.ontrackmetadata = this._ontrackmetadatareceived.bind(this); return this; } /* prototype: function oninitsegment(type: string, initsegment: arraybuffer): void initsegment: { type: string, data: arraybuffer, codec: string, container: string } */ }, { key: 'insertdiscontinuity', value: function insertdiscontinuity() { this._audionextdts = this._videonextdts = undefined; } }, { key: 'seek', value: function seek(originaldts) { this._audiostashedlastsample = null; this._videostashedlastsample = null; this._videosegmentinfolist.clear(); this._audiosegmentinfolist.clear(); } }, { key: 'remux', value: function remux(audiotrack, videotrack) { if (!this._onmediasegment) { throw new _exception.illegalstateexception('mp4remuxer: onmediasegment callback must be specificed!'); } if (!this._dtsbaseinited) { this._calculatedtsbase(audiotrack, videotrack); } this._remuxvideo(videotrack); this._remuxaudio(audiotrack); } }, { key: '_ontrackmetadatareceived', value: function _ontrackmetadatareceived(type, metadata) { var metabox = null; var container = 'mp4'; var codec = metadata.codec; if (type === 'audio') { this._audiometa = metadata; if (metadata.codec === 'mp3' && this._mp3usempegaudio) { // 'audio/mpeg' for mp3 audio track container = 'mpeg'; codec = ''; metabox = new uint8array(); } else { // 'audio/mp4, codecs="codec"' metabox = _mp4generator2.default.generateinitsegment(metadata); } } else if (type === 'video') { this._videometa = metadata; metabox = _mp4generator2.default.generateinitsegment(metadata); } else { return; } // dispatch metabox (initialization segment) if (!this._oninitsegment) { throw new _exception.illegalstateexception('mp4remuxer: oninitsegment callback must be specified!'); } this._oninitsegment(type, { type: type, data: metabox.buffer, codec: codec, container: type + '/' + container, mediaduration: metadata.duration // in timescale 1000 (milliseconds) }); } }, { key: '_calculatedtsbase', value: function _calculatedtsbase(audiotrack, videotrack) { if (this._dtsbaseinited) { return; } if (audiotrack.samples && audiotrack.samples.length) { this._audiodtsbase = audiotrack.samples[0].dts; } if (videotrack.samples && videotrack.samples.length) { this._videodtsbase = videotrack.samples[0].dts; } this._dtsbase = math.min(this._audiodtsbase, this._videodtsbase); this._dtsbaseinited = true; } }, { key: 'flushstashedsamples', value: function flushstashedsamples() { var videosample = this._videostashedlastsample; var audiosample = this._audiostashedlastsample; var videotrack = { type: 'video', id: 1, sequencenumber: 0, samples: [], length: 0 }; if (videosample != null) { videotrack.samples.push(videosample); videotrack.length = videosample.length; } var audiotrack = { type: 'audio', id: 2, sequencenumber: 0, samples: [], length: 0 }; if (audiosample != null) { audiotrack.samples.push(audiosample); audiotrack.length = audiosample.length; } this._videostashedlastsample = null; this._audiostashedlastsample = null; this._remuxvideo(videotrack, true); this._remuxaudio(audiotrack, true); } }, { key: '_remuxaudio', value: function _remuxaudio(audiotrack, force) { if (this._audiometa == null) { return; } var track = audiotrack; var samples = track.samples; var dtscorrection = undefined; var firstdts = -1, lastdts = -1, lastpts = -1; var refsampleduration = this._audiometa.refsampleduration; var mpegrawtrack = this._audiometa.codec === 'mp3' && this._mp3usempegaudio; var firstsegmentafterseek = this._dtsbaseinited && this._audionextdts === undefined; var insertprefixsilentframe = false; if (!samples || samples.length === 0) { return; } if (samples.length === 1 && !force) { // if [sample count in current batch] === 1 && (force != true) // ignore and keep in demuxer's queue return; } // else if (force === true) do remux var offset = 0; var mdatbox = null; var mdatbytes = 0; // calculate initial mdat size if (mpegrawtrack) { // for raw mpeg buffer offset = 0; mdatbytes = track.length; } else { // for fmp4 mdat box offset = 8; // size + type mdatbytes = 8 + track.length; } var lastsample = null; // pop the lastsample and waiting for stash if (samples.length > 1) { lastsample = samples.pop(); mdatbytes -= lastsample.length; } // insert [stashed lastsample in the previous batch] to the front if (this._audiostashedlastsample != null) { var sample = this._audiostashedlastsample; this._audiostashedlastsample = null; samples.unshift(sample); mdatbytes += sample.length; } // stash the lastsample of current batch, waiting for next batch if (lastsample != null) { this._audiostashedlastsample = lastsample; } var firstsampleoriginaldts = samples[0].dts - this._dtsbase; // calculate dtscorrection if (this._audionextdts) { dtscorrection = firstsampleoriginaldts - this._audionextdts; } else { // this._audionextdts == undefined if (this._audiosegmentinfolist.isempty()) { dtscorrection = 0; if (this._fillsilentafterseek && !this._videosegmentinfolist.isempty()) { if (this._audiometa.originalcodec !== 'mp3') { insertprefixsilentframe = true; } } } else { var _lastsample = this._audiosegmentinfolist.getlastsamplebefore(firstsampleoriginaldts); if (_lastsample != null) { var distance = firstsampleoriginaldts - (_lastsample.originaldts + _lastsample.duration); if (distance <= 3) { distance = 0; } var expecteddts = _lastsample.dts + _lastsample.duration + distance; dtscorrection = firstsampleoriginaldts - expecteddts; } else { // lastsample == null, cannot found dtscorrection = 0; } } } if (insertprefixsilentframe) { // align audio segment begindts to match with current video segment's begindts var firstsampledts = firstsampleoriginaldts - dtscorrection; var videosegment = this._videosegmentinfolist.getlastsegmentbefore(firstsampleoriginaldts); if (videosegment != null && videosegment.begindts < firstsampledts) { var silentunit = _aacsilent2.default.getsilentframe(this._audiometa.originalcodec, this._audiometa.channelcount); if (silentunit) { var dts = videosegment.begindts; var silentframeduration = firstsampledts - videosegment.begindts; _logger2.default.v(this.tag, 'insertprefixsilentaudio: dts: ' + dts + ', duration: ' + silentframeduration); samples.unshift({ unit: silentunit, dts: dts, pts: dts }); mdatbytes += silentunit.bytelength; } // silentunit == null: cannot generate, skip } else { insertprefixsilentframe = false; } } var mp4samples = []; // correct dts for each sample, and calculate sample duration. then output to mp4samples for (var i = 0; i < samples.length; i++) { var _sample = samples[i]; var unit = _sample.unit; var originaldts = _sample.dts - this._dtsbase; var _dts = originaldts - dtscorrection; if (firstdts === -1) { firstdts = _dts; } var sampleduration = 0; if (i !== samples.length - 1) { var nextdts = samples[i + 1].dts - this._dtsbase - dtscorrection; sampleduration = nextdts - _dts; } else { // the last sample if (lastsample != null) { // use stashed sample's dts to calculate sample duration var _nextdts = lastsample.dts - this._dtsbase - dtscorrection; sampleduration = _nextdts - _dts; } else if (mp4samples.length >= 1) { // use second last sample duration sampleduration = mp4samples[mp4samples.length - 1].duration; } else { // the only one sample, use reference sample duration sampleduration = math.floor(refsampleduration); } } var needfillsilentframes = false; var silentframes = null; // silent frame generation, if large timestamp gap detected && config.fixaudiotimestampgap if (sampleduration > refsampleduration * 1.5 && this._audiometa.codec !== 'mp3' && this._fillaudiotimestampgap && !_browser2.default.safari) { // we need to insert silent frames to fill timestamp gap needfillsilentframes = true; var delta = math.abs(sampleduration - refsampleduration); var framecount = math.ceil(delta / refsampleduration); var currentdts = _dts + refsampleduration; // notice: in float _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')); var _silentunit = _aacsilent2.default.getsilentframe(this._audiometa.originalcodec, this._audiometa.channelcount); if (_silentunit == null) { _logger2.default.w(this.tag, 'unable to generate silent frame for ' + (this._audiometa.originalcodec + ' with ' + this._audiometa.channelcount + ' channels, repeat last frame')); // repeat last frame _silentunit = unit; } silentframes = []; for (var j = 0; j < framecount; j++) { var intdts = math.round(currentdts); // round to integer if (silentframes.length > 0) { // set previous frame sample duration var previousframe = silentframes[silentframes.length - 1]; previousframe.duration = intdts - previousframe.dts; } var frame = { dts: intdts, pts: intdts, cts: 0, unit: _silentunit, size: _silentunit.bytelength, duration: 0, // wait for next sample originaldts: originaldts, flags: { isleading: 0, dependson: 1, isdependedon: 0, hasredundancy: 0 } }; silentframes.push(frame); mdatbytes += unit.bytelength; currentdts += refsampleduration; } // last frame: align end time to next frame dts var lastframe = silentframes[silentframes.length - 1]; lastframe.duration = _dts + sampleduration - lastframe.dts; // silentframes.foreach((frame) => { // log.w(this.tag, `silentaudio: dts: ${frame.dts}, duration: ${frame.duration}`); // }); // set correct sample duration for current frame sampleduration = math.round(refsampleduration); } mp4samples.push({ dts: _dts, pts: _dts, cts: 0, unit: _sample.unit, size: _sample.unit.bytelength, duration: sampleduration, originaldts: originaldts, flags: { isleading: 0, dependson: 1, isdependedon: 0, hasredundancy: 0 } }); if (needfillsilentframes) { // silent frames should be inserted after wrong-duration frame mp4samples.push.apply(mp4samples, silentframes); } } // allocate mdatbox if (mpegrawtrack) { // allocate for raw mpeg buffer mdatbox = new uint8array(mdatbytes); } else { // allocate for fmp4 mdat box mdatbox = new uint8array(mdatbytes); // size field mdatbox[0] = mdatbytes >>> 24 & 0xff; mdatbox[1] = mdatbytes >>> 16 & 0xff; mdatbox[2] = mdatbytes >>> 8 & 0xff; mdatbox[3] = mdatbytes & 0xff; // type field (fourcc) mdatbox.set(_mp4generator2.default.types.mdat, 4); } // write samples into mdatbox for (var _i = 0; _i < mp4samples.length; _i++) { var _unit = mp4samples[_i].unit; mdatbox.set(_unit, offset); offset += _unit.bytelength; } var latest = mp4samples[mp4samples.length - 1]; lastdts = latest.dts + latest.duration; this._audionextdts = lastdts; // fill media segment info & add to info list var info = new _mediasegmentinfo.mediasegmentinfo(); info.begindts = firstdts; info.enddts = lastdts; info.beginpts = firstdts; info.endpts = lastdts; info.originalbegindts = mp4samples[0].originaldts; info.originalenddts = latest.originaldts + latest.duration; info.firstsample = new _mediasegmentinfo.sampleinfo(mp4samples[0].dts, mp4samples[0].pts, mp4samples[0].duration, mp4samples[0].originaldts, false); info.lastsample = new _mediasegmentinfo.sampleinfo(latest.dts, latest.pts, latest.duration, latest.originaldts, false); if (!this._islive) { this._audiosegmentinfolist.append(info); } track.samples = mp4samples; track.sequencenumber++; var moofbox = null; if (mpegrawtrack) { // generate empty buffer, because useless for raw mpeg moofbox = new uint8array(); } else { // generate moof for fmp4 segment moofbox = _mp4generator2.default.moof(track, firstdts); } track.samples = []; track.length = 0; var segment = { type: 'audio', data: this._mergeboxes(moofbox, mdatbox).buffer, samplecount: mp4samples.length, info: info }; if (mpegrawtrack && firstsegmentafterseek) { // for mpeg audio stream in mse, if seeking occurred, before appending new buffer // we need explicitly set timestampoffset to the desired point in timeline for mpeg sourcebuffer. segment.timestampoffset = firstdts; } this._onmediasegment('audio', segment); } }, { key: '_remuxvideo', value: function _remuxvideo(videotrack, force) { if (this._videometa == null) { return; } var track = videotrack; var samples = track.samples; var dtscorrection = undefined; var firstdts = -1, lastdts = -1; var firstpts = -1, lastpts = -1; if (!samples || samples.length === 0) { return; } if (samples.length === 1 && !force) { // if [sample count in current batch] === 1 && (force != true) // ignore and keep in demuxer's queue return; } // else if (force === true) do remux var offset = 8; var mdatbox = null; var mdatbytes = 8 + videotrack.length; var lastsample = null; // pop the lastsample and waiting for stash if (samples.length > 1) { lastsample = samples.pop(); mdatbytes -= lastsample.length; } // insert [stashed lastsample in the previous batch] to the front if (this._videostashedlastsample != null) { var sample = this._videostashedlastsample; this._videostashedlastsample = null; samples.unshift(sample); mdatbytes += sample.length; } // stash the lastsample of current batch, waiting for next batch if (lastsample != null) { this._videostashedlastsample = lastsample; } var firstsampleoriginaldts = samples[0].dts - this._dtsbase; // calculate dtscorrection if (this._videonextdts) { dtscorrection = firstsampleoriginaldts - this._videonextdts; } else { // this._videonextdts == undefined if (this._videosegmentinfolist.isempty()) { dtscorrection = 0; } else { var _lastsample2 = this._videosegmentinfolist.getlastsamplebefore(firstsampleoriginaldts); if (_lastsample2 != null) { var distance = firstsampleoriginaldts - (_lastsample2.originaldts + _lastsample2.duration); if (distance <= 3) { distance = 0; } var expecteddts = _lastsample2.dts + _lastsample2.duration + distance; dtscorrection = firstsampleoriginaldts - expecteddts; } else { // lastsample == null, cannot found dtscorrection = 0; } } } var info = new _mediasegmentinfo.mediasegmentinfo(); var mp4samples = []; // correct dts for each sample, and calculate sample duration. then output to mp4samples for (var i = 0; i < samples.length; i++) { var _sample2 = samples[i]; var originaldts = _sample2.dts - this._dtsbase; var iskeyframe = _sample2.iskeyframe; var dts = originaldts - dtscorrection; var cts = _sample2.cts; var pts = dts + cts; if (firstdts === -1) { firstdts = dts; firstpts = pts; } var sampleduration = 0; if (i !== samples.length - 1) { var nextdts = samples[i + 1].dts - this._dtsbase - dtscorrection; sampleduration = nextdts - dts; } else { // the last sample if (lastsample != null) { // use stashed sample's dts to calculate sample duration var _nextdts2 = lastsample.dts - this._dtsbase - dtscorrection; sampleduration = _nextdts2 - dts; } else if (mp4samples.length >= 1) { // use second last sample duration sampleduration = mp4samples[mp4samples.length - 1].duration; } else { // the only one sample, use reference sample duration sampleduration = math.floor(this._videometa.refsampleduration); } } if (iskeyframe) { var syncpoint = new _mediasegmentinfo.sampleinfo(dts, pts, sampleduration, _sample2.dts, true); syncpoint.fileposition = _sample2.fileposition; info.appendsyncpoint(syncpoint); } mp4samples.push({ dts: dts, pts: pts, cts: cts, units: _sample2.units, size: _sample2.length, iskeyframe: iskeyframe, duration: sampleduration, originaldts: originaldts, flags: { isleading: 0, dependson: iskeyframe ? 2 : 1, isdependedon: iskeyframe ? 1 : 0, hasredundancy: 0, isnonsync: iskeyframe ? 0 : 1 } }); } // allocate mdatbox mdatbox = new uint8array(mdatbytes); mdatbox[0] = mdatbytes >>> 24 & 0xff; mdatbox[1] = mdatbytes >>> 16 & 0xff; mdatbox[2] = mdatbytes >>> 8 & 0xff; mdatbox[3] = mdatbytes & 0xff; mdatbox.set(_mp4generator2.default.types.mdat, 4); // write samples into mdatbox for (var _i2 = 0; _i2 < mp4samples.length; _i2++) { var units = mp4samples[_i2].units; while (units.length) { var unit = units.shift(); var data = unit.data; mdatbox.set(data, offset); offset += data.bytelength; } } var latest = mp4samples[mp4samples.length - 1]; lastdts = latest.dts + latest.duration; lastpts = latest.pts + latest.duration; this._videonextdts = lastdts; // fill media segment info & add to info list info.begindts = firstdts; info.enddts = lastdts; info.beginpts = firstpts; info.endpts = lastpts; info.originalbegindts = mp4samples[0].originaldts; info.originalenddts = latest.originaldts + latest.duration; info.firstsample = new _mediasegmentinfo.sampleinfo(mp4samples[0].dts, mp4samples[0].pts, mp4samples[0].duration, mp4samples[0].originaldts, mp4samples[0].iskeyframe); info.lastsample = new _mediasegmentinfo.sampleinfo(latest.dts, latest.pts, latest.duration, latest.originaldts, latest.iskeyframe); if (!this._islive) { this._videosegmentinfolist.append(info); } track.samples = mp4samples; track.sequencenumber++; // workaround for chrome < 50: force first sample as a random access point // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 if (this._forcefirstidr) { var flags = mp4samples[0].flags; flags.dependson = 2; flags.isnonsync = 0; } var moofbox = _mp4generator2.default.moof(track, firstdts); track.samples = []; track.length = 0; this._onmediasegment('video', { type: 'video', data: this._mergeboxes(moofbox, mdatbox).buffer, samplecount: mp4samples.length, info: info }); } }, { key: '_mergeboxes', value: function _mergeboxes(moof, mdat) { var result = new uint8array(moof.bytelength + mdat.bytelength); result.set(moof, 0); result.set(mdat, moof.bytelength); return result; } }, { key: 'oninitsegment', get: function get() { return this._oninitsegment; }, set: function set(callback) { this._oninitsegment = callback; } /* prototype: function onmediasegment(type: string, mediasegment: mediasegment): void mediasegment: { type: string, data: arraybuffer, samplecount: int32 info: mediasegmentinfo } */ }, { key: 'onmediasegment', get: function get() { return this._onmediasegment; }, set: function set(callback) { this._onmediasegment = callback; } }]); return mp4remuxer; }(); exports.default = mp4remuxer; },{"../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){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var browser = {}; function detect() { // modified from jquery-browser-plugin var ua = self.navigator.useragent.tolowercase(); 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) || []; 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) || []; var matched = { browser: match[5] || match[3] || match[1] || '', version: match[2] || match[4] || '0', majorversion: match[4] || match[2] || '0', platform: platform_match[0] || '' }; var browser = {}; if (matched.browser) { browser[matched.browser] = true; var versionarray = matched.majorversion.split('.'); browser.version = { major: parseint(matched.majorversion, 10), string: matched.version }; if (versionarray.length > 1) { browser.version.minor = parseint(versionarray[1], 10); } if (versionarray.length > 2) { browser.version.build = parseint(versionarray[2], 10); } } if (matched.platform) { browser[matched.platform] = true; } if (browser.chrome || browser.opr || browser.safari) { browser.webkit = true; } // msie. ie11 has 'rv' identifer if (browser.rv || browser.iemobile) { if (browser.rv) { delete browser.rv; } var msie = 'msie'; matched.browser = msie; browser[msie] = true; } // microsoft edge if (browser.edge) { delete browser.edge; var msedge = 'msedge'; matched.browser = msedge; browser[msedge] = true; } // opera 15+ if (browser.opr) { var opera = 'opera'; matched.browser = opera; browser[opera] = true; } // stock android browsers are marked as safari if (browser.safari && browser.android) { var android = 'android'; matched.browser = android; browser[android] = true; } browser.name = matched.browser; browser.platform = matched.platform; for (var key in browser) { if (browser.hasownproperty(key)) { delete browser[key]; } } object.assign(browser, browser); } detect(); exports.default = browser; },{}],40:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); 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; } 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; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var runtimeexception = exports.runtimeexception = function () { function runtimeexception(message) { _classcallcheck(this, runtimeexception); this._message = message; } _createclass(runtimeexception, [{ key: 'tostring', value: function tostring() { return this.name + ': ' + this.message; } }, { key: 'name', get: function get() { return 'runtimeexception'; } }, { key: 'message', get: function get() { return this._message; } }]); return runtimeexception; }(); var illegalstateexception = exports.illegalstateexception = function (_runtimeexception) { _inherits(illegalstateexception, _runtimeexception); function illegalstateexception(message) { _classcallcheck(this, illegalstateexception); return _possibleconstructorreturn(this, (illegalstateexception.__proto__ || object.getprototypeof(illegalstateexception)).call(this, message)); } _createclass(illegalstateexception, [{ key: 'name', get: function get() { return 'illegalstateexception'; } }]); return illegalstateexception; }(runtimeexception); var invalidargumentexception = exports.invalidargumentexception = function (_runtimeexception2) { _inherits(invalidargumentexception, _runtimeexception2); function invalidargumentexception(message) { _classcallcheck(this, invalidargumentexception); return _possibleconstructorreturn(this, (invalidargumentexception.__proto__ || object.getprototypeof(invalidargumentexception)).call(this, message)); } _createclass(invalidargumentexception, [{ key: 'name', get: function get() { return 'invalidargumentexception'; } }]); return invalidargumentexception; }(runtimeexception); var notimplementedexception = exports.notimplementedexception = function (_runtimeexception3) { _inherits(notimplementedexception, _runtimeexception3); function notimplementedexception(message) { _classcallcheck(this, notimplementedexception); return _possibleconstructorreturn(this, (notimplementedexception.__proto__ || object.getprototypeof(notimplementedexception)).call(this, message)); } _createclass(notimplementedexception, [{ key: 'name', get: function get() { return 'notimplementedexception'; } }]); return notimplementedexception; }(runtimeexception); },{}],41:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var log = function () { function log() { _classcallcheck(this, log); } _createclass(log, null, [{ key: 'e', value: function e(tag, msg) { if (!tag || log.force_global_tag) tag = log.global_tag; var str = '[' + tag + '] > ' + msg; if (log.enable_callback) { log.emitter.emit('log', 'error', str); } if (!log.enable_error) { return; } if (console.error) { console.error(str); } else if (console.warn) { console.warn(str); } else { console.log(str); } } }, { key: 'i', value: function i(tag, msg) { if (!tag || log.force_global_tag) tag = log.global_tag; var str = '[' + tag + '] > ' + msg; if (log.enable_callback) { log.emitter.emit('log', 'info', str); } if (!log.enable_info) { return; } if (console.info) { console.info(str); } else { console.log(str); } } }, { key: 'w', value: function w(tag, msg) { if (!tag || log.force_global_tag) tag = log.global_tag; var str = '[' + tag + '] > ' + msg; if (log.enable_callback) { log.emitter.emit('log', 'warn', str); } if (!log.enable_warn) { return; } if (console.warn) { console.warn(str); } else { console.log(str); } } }, { key: 'd', value: function d(tag, msg) { if (!tag || log.force_global_tag) tag = log.global_tag; var str = '[' + tag + '] > ' + msg; if (log.enable_callback) { log.emitter.emit('log', 'debug', str); } if (!log.enable_debug) { return; } if (console.debug) { console.debug(str); } else { console.log(str); } } }, { key: 'v', value: function v(tag, msg) { if (!tag || log.force_global_tag) tag = log.global_tag; var str = '[' + tag + '] > ' + msg; if (log.enable_callback) { log.emitter.emit('log', 'verbose', str); } if (!log.enable_verbose) { return; } console.log(str); } }]); return log; }(); log.global_tag = 'flv.js'; log.force_global_tag = false; log.enable_error = true; log.enable_info = true; log.enable_warn = true; log.enable_debug = true; log.enable_verbose = true; log.enable_callback = false; log.emitter = new _events2.default(); exports.default = log; },{"events":2}],42:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var _events = _dereq_('events'); var _events2 = _interoprequiredefault(_events); var _logger = _dereq_('./logger.js'); var _logger2 = _interoprequiredefault(_logger); function _interoprequiredefault(obj) { return obj && obj.__esmodule ? obj : { default: obj }; } function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } var loggingcontrol = function () { function loggingcontrol() { _classcallcheck(this, loggingcontrol); } _createclass(loggingcontrol, null, [{ key: 'getconfig', value: function getconfig() { return { globaltag: _logger2.default.global_tag, forceglobaltag: _logger2.default.force_global_tag, enableverbose: _logger2.default.enable_verbose, enabledebug: _logger2.default.enable_debug, enableinfo: _logger2.default.enable_info, enablewarn: _logger2.default.enable_warn, enableerror: _logger2.default.enable_error, enablecallback: _logger2.default.enable_callback }; } }, { key: 'applyconfig', value: function applyconfig(config) { _logger2.default.global_tag = config.globaltag; _logger2.default.force_global_tag = config.forceglobaltag; _logger2.default.enable_verbose = config.enableverbose; _logger2.default.enable_debug = config.enabledebug; _logger2.default.enable_info = config.enableinfo; _logger2.default.enable_warn = config.enablewarn; _logger2.default.enable_error = config.enableerror; _logger2.default.enable_callback = config.enablecallback; } }, { key: '_notifychange', value: function _notifychange() { var emitter = loggingcontrol.emitter; if (emitter.listenercount('change') > 0) { var config = loggingcontrol.getconfig(); emitter.emit('change', config); } } }, { key: 'registerlistener', value: function registerlistener(listener) { loggingcontrol.emitter.addlistener('change', listener); } }, { key: 'removelistener', value: function removelistener(listener) { loggingcontrol.emitter.removelistener('change', listener); } }, { key: 'addloglistener', value: function addloglistener(listener) { _logger2.default.emitter.addlistener('log', listener); if (_logger2.default.emitter.listenercount('log') > 0) { _logger2.default.enable_callback = true; loggingcontrol._notifychange(); } } }, { key: 'removeloglistener', value: function removeloglistener(listener) { _logger2.default.emitter.removelistener('log', listener); if (_logger2.default.emitter.listenercount('log') === 0) { _logger2.default.enable_callback = false; loggingcontrol._notifychange(); } } }, { key: 'forceglobaltag', get: function get() { return _logger2.default.force_global_tag; }, set: function set(enable) { _logger2.default.force_global_tag = enable; loggingcontrol._notifychange(); } }, { key: 'globaltag', get: function get() { return _logger2.default.global_tag; }, set: function set(tag) { _logger2.default.global_tag = tag; loggingcontrol._notifychange(); } }, { key: 'enableall', get: function get() { return _logger2.default.enable_verbose && _logger2.default.enable_debug && _logger2.default.enable_info && _logger2.default.enable_warn && _logger2.default.enable_error; }, set: function set(enable) { _logger2.default.enable_verbose = enable; _logger2.default.enable_debug = enable; _logger2.default.enable_info = enable; _logger2.default.enable_warn = enable; _logger2.default.enable_error = enable; loggingcontrol._notifychange(); } }, { key: 'enabledebug', get: function get() { return _logger2.default.enable_debug; }, set: function set(enable) { _logger2.default.enable_debug = enable; loggingcontrol._notifychange(); } }, { key: 'enableverbose', get: function get() { return _logger2.default.enable_verbose; }, set: function set(enable) { _logger2.default.enable_verbose = enable; loggingcontrol._notifychange(); } }, { key: 'enableinfo', get: function get() { return _logger2.default.enable_info; }, set: function set(enable) { _logger2.default.enable_info = enable; loggingcontrol._notifychange(); } }, { key: 'enablewarn', get: function get() { return _logger2.default.enable_warn; }, set: function set(enable) { _logger2.default.enable_warn = enable; loggingcontrol._notifychange(); } }, { key: 'enableerror', get: function get() { return _logger2.default.enable_error; }, set: function set(enable) { _logger2.default.enable_error = enable; loggingcontrol._notifychange(); } }]); return loggingcontrol; }(); loggingcontrol.emitter = new _events2.default(); exports.default = loggingcontrol; },{"./logger.js":41,"events":2}],43:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); 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; }; }(); function _classcallcheck(instance, constructor) { if (!(instance instanceof constructor)) { throw new typeerror("cannot call a class as a function"); } } /* * copyright (c) 2016 bilibili. all rights reserved. * * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ var polyfill = function () { function polyfill() { _classcallcheck(this, polyfill); } _createclass(polyfill, null, [{ key: 'install', value: function install() { // es6 object.setprototypeof object.setprototypeof = object.setprototypeof || function (obj, proto) { obj.__proto__ = proto; return obj; }; // es6 object.assign object.assign = object.assign || function (target) { if (target === undefined || target === null) { throw new typeerror('cannot convert undefined or null to object'); } var output = object(target); for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; if (source !== undefined && source !== null) { for (var key in source) { if (source.hasownproperty(key)) { output[key] = source[key]; } } } } return output; }; // es6 promise (missing support in ie11) if (typeof self.promise !== 'function') { _dereq_('es6-promise').polyfill(); } } }]); return polyfill; }(); polyfill.install(); exports.default = polyfill; },{"es6-promise":1}],44:[function(_dereq_,module,exports){ 'use strict'; object.defineproperty(exports, "__esmodule", { value: true }); /* * copyright (c) 2016 bilibili. all rights reserved. * * this file is derived from c++ project libwintf8 (https://github.com/m13253/libwintf8) * @author zheng qian * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ function checkcontinuation(uint8array, start, checklength) { var array = uint8array; if (start + checklength < array.length) { while (checklength--) { if ((array[++start] & 0xc0) !== 0x80) return false; } return true; } else { return false; } } function decodeutf8(uint8array) { var out = []; var input = uint8array; var i = 0; var length = uint8array.length; while (i < length) { if (input[i] < 0x80) { out.push(string.fromcharcode(input[i])); ++i; continue; } else if (input[i] < 0xc0) { // fallthrough } else if (input[i] < 0xe0) { if (checkcontinuation(input, i, 1)) { var ucs4 = (input[i] & 0x1f) << 6 | input[i + 1] & 0x3f; if (ucs4 >= 0x80) { out.push(string.fromcharcode(ucs4 & 0xffff)); i += 2; continue; } } } else if (input[i] < 0xf0) { if (checkcontinuation(input, i, 2)) { var _ucs = (input[i] & 0xf) << 12 | (input[i + 1] & 0x3f) << 6 | input[i + 2] & 0x3f; if (_ucs >= 0x800 && (_ucs & 0xf800) !== 0xd800) { out.push(string.fromcharcode(_ucs & 0xffff)); i += 3; continue; } } } else if (input[i] < 0xf8) { if (checkcontinuation(input, i, 3)) { var _ucs2 = (input[i] & 0x7) << 18 | (input[i + 1] & 0x3f) << 12 | (input[i + 2] & 0x3f) << 6 | input[i + 3] & 0x3f; if (_ucs2 > 0x10000 && _ucs2 < 0x110000) { _ucs2 -= 0x10000; out.push(string.fromcharcode(_ucs2 >>> 10 | 0xd800)); out.push(string.fromcharcode(_ucs2 & 0x3ff | 0xdc00)); i += 4; continue; } } } out.push(string.fromcharcode(0xfffd)); ++i; } return out.join(''); } exports.default = decodeutf8; },{}]},{},[21])(21) }); //# sourcemappingurl=flv.js.map