From de0d358dd8611a1dfd6fb3c3e2928511560fc12c Mon Sep 17 00:00:00 2001 From: Zorglube <630192+zorglube@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:14:29 +0100 Subject: [PATCH] Update Flv.js player and Video.js configuration --- static/js/flv.min.js | 16 ++++++++++------ static/js/flv.min.js.map | 1 + static/js/video.js | 5 ++++- 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 static/js/flv.min.js.map diff --git a/static/js/flv.min.js b/static/js/flv.min.js index f52f966..1c8e3d6 100644 --- a/static/js/flv.min.js +++ b/static/js/flv.min.js @@ -1,6 +1,10 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.flvjs=e()}}(function(){var e;return function e(t,n,i){function r(a,o){if(!n[a]){if(!t[a]){var u="function"==typeof require&&require;if(!o&&u)return u(a,!0);if(s)return s(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var d=n[a]={exports:{}};t[a][0].call(d.exports,function(e){var n=t[a][1][e];return r(n||e)},d,d.exports,e,t,n,i)}return n[a].exports}for(var s="function"==typeof require&&require,a=0;a0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},i.prototype.on=i.prototype.addListener,i.prototype.once=function(e,t){function n(){this.removeListener(e,n),i||(i=!0,t.apply(this,arguments))}if(!r(t))throw TypeError("listener must be a function");var i=!1;return n.listener=t,this.on(e,n),this},i.prototype.removeListener=function(e,t){var n,i,s,o;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],s=n.length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(a(n)){for(o=s;o-- >0;)if(n[o]===t||n[o].listener&&n[o].listener===t){i=o;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},i.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],r(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},i.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},i.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},i.listenerCount=function(e,t){return e.listenerCount(t)}},{}],3:[function(e,t,n){function i(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function s(e){if(h===setTimeout)return setTimeout(e,0);if((h===i||!h)&&setTimeout)return h=setTimeout,setTimeout(e,0);try{return h(e,0)}catch(t){try{return h.call(null,e,0)}catch(t){return h.call(this,e,0)}}}function a(e){if(f===clearTimeout)return clearTimeout(e);if((f===r||!f)&&clearTimeout)return f=clearTimeout,clearTimeout(e);try{return f(e)}catch(t){try{return f.call(null,e)}catch(t){return f.call(this,e)}}}function o(){p&&_&&(p=!1,_.length?m=_.concat(m):v=-1,m.length&&u())}function u(){if(!p){var e=s(o);p=!0;for(var t=m.length;t;){for(_=m,m=[];++v1)for(var n=1;n=e[r]&&t0&&e[0].originalDts=t[r].dts&&et[i].lastSample.originalDts&&e=t[i].lastSample.originalDts&&(i===t.length-1||i0&&(r=this._searchNearestSegmentBefore(n.originalBeginDts)+1),this._lastAppendLocation=r,this._list.splice(r,0,n)}},{key:"getLastSegmentBefore",value:function(e){var t=this._searchNearestSegmentBefore(e);return t>=0?this._list[t]:null}},{key:"getLastSampleBefore",value:function(e){var t=this.getLastSegmentBefore(e);return null!=t?t.lastSample:null}},{key:"getLastSyncPointBefore",value:function(e){for(var t=this._searchNearestSegmentBefore(e),n=this._list[t].syncPoints;0===n.length&&t>0;)t--,n=this._list[t].syncPoints;return n.length>0?n[n.length-1]:null}},{key:"type",get:function(){return this._type}},{key:"length",get:function(){return this._list.length}}]),e}()},{}],9:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0&&(i+=";codecs="+n.codec);var r=!1;if(l.default.v(this.TAG,"Received Initialization Segment, mimeType: "+i),this._lastInitSegments[n.type]=n,i!==this._mimeTypes[n.type]){if(this._mimeTypes[n.type])l.default.v(this.TAG,"Notice: "+n.type+" mimeType changed, origin: "+this._mimeTypes[n.type]+", target: "+i);else{r=!0;try{var s=this._sourceBuffers[n.type]=this._mediaSource.addSourceBuffer(i);s.addEventListener("error",this.e.onSourceBufferError),s.addEventListener("updateend",this.e.onSourceBufferUpdateEnd)}catch(e){return l.default.e(this.TAG,e.message),void this._emitter.emit(c.default.ERROR,{code:e.code,msg:e.message})}}this._mimeTypes[n.type]=i}t||this._pendingSegments[n.type].push(n),r||this._sourceBuffers[n.type]&&!this._sourceBuffers[n.type].updating&&this._doAppendSegments(),h.default.safari&&"audio/mpeg"===n.container&&n.mediaDuration>0&&(this._requireSetMediaDuration=!0,this._pendingMediaDuration=n.mediaDuration/1e3,this._updateMediaSourceDuration())}},{key:"appendMediaSegment",value:function(e){var t=e;this._pendingSegments[t.type].push(t),this._config.autoCleanupSourceBuffer&&this._needCleanupSourceBuffer()&&this._doCleanupSourceBuffer();var n=this._sourceBuffers[t.type];!n||n.updating||this._hasPendingRemoveRanges()||this._doAppendSegments()}},{key:"seek",value:function(e){for(var t in this._sourceBuffers)if(this._sourceBuffers[t]){var n=this._sourceBuffers[t];if("open"===this._mediaSource.readyState)try{n.abort()}catch(e){l.default.e(this.TAG,e.message)}this._idrList.clear();var i=this._pendingSegments[t];if(i.splice(0,i.length),"closed"!==this._mediaSource.readyState){for(var r=0;r=1&&e-i.start(0)>=this._config.autoCleanupMaxBackwardDuration)return!0}}return!1}},{key:"_doCleanupSourceBuffer",value:function(){var e=this._mediaElement.currentTime;for(var t in this._sourceBuffers){var n=this._sourceBuffers[t];if(n){for(var i=n.buffered,r=!1,s=0;s=this._config.autoCleanupMaxBackwardDuration){r=!0;var u=e-this._config.autoCleanupMinBackwardDuration;this._pendingRemoveRanges[t].push({start:a,end:u})}}else o0&&(isNaN(t)||n>t)&&(l.default.v(this.TAG,"Update MediaSource duration from "+t+" to "+n),this._mediaSource.duration=n),this._requireSetMediaDuration=!1,this._pendingMediaDuration=0}}},{key:"_doRemoveRanges",value:function(){for(var e in this._pendingRemoveRanges)if(this._sourceBuffers[e]&&!this._sourceBuffers[e].updating)for(var t=this._sourceBuffers[e],n=this._pendingRemoveRanges[e];n.length&&!t.updating;){var i=n.shift();t.remove(i.start,i.end)}}},{key:"_doAppendSegments",value:function(){var e=this._pendingSegments;for(var t in e)if(this._sourceBuffers[t]&&!this._sourceBuffers[t].updating&&e[t].length>0){var n=e[t].shift();if(n.timestampOffset){var i=this._sourceBuffers[t].timestampOffset,r=n.timestampOffset/1e3,s=Math.abs(i-r);s>.1&&(l.default.v(this.TAG,"Update MPEG audio timestampOffset from "+i+" to "+r),this._sourceBuffers[t].timestampOffset=r),delete n.timestampOffset}if(!n.data||0===n.data.byteLength)continue;try{this._sourceBuffers[t].appendBuffer(n.data),this._isBufferFull=!1,"video"===t&&n.hasOwnProperty("info")&&this._idrList.appendArray(n.info.syncPoints)}catch(e){this._pendingSegments[t].unshift(n),22===e.code?(this._isBufferFull||this._emitter.emit(c.default.BUFFER_FULL),this._isBufferFull=!0):(l.default.e(this.TAG,e.message),this._emitter.emit(c.default.ERROR,{code:e.code,msg:e.message}))}}}},{key:"_onSourceOpen",value:function(){if(l.default.v(this.TAG,"MediaSource onSourceOpen"),this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._pendingSourceBufferInit.length>0)for(var e=this._pendingSourceBufferInit;e.length;){var t=e.shift();this.appendInitSegment(t,!0)}this._hasPendingSegments()&&this._doAppendSegments(),this._emitter.emit(c.default.SOURCE_OPEN)}},{key:"_onSourceEnded",value:function(){l.default.v(this.TAG,"MediaSource onSourceEnded")}},{key:"_onSourceClose",value:function(){l.default.v(this.TAG,"MediaSource onSourceClose"),this._mediaSource&&null!=this.e&&(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(){var e=this._pendingSegments;return e.video.length>0||e.audio.length>0}},{key:"_hasPendingRemoveRanges",value:function(){var e=this._pendingRemoveRanges;return e.video.length>0||e.audio.length>0}},{key:"_onSourceBufferUpdateEnd",value:function(){this._requireSetMediaDuration?this._updateMediaSourceDuration():this._hasPendingRemoveRanges()?this._doRemoveRanges():this._hasPendingSegments()?this._doAppendSegments():this._hasPendingEos&&this.endOfStream(),this._emitter.emit(c.default.UPDATE_END)}},{key:"_onSourceBufferError",value:function(e){l.default.e(this.TAG,"SourceBuffer Error: "+e)}}]),e}();n.default=p},{"../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(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={ERROR:"error",SOURCE_OPEN:"source_open",UPDATE_END:"update_end",BUFFER_FULL:"buffer_full"};n.default=i},{}],11:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0)this._demuxer.bindDataSource(this._ioctl),this._demuxer.timestampBase=this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase,r=this._demuxer.parseChunks(e,t);else if((i=m.default.probe(e)).match){this._demuxer=new m.default(i,this._config),this._remuxer||(this._remuxer=new v.default(this._config));var s=this._mediaDataSource;void 0==s.duration||isNaN(s.duration)||(this._demuxer.overridedDuration=s.duration),"boolean"==typeof s.hasAudio&&(this._demuxer.overridedHasAudio=s.hasAudio),"boolean"==typeof s.hasVideo&&(this._demuxer.overridedHasVideo=s.hasVideo),this._demuxer.timestampBase=s.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),r=this._demuxer.parseChunks(e,t)}else i=null,l.default.e(this.TAG,"Non-FLV, Unsupported media type!"),Promise.resolve().then(function(){n._internalAbort()}),this._emitter.emit(k.default.DEMUX_ERROR,y.default.FORMAT_UNSUPPORTED,"Non-FLV, Unsupported media type"),r=0;return r}},{key:"_onMediaInfo",value:function(e){var t=this;null==this._mediaInfo&&(this._mediaInfo=Object.assign({},e),this._mediaInfo.keyframesIndex=null,this._mediaInfo.segments=[],this._mediaInfo.segmentCount=this._mediaDataSource.segments.length,Object.setPrototypeOf(this._mediaInfo,c.default.prototype));var n=Object.assign({},e);Object.setPrototypeOf(n,c.default.prototype),this._mediaInfo.segments[this._currentSegmentIndex]=n,this._reportSegmentMediaInfo(this._currentSegmentIndex),null!=this._pendingSeekTime&&Promise.resolve().then(function(){var e=t._pendingSeekTime;t._pendingSeekTime=null,t.seek(e)})}},{key:"_onIOSeeked",value:function(){this._remuxer.insertDiscontinuity()}},{key:"_onIOComplete",value:function(e){var t=e,n=t+1;n0&&n[0].originalDts===i&&(i=n[0].pts),this._emitter.emit(k.default.RECOMMEND_SEEKPOINT,i)}}},{key:"_enableStatisticsReporter",value:function(){null==this._statisticsReporter&&(this._statisticsReporter=self.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval))}},{key:"_disableStatisticsReporter",value:function(){this._statisticsReporter&&(self.clearInterval(this._statisticsReporter),this._statisticsReporter=null)}},{key:"_reportSegmentMediaInfo",value:function(e){var t=this._mediaInfo.segments[e],n=Object.assign({},t);n.duration=this._mediaInfo.duration,n.segmentCount=this._mediaInfo.segmentCount,delete n.segments,delete n.keyframesIndex,this._emitter.emit(k.default.MEDIA_INFO,n)}},{key:"_reportStatisticsInfo",value:function(){var e={};e.url=this._ioctl.currentURL,e.hasRedirect=this._ioctl.hasRedirect,e.hasRedirect&&(e.redirectedURL=this._ioctl.currentRedirectedURL),e.speed=this._ioctl.currentSpeed,e.loaderType=this._ioctl.loaderType,e.currentSegmentIndex=this._currentSegmentIndex,e.totalSegmentCount=this._mediaDataSource.segments.length,this._emitter.emit(k.default.STATISTICS_INFO,e)}}]),e}());n.default=L},{"../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(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={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"};n.default=i},{}],14:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(n,"__esModule",{value:!0});var r=e("../utils/logger.js"),s=(i(r),e("../utils/logging-control.js")),a=i(s),o=e("../utils/polyfill.js"),u=i(o),l=e("./transmuxing-controller.js"),d=i(l),h=e("./transmuxing-events.js"),f=i(h),c=function(e){function t(t,n){var i={msg:f.default.INIT_SEGMENT,data:{type:t,data:n}};e.postMessage(i,[n.data])}function n(t,n){var i={msg:f.default.MEDIA_SEGMENT,data:{type:t,data:n}};e.postMessage(i,[n.data])}function i(){var t={msg:f.default.LOADING_COMPLETE};e.postMessage(t)}function r(){var t={msg:f.default.RECOVERED_EARLY_EOF};e.postMessage(t)}function s(t){var n={msg:f.default.MEDIA_INFO,data:t};e.postMessage(n)}function o(t){var n={msg:f.default.STATISTICS_INFO,data:t};e.postMessage(n)}function l(t,n){e.postMessage({msg:f.default.IO_ERROR,data:{type:t,info:n}})}function h(t,n){e.postMessage({msg:f.default.DEMUX_ERROR,data:{type:t,info:n}})}function c(t){e.postMessage({msg:f.default.RECOMMEND_SEEKPOINT,data:t})}function _(t,n){e.postMessage({msg:"logcat_callback",data:{type:t,logcat:n}})}var m=null,p=_.bind(this);u.default.install(),e.addEventListener("message",function(u){switch(u.data.cmd){case"init":m=new d.default(u.data.param[0],u.data.param[1]),m.on(f.default.IO_ERROR,l.bind(this)),m.on(f.default.DEMUX_ERROR,h.bind(this)),m.on(f.default.INIT_SEGMENT,t.bind(this)),m.on(f.default.MEDIA_SEGMENT,n.bind(this)),m.on(f.default.LOADING_COMPLETE,i.bind(this)),m.on(f.default.RECOVERED_EARLY_EOF,r.bind(this)),m.on(f.default.MEDIA_INFO,s.bind(this)),m.on(f.default.STATISTICS_INFO,o.bind(this)),m.on(f.default.RECOMMEND_SEEKPOINT,c.bind(this));break;case"destroy":m&&(m.destroy(),m=null),e.postMessage({msg:"destroyed"});break;case"start":m.start();break;case"stop":m.stop();break;case"seek":m.seek(u.data.param);break;case"pause":m.pause();break;case"resume":m.resume();break;case"logging_config":var _=u.data.param;a.default.applyConfig(_),!0===_.enableCallback?a.default.addLogListener(p):a.default.removeLogListener(p)}})};n.default=c},{"../utils/logger.js":41,"../utils/logging-control.js":42,"../utils/polyfill.js":43,"./transmuxing-controller.js":12,"./transmuxing-events.js":13}],15:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0?(0,l.default)(new Uint8Array(e,t+2,r)):"",{data:s,size:2+r}}},{key:"parseLongString",value:function(e,t,n){if(n<4)throw new d.IllegalStateException("Data not enough when parse LongString");var i=new DataView(e,t,n),r=i.getUint32(0,!h),s=void 0;return s=r>0?(0,l.default)(new Uint8Array(e,t+4,r)):"",{data:s,size:4+r}}},{key:"parseDate",value:function(e,t,n){if(n<10)throw new d.IllegalStateException("Data size invalid when parse Date");var i=new DataView(e,t,n),r=i.getFloat64(0,!h);return r+=60*i.getInt16(8,!h)*1e3,{data:new Date(r),size:10}}},{key:"parseValue",value:function(t,n,i){if(i<1)throw new d.IllegalStateException("Data not enough when parse Value");var r=new DataView(t,n,i),s=1,a=r.getUint8(0),u=void 0,l=!1;try{switch(a){case 0:u=r.getFloat64(1,!h),s+=8;break;case 1:u=!!r.getUint8(1),s+=1;break;case 2:var f=e.parseString(t,n+1,i-1);u=f.data,s+=f.size;break;case 3:u={};var c=0;for(9==(16777215&r.getUint32(i-4,!h))&&(c=3);s32)throw new s.InvalidArgumentException("ExpGolomb: readBits() bits exceeded max 32bits!");if(e<=this._current_word_bits_left){var t=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t}var n=this._current_word_bits_left?this._current_word:0;n>>>=32-this._current_word_bits_left;var i=e-this._current_word_bits_left;this._fillCurrentWord();var r=Math.min(i,this._current_word_bits_left),a=this._current_word>>>32-r;return this._current_word<<=r,this._current_word_bits_left-=r,n=n<>>e))return this._current_word<<=e,this._current_word_bits_left-=e,e;return this._fillCurrentWord(),e+this._skipLeadingZero()}},{key:"readUEG",value:function(){var e=this._skipLeadingZero();return this.readBits(e+1)-1}},{key:"readSEG",value:function(){var e=this.readUEG();return 1&e?e+1>>>1:-1*(e>>>1)}}]),e}();n.default=a},{"../utils/exception.js":40}],18:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]}Object.defineProperty(n,"__esModule",{value:!0});var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n13))return 0;i=e.probe(t).dataOffset}if(this._firstParse){this._firstParse=!1,n+i!==this._dataOffset&&l.default.w(this.TAG,"First time parsing but chunk byteStart invalid!");0!==new DataView(t,i).getUint32(0,!r)&&l.default.w(this.TAG,"PrevTagSize0 !== 0 !!!"),i+=4}for(;it.byteLength)break;var a=s.getUint8(0),o=16777215&s.getUint32(0,!r);if(i+11+o+4>t.byteLength)break;if(8===a||9===a||18===a){var u=s.getUint8(4),d=s.getUint8(5),h=s.getUint8(6),f=s.getUint8(7),c=h|d<<8|u<<16|f<<24;0!==(16777215&s.getUint32(7,!r))&&l.default.w(this.TAG,"Meet tag which has StreamID != 0!");var _=i+11;switch(a){case 8:this._parseAudioData(t,_,o,c);break;case 9:this._parseVideoData(t,_,o,c,n+i);break;case 18:this._parseScriptData(t,_,o)}var m=s.getUint32(11+o,!r);m!==11+o&&l.default.w(this.TAG,"Invalid PrevTagSize "+m),i+=11+o+4}else l.default.w(this.TAG,"Unsupported tag type "+a+", skipped"),i+=11+o+4}return this._isInitialMetadataDispatched()&&this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack),i}},{key:"_parseScriptData",value:function(e,t,n){var i=h.default.parseScriptData(e,t,n);if(i.hasOwnProperty("onMetaData")){if(null==i.onMetaData||"object"!==a(i.onMetaData))return void l.default.w(this.TAG,"Invalid onMetaData structure!");this._metadata&&l.default.w(this.TAG,"Found another onMetaData tag!"),this._metadata=i;var r=this._metadata.onMetaData;if("boolean"==typeof r.hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=r.hasAudio,this._mediaInfo.hasAudio=this._hasAudio),"boolean"==typeof r.hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=r.hasVideo,this._mediaInfo.hasVideo=this._hasVideo),"number"==typeof r.audiodatarate&&(this._mediaInfo.audioDataRate=r.audiodatarate),"number"==typeof r.videodatarate&&(this._mediaInfo.videoDataRate=r.videodatarate),"number"==typeof r.width&&(this._mediaInfo.width=r.width),"number"==typeof r.height&&(this._mediaInfo.height=r.height),"number"==typeof r.duration){if(!this._durationOverrided){var s=Math.floor(r.duration*this._timescale);this._duration=s,this._mediaInfo.duration=s}}else this._mediaInfo.duration=0;if("number"==typeof r.framerate){var o=Math.floor(1e3*r.framerate);if(o>0){var u=o/1e3;this._referenceFrameRate.fixed=!0,this._referenceFrameRate.fps=u,this._referenceFrameRate.fps_num=o,this._referenceFrameRate.fps_den=1e3,this._mediaInfo.fps=u}}if("object"===a(r.keyframes)){this._mediaInfo.hasKeyframesIndex=!0;var d=r.keyframes;this._mediaInfo.keyframesIndex=this._parseKeyframesIndex(d),r.keyframes=null}else this._mediaInfo.hasKeyframesIndex=!1;this._dispatch=!1,this._mediaInfo.metadata=r,l.default.v(this.TAG,"Parsed onMetaData"),this._mediaInfo.isComplete()&&this._onMediaInfo(this._mediaInfo)}}},{key:"_parseKeyframesIndex",value:function(e){for(var t=[],n=[],i=1;i>>4;if(2!==a&&10!==a)return void this._onError(m.default.CODEC_UNSUPPORTED,"Flv: Unsupported audio codec idx: "+a);var o=0,u=(12&s)>>>2;if(!(u>=0&&u<=4))return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid audio sample rate idx: "+u);o=this._flvSoundRateTable[u];var d=1&s,h=this._audioMetadata,f=this._audioTrack;if(h||(!1===this._hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=!0,this._mediaInfo.hasAudio=!0),h=this._audioMetadata={},h.type="audio",h.id=f.id,h.timescale=this._timescale,h.duration=this._duration,h.audioSampleRate=o,h.channelCount=0===d?1:2),10===a){var c=this._parseAACAudioData(e,t+1,n-1);if(void 0==c)return;if(0===c.packetType){h.config&&l.default.w(this.TAG,"Found another AudioSpecificConfig!");var _=c.data;h.audioSampleRate=_.samplingRate,h.channelCount=_.channelCount,h.codec=_.codec,h.originalCodec=_.originalCodec,h.config=_.config,h.refSampleDuration=1024/h.audioSampleRate*h.timescale,l.default.v(this.TAG,"Parsed AudioSpecificConfig"),this._isInitialMetadataDispatched()?this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack):this._audioInitialMetadataDispatched=!0,this._dispatch=!1,this._onTrackMetadata("audio",h);var p=this._mediaInfo;p.audioCodec=h.originalCodec,p.audioSampleRate=h.audioSampleRate,p.audioChannelCount=h.channelCount,p.hasVideo?null!=p.videoCodec&&(p.mimeType='video/x-flv; codecs="'+p.videoCodec+","+p.audioCodec+'"'):p.mimeType='video/x-flv; codecs="'+p.audioCodec+'"',p.isComplete()&&this._onMediaInfo(p)}else if(1===c.packetType){var v=this._timestampBase+i,g={unit:c.data,dts:v,pts:v};f.samples.push(g),f.length+=c.data.length}else l.default.e(this.TAG,"Flv: Unsupported AAC data type "+c.packetType)}else if(2===a){if(!h.codec){var y=this._parseMP3AudioData(e,t+1,n-1,!0);if(void 0==y)return;h.audioSampleRate=y.samplingRate,h.channelCount=y.channelCount,h.codec=y.codec,h.originalCodec=y.originalCodec,h.refSampleDuration=1152/h.audioSampleRate*h.timescale,l.default.v(this.TAG,"Parsed MPEG Audio Frame Header"),this._audioInitialMetadataDispatched=!0,this._onTrackMetadata("audio",h);var E=this._mediaInfo;E.audioCodec=h.codec,E.audioSampleRate=h.audioSampleRate,E.audioChannelCount=h.channelCount,E.audioDataRate=y.bitRate,E.hasVideo?null!=E.videoCodec&&(E.mimeType='video/x-flv; codecs="'+E.videoCodec+","+E.audioCodec+'"'):E.mimeType='video/x-flv; codecs="'+E.audioCodec+'"',E.isComplete()&&this._onMediaInfo(E)}var b=this._parseMP3AudioData(e,t+1,n-1,!1);if(void 0==b)return;var S=this._timestampBase+i,k={unit:b,dts:S,pts:S};f.samples.push(k),f.length+=b.length}}}},{ -key:"_parseAACAudioData",value:function(e,t,n){if(n<=1)return void l.default.w(this.TAG,"Flv: Invalid AAC packet, missing AACPacketType or/and Data!");var i={},r=new Uint8Array(e,t,n);return i.packetType=r[0],0===r[0]?i.data=this._parseAACAudioSpecificConfig(e,t+1,n-1):i.data=r.subarray(1),i}},{key:"_parseAACAudioSpecificConfig",value:function(e,t,n){var i=new Uint8Array(e,t,n),r=null,s=0,a=0,o=0,u=null;if(s=a=i[0]>>>3,(o=(7&i[0])<<1|i[1]>>>7)<0||o>=this._mpegSamplingRates.length)return void this._onError(m.default.FORMAT_ERROR,"Flv: AAC invalid sampling frequency index!");var l=this._mpegSamplingRates[o],d=(120&i[1])>>>3;if(d<0||d>=8)return void this._onError(m.default.FORMAT_ERROR,"Flv: AAC invalid channel configuration");5===s&&(u=(7&i[1])<<1|i[2]>>>7,i[2]);var h=self.navigator.userAgent.toLowerCase();return-1!==h.indexOf("firefox")?o>=6?(s=5,r=new Array(4),u=o-3):(s=2,r=new Array(2),u=o):-1!==h.indexOf("android")?(s=2,r=new Array(2),u=o):(s=5,u=o,r=new Array(4),o>=6?u=o-3:1===d&&(s=2,r=new Array(2),u=o)),r[0]=s<<3,r[0]|=(15&o)>>>1,r[1]=(15&o)<<7,r[1]|=(15&d)<<3,5===s&&(r[1]|=(15&u)>>>1,r[2]=(1&u)<<7,r[2]|=8,r[3]=0),{config:r,samplingRate:l,channelCount:d,codec:"mp4a.40."+s,originalCodec:"mp4a.40."+a}}},{key:"_parseMP3AudioData",value:function(e,t,n,i){if(n<4)return void l.default.w(this.TAG,"Flv: Invalid MP3 packet, header missing!");var r=(this._littleEndian,new Uint8Array(e,t,n)),s=null;if(i){if(255!==r[0])return;var a=r[1]>>>3&3,o=(6&r[1])>>1,u=(240&r[2])>>>4,d=(12&r[2])>>>2,h=r[3]>>>6&3,f=3!==h?2:1,c=0,_=0;switch(a){case 0:c=this._mpegAudioV25SampleRateTable[d];break;case 2:c=this._mpegAudioV20SampleRateTable[d];break;case 3:c=this._mpegAudioV10SampleRateTable[d]}switch(o){case 1:34,u>>4,o=15&s;if(7!==o)return void this._onError(m.default.CODEC_UNSUPPORTED,"Flv: Unsupported codec in video frame: "+o);this._parseAVCVideoPacket(e,t+1,n-1,i,r,a)}}},{key:"_parseAVCVideoPacket",value:function(e,t,n,i,r,s){if(n<4)return void l.default.w(this.TAG,"Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime");var a=this._littleEndian,o=new DataView(e,t,n),u=o.getUint8(0),d=16777215&o.getUint32(0,!a);if(0===u)this._parseAVCDecoderConfigurationRecord(e,t+4,n-4);else if(1===u)this._parseAVCVideoData(e,t+4,n-4,i,r,s,d);else if(2!==u)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid video packet type "+u)}},{key:"_parseAVCDecoderConfigurationRecord",value:function(e,t,n){if(n<7)return void l.default.w(this.TAG,"Flv: Invalid AVCDecoderConfigurationRecord, lack of data!");var i=this._videoMetadata,r=this._videoTrack,s=this._littleEndian,a=new DataView(e,t,n);i?void 0!==i.avcc&&l.default.w(this.TAG,"Found another AVCDecoderConfigurationRecord!"):(!1===this._hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=!0,this._mediaInfo.hasVideo=!0),i=this._videoMetadata={},i.type="video",i.id=r.id,i.timescale=this._timescale,i.duration=this._duration);var o=a.getUint8(0),u=a.getUint8(1);a.getUint8(2),a.getUint8(3);if(1!==o||0===u)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid AVCDecoderConfigurationRecord");if(this._naluLengthSize=1+(3&a.getUint8(4)),3!==this._naluLengthSize&&4!==this._naluLengthSize)return void this._onError(m.default.FORMAT_ERROR,"Flv: Strange NaluLengthSizeMinusOne: "+(this._naluLengthSize-1));var d=31&a.getUint8(5);if(0===d)return void this._onError(m.default.FORMAT_ERROR,"Flv: Invalid AVCDecoderConfigurationRecord: No SPS");d>1&&l.default.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: SPS Count = "+d);for(var h=6,f=0;f1&&l.default.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: PPS Count = "+w),h++;for(var R=0;R=n){l.default.w(this.TAG,"Malformed Nalu near timestamp "+_+", offset = "+f+", dataSize = "+n);break}var p=u.getUint32(f,!o);if(3===c&&(p>>>=8),p>n-c)return void l.default.w(this.TAG,"Malformed Nalus near timestamp "+_+", NaluSize > DataSize!");var v=31&u.getUint8(f+c);5===v&&(m=!0);var g=new Uint8Array(e,t+f,c+p),y={type:v,data:g};d.push(y),h+=g.byteLength,f+=c+p}if(d.length){var E=this._videoTrack,b={units:d,length:h,isKeyframe:m,dts:_,cts:a,pts:_+a};m&&(b.fileposition=r),E.samples.push(b),E.length+=h}}},{key:"onTrackMetadata",get:function(){return this._onTrackMetadata},set:function(e){this._onTrackMetadata=e}},{key:"onMediaInfo",get:function(){return this._onMediaInfo},set:function(e){this._onMediaInfo=e}},{key:"onError",get:function(){return this._onError},set:function(e){this._onError=e}},{key:"onDataAvailable",get:function(){return this._onDataAvailable},set:function(e){this._onDataAvailable=e}},{key:"timestampBase",get:function(){return this._timestampBase},set:function(e){this._timestampBase=e}},{key:"overridedDuration",get:function(){return this._duration},set:function(e){this._durationOverrided=!0,this._duration=e,this._mediaInfo.duration=e}},{key:"overridedHasAudio",set:function(e){this._hasAudioFlagOverrided=!0,this._hasAudio=e,this._mediaInfo.hasAudio=e}},{key:"overridedHasVideo",set:function(e){this._hasVideoFlagOverrided=!0,this._hasVideo=e,this._mediaInfo.hasVideo=e}}],[{key:"probe",value:function(e){var t=new Uint8Array(e),n={match:!1};if(70!==t[0]||76!==t[1]||86!==t[2]||1!==t[3])return n;var i=(4&t[4])>>>2!=0,r=0!=(1&t[4]),a=s(t,5);return a<9?n:{match:!0,consumed:a,dataOffset:a,hasAudioTrack:i,hasVideoTrack:r}}}]),e}();n.default=y},{"../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(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=2&&3===t[s]&&0===t[s-1]&&0===t[s-2]||(i[r]=t[s],r++);return new Uint8Array(i.buffer,0,r)}},{key:"parseSPS",value:function(t){var n=e._ebsp2rbsp(t),i=new a.default(n);i.readByte();var r=i.readByte();i.readByte();var s=i.readByte();i.readUEG();var o=e.getProfileString(r),u=e.getLevelString(s),l=1,d=420,h=[0,420,422,444],f=8;if((100===r||110===r||122===r||244===r||44===r||83===r||86===r||118===r||128===r||138===r||144===r)&&(l=i.readUEG(),3===l&&i.readBits(1),l<=3&&(d=h[l]),f=i.readUEG()+8,i.readUEG(),i.readBits(1),i.readBool()))for(var c=3!==l?8:12,_=0;_0&&I<16?(w=x[I-1],R=M[I-1]):255===I&&(w=i.readByte()<<8|i.readByte(),R=i.readByte()<<8|i.readByte())}if(i.readBool()&&i.readBool(),i.readBool()&&(i.readBits(4),i.readBool()&&i.readBits(24)),i.readBool()&&(i.readUEG(),i.readUEG()),i.readBool()){var D=i.readBits(32),B=i.readBits(32);O=i.readBool(),T=B,C=2*D,A=T/C}}var j=1;1===w&&1===R||(j=w/R);var P=0,U=0;if(0===l)P=1,U=2-E;else{var N=3===l?1:2,F=1===l?2:1;P=N,U=F*(2-E)}var G=16*(g+1),V=16*(y+1)*(2-E);G-=(b+S)*P,V-=(k+L)*U;var z=Math.ceil(G*j);return i.destroy(),i=null,{profile_string:o,level_string:u,bit_depth:f,chroma_format:d,chroma_format_string:e.getChromaFormatString(d),frame_rate:{fixed:O,fps:A,fps_den:C,fps_num:T},sar_ratio:{width:w,height:R},codec_size:{width:G,height:V},present_size:{width:z,height:V}}}},{key:"_skipScalingList",value:function(e,t){for(var n=8,i=8,r=0,s=0;s=15048,t=!f.default.msedge||e;return self.fetch&&self.ReadableStream&&t}catch(e){return!1}}}]),l(t,[{key:"destroy",value:function(){this.isWorking()&&this.abort(),u(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"destroy",this).call(this)}},{key:"open",value:function(e,t){var n=this;this._dataSource=e,this._range=t;var i=e.url;this._config.reuseRedirectedURL&&void 0!=e.redirectedURL&&(i=e.redirectedURL);var r=this._seekHandler.getConfig(i,t),s=new self.Headers;if("object"===o(r.headers)){var a=r.headers;for(var u in a)a.hasOwnProperty(u)&&s.append(u,a[u])}var l={method:"GET",headers:s,mode:"cors",cache:"default",referrerPolicy:"no-referrer-when-downgrade"};!1===e.cors&&(l.mode="same-origin"),e.withCredentials&&(l.credentials="include"),e.referrerPolicy&&(l.referrerPolicy=e.referrerPolicy),this._status=c.LoaderStatus.kConnecting,self.fetch(r.url,l).then(function(e){if(n._requestAbort)return n._requestAbort=!1,void(n._status=c.LoaderStatus.kIdle);if(e.ok&&e.status>=200&&e.status<=299){if(e.url!==r.url&&n._onURLRedirect){var t=n._seekHandler.removeURLParameters(e.url);n._onURLRedirect(t)}var i=e.headers.get("Content-Length");return null!=i&&(n._contentLength=parseInt(i),0!==n._contentLength&&n._onContentLengthKnown&&n._onContentLengthKnown(n._contentLength)),n._pump.call(n,e.body.getReader())}if(n._status=c.LoaderStatus.kError,!n._onError)throw new _.RuntimeException("FetchStreamLoader: Http code invalid, "+e.status+" "+e.statusText);n._onError(c.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:e.status,msg:e.statusText})}).catch(function(e){if(n._status=c.LoaderStatus.kError,!n._onError)throw e;n._onError(c.LoaderErrors.EXCEPTION,{code:-1,msg:e.message})})}},{key:"abort",value:function(){this._requestAbort=!0}},{key:"_pump",value:function(e){var t=this;return e.read().then(function(n){if(!n.done){if(!0===t._requestAbort)return t._requestAbort=!1,t._status=c.LoaderStatus.kComplete,e.cancel();t._status=c.LoaderStatus.kBuffering;var i=n.value.buffer,r=t._range.from+t._receivedLength;return t._receivedLength+=i.byteLength,t._onDataArrival&&t._onDataArrival(i,r,t._receivedLength),t._pump(e)}t._status=c.LoaderStatus.kComplete,t._onComplete&&t._onComplete(t._range.from,t._range.from+t._receivedLength-1)}).catch(function(e){if(11!==e.code||!f.default.msedge){t._status=c.LoaderStatus.kError;var n=0,i=null;if(19!==e.code&&"network error"!==e.message||!(null===t._contentLength||null!==t._contentLength&&t._receivedLength0&&(this._stashInitialSize=n.stashInitialSize),this._stashUsed=0,this._stashSize=this._stashInitialSize,this._bufferSize=3145728,this._stashBuffer=new ArrayBuffer(this._bufferSize),this._stashByteStart=0,this._enableStash=!0,!1===n.enableStashBuffer&&(this._enableStash=!1),this._loader=null,this._loaderClass=null,this._seekHandler=null,this._dataSource=t,this._isWebSocketURL=/wss?:\/\/(.+?)/.test(t.url),this._refTotalLength=t.filesize?t.filesize:null,this._totalLength=this._refTotalLength,this._fullRequestFlag=!1,this._currentRange=null,this._redirectedURL=null,this._speedNormalized=0,this._speedSampler=new l.default,this._speedNormalizeList=[64,128,256,384,512,768,1024,1536,2048,3072,4096],this._isEarlyEofReconnecting=!1,this._paused=!1,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()}return s(e,[{key:"destroy",value:function(){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=!1,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(){return this._loader&&this._loader.isWorking()&&!this._paused}},{key:"isPaused",value:function(){return this._paused}},{key:"_selectSeekHandler",value:function(){var e=this._config;if("range"===e.seekType)this._seekHandler=new b.default(this._config.rangeLoadZeroStart);else if("param"===e.seekType){var t=e.seekParamStart||"bstart",n=e.seekParamEnd||"bend";this._seekHandler=new k.default(t,n)}else{if("custom"!==e.seekType)throw new L.InvalidArgumentException("Invalid seekType in config: "+e.seekType);if("function"!=typeof e.customSeekHandler)throw new L.InvalidArgumentException("Custom seekType specified in config but invalid customSeekHandler!");this._seekHandler=new e.customSeekHandler}}},{key:"_selectLoader",value:function(){if(this._isWebSocketURL)this._loaderClass=y.default;else if(f.default.isSupported())this._loaderClass=f.default;else if(_.default.isSupported())this._loaderClass=_.default;else{if(!v.default.isSupported())throw new L.RuntimeException("Your browser doesn't support xhr with arraybuffer responseType!");this._loaderClass=v.default}}},{key:"_createLoader",value:function(){this._loader=new this._loaderClass(this._seekHandler,this._config),!1===this._loader.needStashBuffer&&(this._enableStash=!1),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(e){this._currentRange={from:0,to:-1},e&&(this._currentRange.from=e),this._speedSampler.reset(),e||(this._fullRequestFlag=!0),this._loader.open(this._dataSource,Object.assign({},this._currentRange))}},{key:"abort",value:function(){this._loader.abort(),this._paused&&(this._paused=!1,this._resumeFrom=0)}},{key:"pause",value:function(){this.isWorking()&&(this._loader.abort(),0!==this._stashUsed?(this._resumeFrom=this._stashByteStart,this._currentRange.to=this._stashByteStart-1):this._resumeFrom=this._currentRange.to+1,this._stashUsed=0,this._stashByteStart=0,this._paused=!0)}},{key:"resume",value:function(){if(this._paused){this._paused=!1;var e=this._resumeFrom;this._resumeFrom=0,this._internalSeek(e,!0)}}},{key:"seek",value:function(e){this._paused=!1,this._stashUsed=0,this._stashByteStart=0,this._internalSeek(e,!0)}},{key:"_internalSeek",value:function(e,t){this._loader.isWorking()&&this._loader.abort(),this._flushStashBuffer(t),this._loader.destroy(),this._loader=null;var n={from:e,to:-1};this._currentRange={from:n.from,to:-1},this._speedSampler.reset(),this._stashSize=this._stashInitialSize,this._createLoader(),this._loader.open(this._dataSource,n),this._onSeeked&&this._onSeeked()}},{key:"updateUrl",value:function(e){if(!e||"string"!=typeof e||0===e.length)throw new L.InvalidArgumentException("Url must be a non-empty string!");this._dataSource.url=e}},{key:"_expandBuffer",value:function(e){for(var t=this._stashSize;t+10485760){var i=new Uint8Array(this._stashBuffer,0,this._stashUsed);new Uint8Array(n,0,t).set(i,0)}this._stashBuffer=n,this._bufferSize=t}}},{key:"_normalizeSpeed",value:function(e){var t=this._speedNormalizeList,n=t.length-1,i=0,r=0,s=n;if(e=t[i]&&e=512&&e<=1024?Math.floor(1.5*e):2*e)>8192&&(t=8192);var n=1024*t+1048576;this._bufferSize0){var o=this._stashBuffer.slice(0,this._stashUsed),u=this._dispatchChunks(o,this._stashByteStart);if(u0){var l=new Uint8Array(o,u);a.set(l,0),this._stashUsed=l.byteLength,this._stashByteStart+=u}}else this._stashUsed=0,this._stashByteStart+=u;this._stashUsed+e.byteLength>this._bufferSize&&(this._expandBuffer(this._stashUsed+e.byteLength),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength}else{var d=this._dispatchChunks(e,t);if(dthis._bufferSize&&(this._expandBuffer(h),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e,d),0),this._stashUsed+=h,this._stashByteStart=t+d}}}else if(0===this._stashUsed){var f=this._dispatchChunks(e,t);if(fthis._bufferSize&&this._expandBuffer(c);var _=new Uint8Array(this._stashBuffer,0,this._bufferSize);_.set(new Uint8Array(e,f),0),this._stashUsed+=c,this._stashByteStart=t+f}}else{this._stashUsed+e.byteLength>this._bufferSize&&this._expandBuffer(this._stashUsed+e.byteLength);var m=new Uint8Array(this._stashBuffer,0,this._bufferSize);m.set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength;var p=this._dispatchChunks(this._stashBuffer.slice(0,this._stashUsed),this._stashByteStart);if(p0){var v=new Uint8Array(this._stashBuffer,p);m.set(v,0)}this._stashUsed-=p,this._stashByteStart+=p}}}},{key:"_flushStashBuffer",value:function(e){if(this._stashUsed>0){var t=this._stashBuffer.slice(0,this._stashUsed),n=this._dispatchChunks(t,this._stashByteStart),i=t.byteLength-n;if(n0){var r=new Uint8Array(this._stashBuffer,0,this._bufferSize),s=new Uint8Array(t,n);r.set(s,0),this._stashUsed=s.byteLength,this._stashByteStart+=n}return 0}o.default.w(this.TAG,i+" bytes unconsumed data remain when flush buffer, dropped")}return this._stashUsed=0,this._stashByteStart=0,i}return 0}},{key:"_onLoaderComplete",value:function(e,t){this._flushStashBuffer(!0),this._onComplete&&this._onComplete(this._extraData)}},{key:"_onLoaderError",value:function(e,t){switch(o.default.e(this.TAG,"Loader error, code = "+t.code+", msg = "+t.msg),this._flushStashBuffer(!1),this._isEarlyEofReconnecting&&(this._isEarlyEofReconnecting=!1,e=d.LoaderErrors.UNRECOVERABLE_EARLY_EOF),e){case d.LoaderErrors.EARLY_EOF:if(!this._config.isLive&&this._totalLength){var n=this._currentRange.to+1;return void(n0)for(var s=n.split("&"),a=0;a0;o[0]!==this._startName&&o[0]!==this._endName&&(u&&(r+="&"),r+=s[a])}return 0===r.length?t:t+"?"+r}}]),e}();n.default=s},{}],26:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=500?this.currentKBps:0}},{key:"averageKBps",get:function(){var e=(this._now()-this._firstCheckpoint)/1e3;return this._totalBytes/e/1024}}]),e}();n.default=s},{}],28:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(n,"__esModule",{value:!0});var a=function e(t,n,i){null===t&&(t=Function.prototype);var r=Object.getOwnPropertyDescriptor(t,n);if(void 0===r){var s=Object.getPrototypeOf(t);return null===s?void 0:e(s,n,i)}if("value"in r)return r.value;var a=r.get;if(void 0!==a)return a.call(i)},o=function(){function e(e,t){for(var n=0;n299)){if(this._status=h.LoaderStatus.kError,!this._onError)throw new f.RuntimeException("MozChunkedLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}else this._status=h.LoaderStatus.kBuffering}}},{key:"_onProgress",value:function(e){null===this._contentLength&&null!==e.total&&0!==e.total&&(this._contentLength=e.total,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength));var t=e.target.response,n=this._range.from+this._receivedLength;this._receivedLength+=t.byteLength,this._onDataArrival&&this._onDataArrival(t,n,this._receivedLength)}},{key:"_onLoadEnd",value:function(e){if(!0===this._requestAbort)return void(this._requestAbort=!1);this._status!==h.LoaderStatus.kError&&(this._status=h.LoaderStatus.kComplete,this._onComplete&&this._onComplete(this._range.from,this._range.from+this._receivedLength-1))}},{key:"_onXhrError",value:function(e){this._status=h.LoaderStatus.kError;var t=0,n=null;if(this._contentLength&&e.loaded=200&&t.status<=299){if(this._status=h.LoaderStatus.kBuffering,void 0!=t.responseURL){var n=this._seekHandler.removeURLParameters(t.responseURL);t.responseURL!==this._currentRequestURL&&n!==this._currentRedirectedURL&&(this._currentRedirectedURL=n,this._onURLRedirect&&this._onURLRedirect(n))}var i=t.getResponseHeader("Content-Length");if(null!=i&&null==this._contentLength){var r=parseInt(i);r>0&&(this._contentLength=r,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength))}}else{if(this._status=h.LoaderStatus.kError,!this._onError)throw new f.RuntimeException("MSStreamLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(h.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}else if(3===t.readyState&&t.status>=200&&t.status<=299){this._status=h.LoaderStatus.kBuffering;var s=t.response;this._reader.readAsArrayBuffer(s)}}},{key:"_xhrOnError",value:function(e){this._status=h.LoaderStatus.kError;var t=h.LoaderErrors.EXCEPTION,n={code:-1,msg:e.constructor.name+" "+e.type};if(!this._onError)throw new f.RuntimeException(n.msg);this._onError(t,n)}},{key:"_msrOnProgress",value:function(e){var t=e.target,n=t.result;if(null==n)return void this._doReconnectIfNeeded();var i=n.slice(this._lastTimeBufferSize);this._lastTimeBufferSize=n.byteLength;var r=this._totalRange.from+this._receivedLength;this._receivedLength+=i.byteLength,this._onDataArrival&&this._onDataArrival(i,r,this._receivedLength),n.byteLength>=this._bufferLimit&&(d.default.v(this.TAG,"MSStream buffer exceeded max size near "+(r+i.byteLength)+", reconnecting..."),this._doReconnectIfNeeded())}},{key:"_doReconnectIfNeeded",value:function(){if(null==this._contentLength||this._receivedLength=this._contentLength&&(n=this._range.from+this._contentLength-1),this._currentRequestRange={from:t,to:n},this._internalOpen(this._dataSource,this._currentRequestRange)}},{key:"_internalOpen",value:function(e,t){this._lastTimeLoaded=0;var n=e.url;this._config.reuseRedirectedURL&&(void 0!=this._currentRedirectedURL?n=this._currentRedirectedURL:void 0!=e.redirectedURL&&(n=e.redirectedURL));var i=this._seekHandler.getConfig(n,t);this._currentRequestURL=i.url;var r=this._xhr=new XMLHttpRequest;if(r.open("GET",i.url,!0),r.responseType="arraybuffer",r.onreadystatechange=this._onReadyStateChange.bind(this),r.onprogress=this._onProgress.bind(this),r.onload=this._onLoad.bind(this),r.onerror=this._onXhrError.bind(this),e.withCredentials&&r.withCredentials&&(r.withCredentials=!0),"object"===o(i.headers)){var s=i.headers;for(var a in s)s.hasOwnProperty(a)&&r.setRequestHeader(a,s[a])}r.send()}},{key:"abort",value:function(){this._requestAbort=!0,this._internalAbort(),this._status=_.LoaderStatus.kComplete}},{key:"_internalAbort",value:function(){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(e){var t=e.target;if(2===t.readyState){if(void 0!=t.responseURL){var n=this._seekHandler.removeURLParameters(t.responseURL);t.responseURL!==this._currentRequestURL&&n!==this._currentRedirectedURL&&(this._currentRedirectedURL=n,this._onURLRedirect&&this._onURLRedirect(n))}if(t.status>=200&&t.status<=299){if(this._waitForTotalLength)return;this._status=_.LoaderStatus.kBuffering}else{if(this._status=_.LoaderStatus.kError,!this._onError)throw new m.RuntimeException("RangeLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(_.LoaderErrors.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}}}},{key:"_onProgress",value:function(e){if(null===this._contentLength){var t=!1;if(this._waitForTotalLength){this._waitForTotalLength=!1,this._totalLengthReceived=!0,t=!0;var n=e.total;this._internalAbort(),null!=n&0!==n&&(this._totalLength=n)}if(-1===this._range.to?this._contentLength=this._totalLength-this._range.from:this._contentLength=this._range.to-this._range.from+1,t)return void this._openSubRange();this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength)}var i=e.loaded-this._lastTimeLoaded;this._lastTimeLoaded=e.loaded,this._speedSampler.addBytes(i)}},{key:"_normalizeSpeed",value:function(e){var t=this._chunkSizeKBList,n=t.length-1,i=0,r=0,s=n;if(e=t[i]&&e=3&&(t=this._speedSampler.currentKBps),0!==t){var n=this._normalizeSpeed(t);this._currentSpeedNormalized!==n&&(this._currentSpeedNormalized=n,this._currentChunkSizeKB=n)}var i=e.target.response,r=this._range.from+this._receivedLength;this._receivedLength+=i.byteLength;var s=!1;null!=this._contentLength&&this._receivedLength0&&this._receivedLength0&&(this._requestSetTime=!0,this._mediaElement.currentTime=0),this._transmuxer=new p.default(this._mediaDataSource,this._config),this._transmuxer.on(g.default.INIT_SEGMENT,function(t,n){e._msectl.appendInitSegment(n)}),this._transmuxer.on(g.default.MEDIA_SEGMENT,function(t,n){if(e._msectl.appendMediaSegment(n),e._config.lazyLoad&&!e._config.isLive){var i=e._mediaElement.currentTime;n.info.endDts>=1e3*(i+e._config.lazyLoadMaxDuration)&&null==e._progressChecker&&(d.default.v(e.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),e._suspendTransmuxer())}}),this._transmuxer.on(g.default.LOADING_COMPLETE,function(){e._msectl.endOfStream(),e._emitter.emit(_.default.LOADING_COMPLETE)}),this._transmuxer.on(g.default.RECOVERED_EARLY_EOF,function(){e._emitter.emit(_.default.RECOVERED_EARLY_EOF)}),this._transmuxer.on(g.default.IO_ERROR,function(t,n){e._emitter.emit(_.default.ERROR,k.ErrorTypes.NETWORK_ERROR,t,n)}),this._transmuxer.on(g.default.DEMUX_ERROR,function(t,n){e._emitter.emit(_.default.ERROR,k.ErrorTypes.MEDIA_ERROR,t,{code:-1,msg:n})}),this._transmuxer.on(g.default.MEDIA_INFO,function(t){e._mediaInfo=t,e._emitter.emit(_.default.MEDIA_INFO,Object.assign({},t))}),this._transmuxer.on(g.default.STATISTICS_INFO,function(t){e._statisticsInfo=e._fillStatisticsInfo(t), -e._emitter.emit(_.default.STATISTICS_INFO,Object.assign({},e._statisticsInfo))}),this._transmuxer.on(g.default.RECOMMEND_SEEKPOINT,function(t){e._mediaElement&&!e._config.accurateSeek&&(e._requestSetTime=!0,e._mediaElement.currentTime=t/1e3)}),this._transmuxer.open()}}},{key:"unload",value:function(){this._mediaElement&&this._mediaElement.pause(),this._msectl&&this._msectl.seek(0),this._transmuxer&&(this._transmuxer.close(),this._transmuxer.destroy(),this._transmuxer=null)}},{key:"play",value:function(){return this._mediaElement.play()}},{key:"pause",value:function(){this._mediaElement.pause()}},{key:"_fillStatisticsInfo",value:function(e){if(e.playerType=this._type,!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,n=0,i=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();n=r.totalVideoFrames,i=r.droppedVideoFrames}else void 0!=this._mediaElement.webkitDecodedFrameCount?(n=this._mediaElement.webkitDecodedFrameCount,i=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=n,e.droppedFrames=i),e}},{key:"_onmseUpdateEnd",value:function(){if(this._config.lazyLoad&&!this._config.isLive){for(var e=this._mediaElement.buffered,t=this._mediaElement.currentTime,n=0,i=0;i=t+this._config.lazyLoadMaxDuration&&null==this._progressChecker&&(d.default.v(this.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),this._suspendTransmuxer())}}},{key:"_onmseBufferFull",value:function(){d.default.v(this.TAG,"MSE SourceBuffer is full, suspend transmuxing task"),null==this._progressChecker&&this._suspendTransmuxer()}},{key:"_suspendTransmuxer",value:function(){this._transmuxer&&(this._transmuxer.pause(),null==this._progressChecker&&(this._progressChecker=window.setInterval(this._checkProgressAndResume.bind(this),1e3)))}},{key:"_checkProgressAndResume",value:function(){for(var e=this._mediaElement.currentTime,t=this._mediaElement.buffered,n=!1,i=0;i=r&&e=s-this._config.lazyLoadRecoverDuration&&(n=!0);break}}n&&(window.clearInterval(this._progressChecker),this._progressChecker=null,n&&(d.default.v(this.TAG,"Continue loading from paused position"),this._transmuxer.resume()))}},{key:"_isTimepointBuffered",value:function(e){for(var t=this._mediaElement.buffered,n=0;n=i&&e0){var r=this._mediaElement.buffered.start(0);(r<1&&e0&&t.currentTime0){var i=n.start(0);if(i<1&&t0&&(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(){this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src")),null!=this._statisticsReporter&&(window.clearInterval(this._statisticsReporter),this._statisticsReporter=null)}},{key:"play",value:function(){return this._mediaElement.play()}},{key:"pause",value:function(){this._mediaElement.pause()}},{key:"_onvLoadedMetadata",value:function(e){null!=this._pendingSeekTime&&(this._mediaElement.currentTime=this._pendingSeekTime,this._pendingSeekTime=null),this._emitter.emit(d.default.MEDIA_INFO,this.mediaInfo)}},{key:"_reportStatisticsInfo",value:function(){this._emitter.emit(d.default.STATISTICS_INFO,this.statisticsInfo)}},{key:"type",get:function(){return this._type}},{key:"buffered",get:function(){return this._mediaElement.buffered}},{key:"duration",get:function(){return this._mediaElement.duration}},{key:"volume",get:function(){return this._mediaElement.volume},set:function(e){this._mediaElement.volume=e}},{key:"muted",get:function(){return this._mediaElement.muted},set:function(e){this._mediaElement.muted=e}},{key:"currentTime",get:function(){return this._mediaElement?this._mediaElement.currentTime:0},set:function(e){this._mediaElement?this._mediaElement.currentTime=e:this._pendingSeekTime=e}},{key:"mediaInfo",get:function(){var e=this._mediaElement instanceof HTMLAudioElement?"audio/":"video/",t={mimeType:e+this._mediaDataSource.type};return this._mediaElement&&(t.duration=Math.floor(1e3*this._mediaElement.duration),this._mediaElement instanceof HTMLVideoElement&&(t.width=this._mediaElement.videoWidth,t.height=this._mediaElement.videoHeight)),t}},{key:"statisticsInfo",get:function(){var e={playerType:this._type,url:this._mediaDataSource.url};if(!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,n=0,i=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();n=r.totalVideoFrames,i=r.droppedVideoFrames}else void 0!=this._mediaElement.webkitDecodedFrameCount?(n=this._mediaElement.webkitDecodedFrameCount,i=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=n,e.droppedFrames=i),e}}]),e}();n.default=c},{"../config.js":5,"../utils/exception.js":40,"./player-events.js":35,events:2}],34:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.ErrorDetails=n.ErrorTypes=void 0;var i=e("../io/loader.js"),r=e("../demux/demux-errors.js"),s=function(e){return e&&e.__esModule?e:{default:e}}(r);n.ErrorTypes={NETWORK_ERROR:"NetworkError",MEDIA_ERROR:"MediaError",OTHER_ERROR:"OtherError"},n.ErrorDetails={NETWORK_EXCEPTION:i.LoaderErrors.EXCEPTION,NETWORK_STATUS_CODE_INVALID:i.LoaderErrors.HTTP_STATUS_CODE_INVALID,NETWORK_TIMEOUT:i.LoaderErrors.CONNECTING_TIMEOUT,NETWORK_UNRECOVERABLE_EARLY_EOF:i.LoaderErrors.UNRECOVERABLE_EARLY_EOF,MEDIA_MSE_ERROR:"MediaMSEError",MEDIA_FORMAT_ERROR:s.default.FORMAT_ERROR,MEDIA_FORMAT_UNSUPPORTED:s.default.FORMAT_UNSUPPORTED,MEDIA_CODEC_UNSUPPORTED:s.default.CODEC_UNSUPPORTED}},{"../demux/demux-errors.js":16,"../io/loader.js":24}],35:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i={ERROR:"error",LOADING_COMPLETE:"loading_complete",RECOVERED_EARLY_EOF:"recovered_early_eof",MEDIA_INFO:"media_info",STATISTICS_INFO:"statistics_info"};n.default=i},{}],36:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n>>24&255,n[1]=t>>>16&255,n[2]=t>>>8&255,n[3]=255&t,n.set(e,4);for(var a=8,o=0;o>>24&255,t>>>16&255,t>>>8&255,255&t,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]))}},{key:"trak",value:function(t){return e.box(e.types.trak,e.tkhd(t),e.mdia(t))}},{key:"tkhd",value:function(t){var n=t.id,i=t.duration,r=t.presentWidth,s=t.presentHeight;return e.box(e.types.tkhd,new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,r>>>8&255,255&r,0,0,s>>>8&255,255&s,0,0]))}},{key:"mdia",value:function(t){return e.box(e.types.mdia,e.mdhd(t),e.hdlr(t),e.minf(t))}},{key:"mdhd",value:function(t){var n=t.timescale,i=t.duration;return e.box(e.types.mdhd,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,i>>>24&255,i>>>16&255,i>>>8&255,255&i,85,196,0,0]))}},{key:"hdlr",value:function(t){var n=null;return n="audio"===t.type?e.constants.HDLR_AUDIO:e.constants.HDLR_VIDEO,e.box(e.types.hdlr,n)}},{key:"minf",value:function(t){var n=null;return n="audio"===t.type?e.box(e.types.smhd,e.constants.SMHD):e.box(e.types.vmhd,e.constants.VMHD),e.box(e.types.minf,n,e.dinf(),e.stbl(t))}},{key:"dinf",value:function(){return e.box(e.types.dinf,e.box(e.types.dref,e.constants.DREF))}},{key:"stbl",value:function(t){return e.box(e.types.stbl,e.stsd(t),e.box(e.types.stts,e.constants.STTS),e.box(e.types.stsc,e.constants.STSC),e.box(e.types.stsz,e.constants.STSZ),e.box(e.types.stco,e.constants.STCO))}},{key:"stsd",value:function(t){return"audio"===t.type?"mp3"===t.codec?e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp3(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp4a(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.avc1(t))}},{key:"mp3",value:function(t){var n=t.channelCount,i=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,n,0,16,0,0,0,0,i>>>8&255,255&i,0,0]);return e.box(e.types[".mp3"],r)}},{key:"mp4a",value:function(t){var n=t.channelCount,i=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,n,0,16,0,0,0,0,i>>>8&255,255&i,0,0]);return e.box(e.types.mp4a,r,e.esds(t))}},{key:"esds",value:function(t){var n=t.config||[],i=n.length,r=new Uint8Array([0,0,0,0,3,23+i,0,1,0,4,15+i,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([i]).concat(n).concat([6,1,2]));return e.box(e.types.esds,r)}},{key:"avc1",value:function(t){var n=t.avcc,i=t.codecWidth,r=t.codecHeight,s=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,i>>>8&255,255&i,r>>>8&255,255&r,0,72,0,0,0,72,0,0,0,0,0,0,0,1,10,120,113,113,47,102,108,118,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255]);return e.box(e.types.avc1,s,e.box(e.types.avcC,n))}},{key:"mvex",value:function(t){return e.box(e.types.mvex,e.trex(t))}},{key:"trex",value:function(t){var n=t.id,i=new Uint8Array([0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return e.box(e.types.trex,i)}},{key:"moof",value:function(t,n){return e.box(e.types.moof,e.mfhd(t.sequenceNumber),e.traf(t,n))}},{key:"mfhd",value:function(t){var n=new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t]);return e.box(e.types.mfhd,n)}},{key:"traf",value:function(t,n){var i=t.id,r=e.box(e.types.tfhd,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),s=e.box(e.types.tfdt,new Uint8Array([0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n])),a=e.sdtp(t),o=e.trun(t,a.byteLength+16+16+8+16+8+8);return e.box(e.types.traf,r,s,o,a)}},{key:"sdtp",value:function(t){for(var n=t.samples||[],i=n.length,r=new Uint8Array(4+i),s=0;s>>24&255,r>>>16&255,r>>>8&255,255&r,n>>>24&255,n>>>16&255,n>>>8&255,255&n],0);for(var o=0;o>>24&255,u>>>16&255,u>>>8&255,255&u,l>>>24&255,l>>>16&255,l>>>8&255,255&l,d.isLeading<<2|d.dependsOn,d.isDependedOn<<6|d.hasRedundancy<<4|d.isNonSync,0,0,h>>>24&255,h>>>16&255,h>>>8&255,255&h],12+16*o)}return e.box(e.types.trun,a)}},{key:"mdat",value:function(t){return e.box(e.types.mdat,t)}}]),e}();s.init(),n.default=s},{}],38:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n=1?A[A.length-1].duration:Math.floor(a);var D=!1,B=null;if(M>1.5*a&&"mp3"!==this._audioMeta.codec&&this._fillAudioTimestampGap&&!c.default.safari){D=!0;var j=Math.abs(M-a),P=Math.ceil(j/a),U=x+a;o.default.w(this.TAG,"Large audio timestamp gap detected, may cause AV sync to drift. Silent frames will be generated to avoid unsync.\ndts: "+(x+M)+" ms, expected: "+(x+Math.round(a))+" ms, delta: "+Math.round(j)+" ms, generate: "+P+" frames");var N=h.default.getSilentFrame(this._audioMeta.originalCodec,this._audioMeta.channelCount);null==N&&(o.default.w(this.TAG,"Unable to generate silent frame for "+this._audioMeta.originalCodec+" with "+this._audioMeta.channelCount+" channels, repeat last frame"),N=C),B=[];for(var F=0;F0){var V=B[B.length-1];V.duration=G-V.dts}var z={dts:G,pts:G,cts:0,unit:N,size:N.byteLength,duration:0,originalDts:I,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}};B.push(z),v+=C.byteLength,U+=a}var H=B[B.length-1];H.duration=x+M-H.dts,M=Math.round(a)}A.push({dts:x,pts:x,cts:0,unit:T.unit,size:T.unit.byteLength,duration:M,originalDts:I,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}}),D&&A.push.apply(A,B)}u?p=new Uint8Array(v):(p=new Uint8Array(v),p[0]=v>>>24&255,p[1]=v>>>16&255,p[2]=v>>>8&255,p[3]=255&v,p.set(l.default.types.mdat,4));for(var K=0;K>>24&255,h[1]=d>>>16&255,h[2]=d>>>8&255,h[3]=255&d,h.set(l.default.types.mdat,4);var f=n[0].dts-this._dtsBase;if(this._videoNextDts)i=f-this._videoNextDts;else if(this._videoSegmentInfoList.isEmpty())i=0;else{var c=this._videoSegmentInfoList.getLastSampleBefore(f);if(null!=c){var m=f-(c.originalDts+c.duration);m<=3&&(m=0);var p=c.dts+c.duration+m;i=f-p}else i=0}for(var v=new _.MediaSegmentInfo,g=[],y=0;y=1?g[g.length-1].duration:Math.floor(this._videoMeta.refSampleDuration);if(S){var A=new _.SampleInfo(k,w,R,E.dts,!0);A.fileposition=E.fileposition,v.appendSyncPoint(A)}g.push({dts:k,pts:w,cts:L,units:E.units,size:E.length,isKeyframe:S,duration:R,originalDts:b,flags:{isLeading:0,dependsOn:S?2:1,isDependedOn:S?1:0,hasRedundancy:0,isNonSync:S?0:1}})}for(var O=0;O=0&&/(rv)(?::| )([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(firefox)[ \/]([\w.]+)/.exec(e)||[],n=/(ipad)/.exec(e)||/(ipod)/.exec(e)||/(windows phone)/.exec(e)||/(iphone)/.exec(e)||/(kindle)/.exec(e)||/(android)/.exec(e)||/(windows)/.exec(e)||/(mac)/.exec(e)||/(linux)/.exec(e)||/(cros)/.exec(e)||[],r={browser:t[5]||t[3]||t[1]||"",version:t[2]||t[4]||"0",majorVersion:t[4]||t[2]||"0",platform:n[0]||""},s={};if(r.browser){s[r.browser]=!0;var a=r.majorVersion.split(".");s.version={major:parseInt(r.majorVersion,10),string:r.version},a.length>1&&(s.version.minor=parseInt(a[1],10)),a.length>2&&(s.version.build=parseInt(a[2],10))}r.platform&&(s[r.platform]=!0),(s.chrome||s.opr||s.safari)&&(s.webkit=!0),(s.rv||s.iemobile)&&(s.rv&&delete s.rv,r.browser="msie",s.msie=!0),s.edge&&(delete s.edge,r.browser="msedge",s.msedge=!0),s.opr&&(r.browser="opera",s.opera=!0),s.safari&&s.android&&(r.browser="android", -s.android=!0),s.name=r.browser,s.platform=r.platform;for(var o in i)i.hasOwnProperty(o)&&delete i[o];Object.assign(i,s)}(),n.default=i},{}],40:[function(e,t,n){"use strict";function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function r(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;n "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","error",i),e.ENABLE_ERROR&&(console.error?console.error(i):console.warn?console.warn(i):console.log(i))}},{key:"i",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","info",i),e.ENABLE_INFO&&(console.info?console.info(i):console.log(i))}},{key:"w",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","warn",i),e.ENABLE_WARN&&(console.warn?console.warn(i):console.log(i))}},{key:"d",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","debug",i),e.ENABLE_DEBUG&&(console.debug?console.debug(i):console.log(i))}},{key:"v",value:function(t,n){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var i="["+t+"] > "+n;e.ENABLE_CALLBACK&&e.emitter.emit("log","verbose",i),e.ENABLE_VERBOSE&&console.log(i)}}]),e}();o.GLOBAL_TAG="flv.js",o.FORCE_GLOBAL_TAG=!1,o.ENABLE_ERROR=!0,o.ENABLE_INFO=!0,o.ENABLE_WARN=!0,o.ENABLE_DEBUG=!0,o.ENABLE_VERBOSE=!0,o.ENABLE_CALLBACK=!1,o.emitter=new a.default,n.default=o},{events:2}],42:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0){var n=e.getConfig();t.emit("change",n)}}},{key:"registerListener",value:function(t){e.emitter.addListener("change",t)}},{key:"removeListener",value:function(t){e.emitter.removeListener("change",t)}},{key:"addLogListener",value:function(t){l.default.emitter.addListener("log",t),l.default.emitter.listenerCount("log")>0&&(l.default.ENABLE_CALLBACK=!0,e._notifyChange())}},{key:"removeLogListener",value:function(t){l.default.emitter.removeListener("log",t),0===l.default.emitter.listenerCount("log")&&(l.default.ENABLE_CALLBACK=!1,e._notifyChange())}},{key:"forceGlobalTag",get:function(){return l.default.FORCE_GLOBAL_TAG},set:function(t){l.default.FORCE_GLOBAL_TAG=t,e._notifyChange()}},{key:"globalTag",get:function(){return l.default.GLOBAL_TAG},set:function(t){l.default.GLOBAL_TAG=t,e._notifyChange()}},{key:"enableAll",get:function(){return l.default.ENABLE_VERBOSE&&l.default.ENABLE_DEBUG&&l.default.ENABLE_INFO&&l.default.ENABLE_WARN&&l.default.ENABLE_ERROR},set:function(t){l.default.ENABLE_VERBOSE=t,l.default.ENABLE_DEBUG=t,l.default.ENABLE_INFO=t,l.default.ENABLE_WARN=t,l.default.ENABLE_ERROR=t,e._notifyChange()}},{key:"enableDebug",get:function(){return l.default.ENABLE_DEBUG},set:function(t){l.default.ENABLE_DEBUG=t,e._notifyChange()}},{key:"enableVerbose",get:function(){return l.default.ENABLE_VERBOSE},set:function(t){l.default.ENABLE_VERBOSE=t,e._notifyChange()}},{key:"enableInfo",get:function(){return l.default.ENABLE_INFO},set:function(t){l.default.ENABLE_INFO=t,e._notifyChange()}},{key:"enableWarn",get:function(){return l.default.ENABLE_WARN},set:function(t){l.default.ENABLE_WARN=t,e._notifyChange()}},{key:"enableError",get:function(){return l.default.ENABLE_ERROR},set:function(t){l.default.ENABLE_ERROR=t,e._notifyChange()}}]),e}();d.emitter=new o.default,n.default=d},{"./logger.js":41,events:2}],43:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n=128){t.push(String.fromCharCode(65535&a)),r+=2;continue}}}else if(n[r]<240){if(i(n,r,2)){var o=(15&n[r])<<12|(63&n[r+1])<<6|63&n[r+2];if(o>=2048&&55296!=(63488&o)){t.push(String.fromCharCode(65535&o)),r+=3;continue}}}else if(n[r]<248&&i(n,r,3)){var u=(7&n[r])<<18|(63&n[r+1])<<12|(63&n[r+2])<<6|63&n[r+3];if(u>65536&&u<1114112){u-=65536,t.push(String.fromCharCode(u>>>10|55296)),t.push(String.fromCharCode(1023&u|56320)),r+=4;continue}}t.push(String.fromCharCode(65533)),++r}return t.join("")}Object.defineProperty(n,"__esModule",{value:!0}),n.default=r},{}]},{},[21])(21)}); +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flvjs=t():e.flvjs=t()}(self,(function(){return function(){var e={480:function(e,t,i){ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.8+1e68dce6 + */ +e.exports=function(){"use strict";function e(e){var t=typeof e;return null!==e&&("object"===t||"function"===t)}function t(e){return"function"==typeof e}var n=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)},r=0,s=void 0,o=void 0,a=function(e,t){A[r]=e,A[r+1]=t,2===(r+=2)&&(o?o(b):S())};function h(e){o=e}function u(e){a=e}var l="undefined"!=typeof window?window:void 0,d=l||{},c=d.MutationObserver||d.WebKitMutationObserver,_="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),f="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function p(){return function(){return process.nextTick(b)}}function m(){return void 0!==s?function(){s(b)}:y()}function g(){var e=0,t=new c(b),i=document.createTextNode("");return t.observe(i,{characterData:!0}),function(){i.data=e=++e%2}}function v(){var e=new MessageChannel;return e.port1.onmessage=b,function(){return e.port2.postMessage(0)}}function y(){var e=setTimeout;return function(){return e(b,1)}}var A=new Array(1e3);function b(){for(var e=0;e0&&o.length>r&&!o.warned){o.warned=!0;var l=new Error("Possible EventEmitter memory leak detected. "+o.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");l.name="MaxListenersExceededWarning",l.emitter=e,l.type=t,l.count=o.length,u=l,console&&console.warn&&console.warn(u)}return e}function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function d(e,t,i){var n={fired:!1,wrapFn:void 0,target:e,type:t,listener:i},r=l.bind(n);return r.listener=i,n.wrapFn=r,r}function c(e,t,i){var n=e._events;if(void 0===n)return[];var r=n[t];return void 0===r?[]:"function"==typeof r?i?[r.listener||r]:[r]:i?function(e){for(var t=new Array(e.length),i=0;i0&&(o=t[0]),o instanceof Error)throw o;var a=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw a.context=o,a}var h=s[e];if(void 0===h)return!1;if("function"==typeof h)n(h,this,t);else{var u=h.length,l=f(h,u);for(i=0;i=0;s--)if(i[s]===t||i[s].listener===t){o=i[s].listener,r=s;break}if(r<0)return this;0===r?i.shift():function(e,t){for(;t+1=0;n--)this.removeListener(e,t[n]);return this},s.prototype.listeners=function(e){return c(this,e,!0)},s.prototype.rawListeners=function(e){return c(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):_.call(e,t)},s.prototype.listenerCount=_,s.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},348:function(e,t,i){function n(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=t,i.i=function(e){return e},i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},i.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/",i.oe=function(e){throw console.error(e),e};var n=i(i.s=ENTRY_MODULE);return n.default||n}var r="[\\.|\\-|\\+|\\w|/|@]+",s="\\(\\s*(/\\*.*?\\*/)?\\s*.*?("+r+").*?\\)";function o(e){return(e+"").replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}function a(e,t,n){var a={};a[n]=[];var h=t.toString(),u=h.match(/^function\s?\w*\(\w+,\s*\w+,\s*(\w+)\)/);if(!u)return a;for(var l,d=u[1],c=new RegExp("(\\\\n|\\W)"+o(d)+s,"g");l=c.exec(h);)"dll-reference"!==l[3]&&a[n].push(l[3]);for(c=new RegExp("\\("+o(d)+'\\("(dll-reference\\s('+r+'))"\\)\\)'+s,"g");l=c.exec(h);)e[l[2]]||(a[n].push(l[1]),e[l[2]]=i(l[1]).m),a[l[2]]=a[l[2]]||[],a[l[2]].push(l[4]);for(var _,f=Object.keys(a),p=0;p0}),!1)}e.exports=function(e,t){t=t||{};var r={main:i.m},s=t.all?{main:Object.keys(r.main)}:function(e,t){for(var i={main:[t]},n={main:[]},r={main:{}};h(i);)for(var s=Object.keys(i),o=0;o=e[r]&&t0&&e[0].originalDts=t[r].dts&&et[n].lastSample.originalDts&&e=t[n].lastSample.originalDts&&(n===t.length-1||n0&&(r=this._searchNearestSegmentBefore(i.originalBeginDts)+1),this._lastAppendLocation=r,this._list.splice(r,0,i)},e.prototype.getLastSegmentBefore=function(e){var t=this._searchNearestSegmentBefore(e);return t>=0?this._list[t]:null},e.prototype.getLastSampleBefore=function(e){var t=this.getLastSegmentBefore(e);return null!=t?t.lastSample:null},e.prototype.getLastSyncPointBefore=function(e){for(var t=this._searchNearestSegmentBefore(e),i=this._list[t].syncPoints;0===i.length&&t>0;)t--,i=this._list[t].syncPoints;return i.length>0?i[i.length-1]:null},e}()},214:function(e,t,i){"use strict";i.d(t,{A:function(){return w}});var n=i(211),r=i.n(n),s=i(856),o=i(994),a=i(955);function h(e,t,i){var n=e;if(t+i=128){t.push(String.fromCharCode(65535&s)),n+=2;continue}}else if(i[n]<240){if(h(i,n,2))if((s=(15&i[n])<<12|(63&i[n+1])<<6|63&i[n+2])>=2048&&55296!=(63488&s)){t.push(String.fromCharCode(65535&s)),n+=3;continue}}else if(i[n]<248){var s;if(h(i,n,3))if((s=(7&i[n])<<18|(63&i[n+1])<<12|(63&i[n+2])<<6|63&i[n+3])>65536&&s<1114112){s-=65536,t.push(String.fromCharCode(s>>>10|55296)),t.push(String.fromCharCode(1023&s|56320)),n+=4;continue}}t.push(String.fromCharCode(65533)),++n}return t.join("")},d=i(867),c=(u=new ArrayBuffer(2),new DataView(u).setInt16(0,256,!0),256===new Int16Array(u)[0]),_=function(){function e(){}return e.parseScriptData=function(t,i,n){var r={};try{var o=e.parseValue(t,i,n),a=e.parseValue(t,i+o.size,n-o.size);r[o.data]=a.data}catch(e){s.A.e("AMF",e.toString())}return r},e.parseObject=function(t,i,n){if(n<3)throw new d.j4("Data not enough when parse ScriptDataObject");var r=e.parseString(t,i,n),s=e.parseValue(t,i+r.size,n-r.size),o=s.objectEnd;return{data:{name:r.data,value:s.data},size:r.size+s.size,objectEnd:o}},e.parseVariable=function(t,i,n){return e.parseObject(t,i,n)},e.parseString=function(e,t,i){if(i<2)throw new d.j4("Data not enough when parse String");var n=new DataView(e,t,i).getUint16(0,!c);return{data:n>0?l(new Uint8Array(e,t+2,n)):"",size:2+n}},e.parseLongString=function(e,t,i){if(i<4)throw new d.j4("Data not enough when parse LongString");var n=new DataView(e,t,i).getUint32(0,!c);return{data:n>0?l(new Uint8Array(e,t+4,n)):"",size:4+n}},e.parseDate=function(e,t,i){if(i<10)throw new d.j4("Data size invalid when parse Date");var n=new DataView(e,t,i),r=n.getFloat64(0,!c),s=n.getInt16(8,!c);return{data:new Date(r+=60*s*1e3),size:10}},e.parseValue=function(t,i,n){if(n<1)throw new d.j4("Data not enough when parse Value");var r,o=new DataView(t,i,n),a=1,h=o.getUint8(0),u=!1;try{switch(h){case 0:r=o.getFloat64(1,!c),a+=8;break;case 1:r=!!o.getUint8(1),a+=1;break;case 2:var l=e.parseString(t,i+1,n-1);r=l.data,a+=l.size;break;case 3:r={};var _=0;for(9==(16777215&o.getUint32(n-4,!c))&&(_=3);a32)throw new d.Qn("ExpGolomb: readBits() bits exceeded max 32bits!");if(e<=this._current_word_bits_left){var t=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t}var i=this._current_word_bits_left?this._current_word:0;i>>>=32-this._current_word_bits_left;var n=e-this._current_word_bits_left;this._fillCurrentWord();var r=Math.min(n,this._current_word_bits_left),s=this._current_word>>>32-r;return this._current_word<<=r,this._current_word_bits_left-=r,i=i<>>e))return this._current_word<<=e,this._current_word_bits_left-=e,e;return this._fillCurrentWord(),e+this._skipLeadingZero()},e.prototype.readUEG=function(){var e=this._skipLeadingZero();return this.readBits(e+1)-1},e.prototype.readSEG=function(){var e=this.readUEG();return 1&e?e+1>>>1:-1*(e>>>1)},e}(),p=function(){function e(){}return e._ebsp2rbsp=function(e){for(var t=e,i=t.byteLength,n=new Uint8Array(i),r=0,s=0;s=2&&3===t[s]&&0===t[s-1]&&0===t[s-2]||(n[r]=t[s],r++);return new Uint8Array(n.buffer,0,r)},e.parseSPS=function(t){var i=e._ebsp2rbsp(t),n=new f(i);n.readByte();var r=n.readByte();n.readByte();var s=n.readByte();n.readUEG();var o=e.getProfileString(r),a=e.getLevelString(s),h=1,u=420,l=8;if((100===r||110===r||122===r||244===r||44===r||83===r||86===r||118===r||128===r||138===r||144===r)&&(3===(h=n.readUEG())&&n.readBits(1),h<=3&&(u=[0,420,422,444][h]),l=n.readUEG()+8,n.readUEG(),n.readBits(1),n.readBool()))for(var d=3!==h?8:12,c=0;c0&&k<16?(L=[1,12,10,16,40,24,20,32,80,18,15,64,160,4,3,2][k-1],R=[1,11,11,11,33,11,11,11,33,11,11,33,99,3,2,1][k-1]):255===k&&(L=n.readByte()<<8|n.readByte(),R=n.readByte()<<8|n.readByte())}if(n.readBool()&&n.readBool(),n.readBool()&&(n.readBits(4),n.readBool()&&n.readBits(24)),n.readBool()&&(n.readUEG(),n.readUEG()),n.readBool()){var D=n.readBits(32),I=n.readBits(32);O=n.readBool(),w=(T=I)/(C=2*D)}}var B=1;1===L&&1===R||(B=L/R);var M=0,x=0;0===h?(M=1,x=2-y):(M=3===h?1:2,x=(1===h?2:1)*(2-y));var P=16*(g+1),U=16*(v+1)*(2-y);P-=(A+b)*M,U-=(E+S)*x;var N=Math.ceil(P*B);return n.destroy(),n=null,{profile_string:o,level_string:a,bit_depth:l,ref_frames:m,chroma_format:u,chroma_format_string:e.getChromaFormatString(u),frame_rate:{fixed:O,fps:w,fps_den:C,fps_num:T},sar_ratio:{width:L,height:R},codec_size:{width:P,height:U},present_size:{width:N,height:U}}},e._skipScalingList=function(e,t){for(var i=8,n=8,r=0;r>>2!=0,o=0!=(1&t[4]),a=(n=t)[r=5]<<24|n[r+1]<<16|n[r+2]<<8|n[r+3];return a<9?i:{match:!0,consumed:a,dataOffset:a,hasAudioTrack:s,hasVideoTrack:o}},e.prototype.bindDataSource=function(e){return e.onDataArrival=this.parseChunks.bind(this),this},Object.defineProperty(e.prototype,"onTrackMetadata",{get:function(){return this._onTrackMetadata},set:function(e){this._onTrackMetadata=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onMediaInfo",{get:function(){return this._onMediaInfo},set:function(e){this._onMediaInfo=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onMetaDataArrived",{get:function(){return this._onMetaDataArrived},set:function(e){this._onMetaDataArrived=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onScriptDataArrived",{get:function(){return this._onScriptDataArrived},set:function(e){this._onScriptDataArrived=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onError",{get:function(){return this._onError},set:function(e){this._onError=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onDataAvailable",{get:function(){return this._onDataAvailable},set:function(e){this._onDataAvailable=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"timestampBase",{get:function(){return this._timestampBase},set:function(e){this._timestampBase=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"overridedDuration",{get:function(){return this._duration},set:function(e){this._durationOverrided=!0,this._duration=e,this._mediaInfo.duration=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"overridedHasAudio",{set:function(e){this._hasAudioFlagOverrided=!0,this._hasAudio=e,this._mediaInfo.hasAudio=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"overridedHasVideo",{set:function(e){this._hasVideoFlagOverrided=!0,this._hasVideo=e,this._mediaInfo.hasVideo=e},enumerable:!1,configurable:!0}),e.prototype.resetMediaInfo=function(){this._mediaInfo=new a.A},e.prototype._isInitialMetadataDispatched=function(){return this._hasAudio&&this._hasVideo?this._audioInitialMetadataDispatched&&this._videoInitialMetadataDispatched:this._hasAudio&&!this._hasVideo?this._audioInitialMetadataDispatched:!(this._hasAudio||!this._hasVideo)&&this._videoInitialMetadataDispatched},e.prototype.parseChunks=function(t,i){if(!(this._onError&&this._onMediaInfo&&this._onTrackMetadata&&this._onDataAvailable))throw new d.j4("Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified");var n=0,r=this._littleEndian;if(0===i){if(!(t.byteLength>13))return 0;n=e.probe(t).dataOffset}this._firstParse&&(this._firstParse=!1,i+n!==this._dataOffset&&s.A.w(this.TAG,"First time parsing but chunk byteStart invalid!"),0!==(o=new DataView(t,n)).getUint32(0,!r)&&s.A.w(this.TAG,"PrevTagSize0 !== 0 !!!"),n+=4);for(;nt.byteLength)break;var a=o.getUint8(0),h=16777215&o.getUint32(0,!r);if(n+11+h+4>t.byteLength)break;if(8===a||9===a||18===a){var u=o.getUint8(4),l=o.getUint8(5),c=o.getUint8(6)|l<<8|u<<16|o.getUint8(7)<<24;0!==(16777215&o.getUint32(7,!r))&&s.A.w(this.TAG,"Meet tag which has StreamID != 0!");var _=n+11;switch(a){case 8:this._parseAudioData(t,_,h,c);break;case 9:this._parseVideoData(t,_,h,c,i+n);break;case 18:this._parseScriptData(t,_,h)}var f=o.getUint32(11+h,!r);f!==11+h&&s.A.w(this.TAG,"Invalid PrevTagSize "+f),n+=11+h+4}else s.A.w(this.TAG,"Unsupported tag type "+a+", skipped"),n+=11+h+4}return this._isInitialMetadataDispatched()&&this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack),n},e.prototype._parseScriptData=function(e,t,i){var n=_.parseScriptData(e,t,i);if(n.hasOwnProperty("onMetaData")){if(null==n.onMetaData||"object"!=typeof n.onMetaData)return void s.A.w(this.TAG,"Invalid onMetaData structure!");this._metadata&&s.A.w(this.TAG,"Found another onMetaData tag!"),this._metadata=n;var r=this._metadata.onMetaData;if(this._onMetaDataArrived&&this._onMetaDataArrived(Object.assign({},r)),"boolean"==typeof r.hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=r.hasAudio,this._mediaInfo.hasAudio=this._hasAudio),"boolean"==typeof r.hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=r.hasVideo,this._mediaInfo.hasVideo=this._hasVideo),"number"==typeof r.audiodatarate&&(this._mediaInfo.audioDataRate=r.audiodatarate),"number"==typeof r.videodatarate&&(this._mediaInfo.videoDataRate=r.videodatarate),"number"==typeof r.width&&(this._mediaInfo.width=r.width),"number"==typeof r.height&&(this._mediaInfo.height=r.height),"number"==typeof r.duration){if(!this._durationOverrided){var o=Math.floor(r.duration*this._timescale);this._duration=o,this._mediaInfo.duration=o}}else this._mediaInfo.duration=0;if("number"==typeof r.framerate){var a=Math.floor(1e3*r.framerate);if(a>0){var h=a/1e3;this._referenceFrameRate.fixed=!0,this._referenceFrameRate.fps=h,this._referenceFrameRate.fps_num=a,this._referenceFrameRate.fps_den=1e3,this._mediaInfo.fps=h}}if("object"==typeof r.keyframes){this._mediaInfo.hasKeyframesIndex=!0;var u=r.keyframes;this._mediaInfo.keyframesIndex=this._parseKeyframesIndex(u),r.keyframes=null}else this._mediaInfo.hasKeyframesIndex=!1;this._dispatch=!1,this._mediaInfo.metadata=r,s.A.v(this.TAG,"Parsed onMetaData"),this._mediaInfo.isComplete()&&this._onMediaInfo(this._mediaInfo)}Object.keys(n).length>0&&this._onScriptDataArrived&&this._onScriptDataArrived(Object.assign({},n))},e.prototype._parseKeyframesIndex=function(e){for(var t=[],i=[],n=1;n>>4;if(2===o||10===o){var a=0,h=(12&r)>>>2;if(h>=0&&h<=4){a=this._flvSoundRateTable[h];var u=1&r,l=this._audioMetadata,d=this._audioTrack;if(l||(!1===this._hasAudio&&!1===this._hasAudioFlagOverrided&&(this._hasAudio=!0,this._mediaInfo.hasAudio=!0),(l=this._audioMetadata={}).type="audio",l.id=d.id,l.timescale=this._timescale,l.duration=this._duration,l.audioSampleRate=a,l.channelCount=0===u?1:2),10===o){var c=this._parseAACAudioData(e,t+1,i-1);if(null==c)return;if(0===c.packetType){l.config&&s.A.w(this.TAG,"Found another AudioSpecificConfig!");var _=c.data;l.audioSampleRate=_.samplingRate,l.channelCount=_.channelCount,l.codec=_.codec,l.originalCodec=_.originalCodec,l.config=_.config,l.refSampleDuration=1024/l.audioSampleRate*l.timescale,s.A.v(this.TAG,"Parsed AudioSpecificConfig"),this._isInitialMetadataDispatched()?this._dispatch&&(this._audioTrack.length||this._videoTrack.length)&&this._onDataAvailable(this._audioTrack,this._videoTrack):this._audioInitialMetadataDispatched=!0,this._dispatch=!1,this._onTrackMetadata("audio",l),(g=this._mediaInfo).audioCodec=l.originalCodec,g.audioSampleRate=l.audioSampleRate,g.audioChannelCount=l.channelCount,g.hasVideo?null!=g.videoCodec&&(g.mimeType='video/x-flv; codecs="'+g.videoCodec+","+g.audioCodec+'"'):g.mimeType='video/x-flv; codecs="'+g.audioCodec+'"',g.isComplete()&&this._onMediaInfo(g)}else if(1===c.packetType){var f=this._timestampBase+n,p={unit:c.data,length:c.data.byteLength,dts:f,pts:f};d.samples.push(p),d.length+=c.data.length}else s.A.e(this.TAG,"Flv: Unsupported AAC data type "+c.packetType)}else if(2===o){if(!l.codec){var g;if(null==(_=this._parseMP3AudioData(e,t+1,i-1,!0)))return;l.audioSampleRate=_.samplingRate,l.channelCount=_.channelCount,l.codec=_.codec,l.originalCodec=_.originalCodec,l.refSampleDuration=1152/l.audioSampleRate*l.timescale,s.A.v(this.TAG,"Parsed MPEG Audio Frame Header"),this._audioInitialMetadataDispatched=!0,this._onTrackMetadata("audio",l),(g=this._mediaInfo).audioCodec=l.codec,g.audioSampleRate=l.audioSampleRate,g.audioChannelCount=l.channelCount,g.audioDataRate=_.bitRate,g.hasVideo?null!=g.videoCodec&&(g.mimeType='video/x-flv; codecs="'+g.videoCodec+","+g.audioCodec+'"'):g.mimeType='video/x-flv; codecs="'+g.audioCodec+'"',g.isComplete()&&this._onMediaInfo(g)}var v=this._parseMP3AudioData(e,t+1,i-1,!1);if(null==v)return;f=this._timestampBase+n;var y={unit:v,length:v.byteLength,dts:f,pts:f};d.samples.push(y),d.length+=v.length}}else this._onError(m.A.FORMAT_ERROR,"Flv: Invalid audio sample rate idx: "+h)}else this._onError(m.A.CODEC_UNSUPPORTED,"Flv: Unsupported audio codec idx: "+o)}},e.prototype._parseAACAudioData=function(e,t,i){if(!(i<=1)){var n={},r=new Uint8Array(e,t,i);return n.packetType=r[0],0===r[0]?n.data=this._parseAACAudioSpecificConfig(e,t+1,i-1):n.data=r.subarray(1),n}s.A.w(this.TAG,"Flv: Invalid AAC packet, missing AACPacketType or/and Data!")},e.prototype._parseAACAudioSpecificConfig=function(e,t,i){var n,r,s=new Uint8Array(e,t,i),o=null,a=0,h=null;if(a=n=s[0]>>>3,(r=(7&s[0])<<1|s[1]>>>7)<0||r>=this._mpegSamplingRates.length)this._onError(m.A.FORMAT_ERROR,"Flv: AAC invalid sampling frequency index!");else{var u=this._mpegSamplingRates[r],l=(120&s[1])>>>3;if(!(l<0||l>=8)){5===a&&(h=(7&s[1])<<1|s[2]>>>7,(124&s[2])>>>2);var d=self.navigator.userAgent.toLowerCase();return-1!==d.indexOf("firefox")?r>=6?(a=5,o=new Array(4),h=r-3):(a=2,o=new Array(2),h=r):-1!==d.indexOf("android")?(a=2,o=new Array(2),h=r):(a=5,h=r,o=new Array(4),r>=6?h=r-3:1===l&&(a=2,o=new Array(2),h=r)),o[0]=a<<3,o[0]|=(15&r)>>>1,o[1]=(15&r)<<7,o[1]|=(15&l)<<3,5===a&&(o[1]|=(15&h)>>>1,o[2]=(1&h)<<7,o[2]|=8,o[3]=0),{config:o,samplingRate:u,channelCount:l,codec:"mp4a.40."+a,originalCodec:"mp4a.40."+n}}this._onError(m.A.FORMAT_ERROR,"Flv: AAC invalid channel configuration")}},e.prototype._parseMP3AudioData=function(e,t,i,n){if(!(i<4)){this._littleEndian;var r=new Uint8Array(e,t,i),o=null;if(n){if(255!==r[0])return;var a=r[1]>>>3&3,h=(6&r[1])>>1,u=(240&r[2])>>>4,l=(12&r[2])>>>2,d=3!==(r[3]>>>6&3)?2:1,c=0,_=0;switch(a){case 0:c=this._mpegAudioV25SampleRateTable[l];break;case 2:c=this._mpegAudioV20SampleRateTable[l];break;case 3:c=this._mpegAudioV10SampleRateTable[l]}switch(h){case 1:34,u>>4,h=15&o;7===h?this._parseAVCVideoPacket(e,t+1,i-1,n,r,a):this._onError(m.A.CODEC_UNSUPPORTED,"Flv: Unsupported codec in video frame: "+h)}},e.prototype._parseAVCVideoPacket=function(e,t,i,n,r,o){if(i<4)s.A.w(this.TAG,"Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime");else{var a=this._littleEndian,h=new DataView(e,t,i),u=h.getUint8(0),l=(16777215&h.getUint32(0,!a))<<8>>8;if(0===u)this._parseAVCDecoderConfigurationRecord(e,t+4,i-4);else if(1===u)this._parseAVCVideoData(e,t+4,i-4,n,r,o,l);else if(2!==u)return void this._onError(m.A.FORMAT_ERROR,"Flv: Invalid video packet type "+u)}},e.prototype._parseAVCDecoderConfigurationRecord=function(e,t,i){if(i<7)s.A.w(this.TAG,"Flv: Invalid AVCDecoderConfigurationRecord, lack of data!");else{var n=this._videoMetadata,r=this._videoTrack,o=this._littleEndian,a=new DataView(e,t,i);n?void 0!==n.avcc&&s.A.w(this.TAG,"Found another AVCDecoderConfigurationRecord!"):(!1===this._hasVideo&&!1===this._hasVideoFlagOverrided&&(this._hasVideo=!0,this._mediaInfo.hasVideo=!0),(n=this._videoMetadata={}).type="video",n.id=r.id,n.timescale=this._timescale,n.duration=this._duration);var h=a.getUint8(0),u=a.getUint8(1);a.getUint8(2),a.getUint8(3);if(1===h&&0!==u)if(this._naluLengthSize=1+(3&a.getUint8(4)),3===this._naluLengthSize||4===this._naluLengthSize){var l=31&a.getUint8(5);if(0!==l){l>1&&s.A.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: SPS Count = "+l);for(var d=6,c=0;c1&&s.A.w(this.TAG,"Flv: Strange AVCDecoderConfigurationRecord: PPS Count = "+R),d++;for(c=0;c=i){s.A.w(this.TAG,"Malformed Nalu near timestamp "+f+", offset = "+c+", dataSize = "+i);break}var m=u.getUint32(c,!h);if(3===_&&(m>>>=8),m>i-_)return void s.A.w(this.TAG,"Malformed Nalus near timestamp "+f+", NaluSize > DataSize!");var g=31&u.getUint8(c+_);5===g&&(p=!0);var v=new Uint8Array(e,t+c,_+m),y={type:g,data:v};l.push(y),d+=v.byteLength,c+=_+m}if(l.length){var A=this._videoTrack,b={units:l,length:d,isKeyframe:p,dts:f,cts:a,pts:f+a};p&&(b.fileposition=r),A.samples.push(b),A.length+=d}},e}(),v=g,y=function(){function e(){}return e.init=function(){for(var t in e.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":[]},e.types)e.types.hasOwnProperty(t)&&(e.types[t]=[t.charCodeAt(0),t.charCodeAt(1),t.charCodeAt(2),t.charCodeAt(3)]);var i=e.constants={};i.FTYP=new Uint8Array([105,115,111,109,0,0,0,1,105,115,111,109,97,118,99,49]),i.STSD_PREFIX=new Uint8Array([0,0,0,0,0,0,0,1]),i.STTS=new Uint8Array([0,0,0,0,0,0,0,0]),i.STSC=i.STCO=i.STTS,i.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),i.HDLR_VIDEO=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),i.HDLR_AUDIO=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]),i.DREF=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),i.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),i.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0])},e.box=function(e){for(var t=8,i=null,n=Array.prototype.slice.call(arguments,1),r=n.length,s=0;s>>24&255,i[1]=t>>>16&255,i[2]=t>>>8&255,i[3]=255&t,i.set(e,4);var o=8;for(s=0;s>>24&255,t>>>16&255,t>>>8&255,255&t,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]))},e.trak=function(t){return e.box(e.types.trak,e.tkhd(t),e.mdia(t))},e.tkhd=function(t){var i=t.id,n=t.duration,r=t.presentWidth,s=t.presentHeight;return e.box(e.types.tkhd,new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,r>>>8&255,255&r,0,0,s>>>8&255,255&s,0,0]))},e.mdia=function(t){return e.box(e.types.mdia,e.mdhd(t),e.hdlr(t),e.minf(t))},e.mdhd=function(t){var i=t.timescale,n=t.duration;return e.box(e.types.mdhd,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,n>>>24&255,n>>>16&255,n>>>8&255,255&n,85,196,0,0]))},e.hdlr=function(t){var i=null;return i="audio"===t.type?e.constants.HDLR_AUDIO:e.constants.HDLR_VIDEO,e.box(e.types.hdlr,i)},e.minf=function(t){var i=null;return i="audio"===t.type?e.box(e.types.smhd,e.constants.SMHD):e.box(e.types.vmhd,e.constants.VMHD),e.box(e.types.minf,i,e.dinf(),e.stbl(t))},e.dinf=function(){return e.box(e.types.dinf,e.box(e.types.dref,e.constants.DREF))},e.stbl=function(t){return e.box(e.types.stbl,e.stsd(t),e.box(e.types.stts,e.constants.STTS),e.box(e.types.stsc,e.constants.STSC),e.box(e.types.stsz,e.constants.STSZ),e.box(e.types.stco,e.constants.STCO))},e.stsd=function(t){return"audio"===t.type?"mp3"===t.codec?e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp3(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.mp4a(t)):e.box(e.types.stsd,e.constants.STSD_PREFIX,e.avc1(t))},e.mp3=function(t){var i=t.channelCount,n=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,i,0,16,0,0,0,0,n>>>8&255,255&n,0,0]);return e.box(e.types[".mp3"],r)},e.mp4a=function(t){var i=t.channelCount,n=t.audioSampleRate,r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,i,0,16,0,0,0,0,n>>>8&255,255&n,0,0]);return e.box(e.types.mp4a,r,e.esds(t))},e.esds=function(t){var i=t.config||[],n=i.length,r=new Uint8Array([0,0,0,0,3,23+n,0,1,0,4,15+n,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([n]).concat(i).concat([6,1,2]));return e.box(e.types.esds,r)},e.avc1=function(t){var i=t.avcc,n=t.codecWidth,r=t.codecHeight,s=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,n>>>8&255,255&n,r>>>8&255,255&r,0,72,0,0,0,72,0,0,0,0,0,0,0,1,10,120,113,113,47,102,108,118,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255]);return e.box(e.types.avc1,s,e.box(e.types.avcC,i))},e.mvex=function(t){return e.box(e.types.mvex,e.trex(t))},e.trex=function(t){var i=t.id,n=new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return e.box(e.types.trex,n)},e.moof=function(t,i){return e.box(e.types.moof,e.mfhd(t.sequenceNumber),e.traf(t,i))},e.mfhd=function(t){var i=new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t]);return e.box(e.types.mfhd,i)},e.traf=function(t,i){var n=t.id,r=e.box(e.types.tfhd,new Uint8Array([0,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n])),s=e.box(e.types.tfdt,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),o=e.sdtp(t),a=e.trun(t,o.byteLength+16+16+8+16+8+8);return e.box(e.types.traf,r,s,a,o)},e.sdtp=function(t){for(var i=t.samples||[],n=i.length,r=new Uint8Array(4+n),s=0;s>>24&255,r>>>16&255,r>>>8&255,255&r,i>>>24&255,i>>>16&255,i>>>8&255,255&i],0);for(var a=0;a>>24&255,h>>>16&255,h>>>8&255,255&h,u>>>24&255,u>>>16&255,u>>>8&255,255&u,l.isLeading<<2|l.dependsOn,l.isDependedOn<<6|l.hasRedundancy<<4|l.isNonSync,0,0,d>>>24&255,d>>>16&255,d>>>8&255,255&d],12+16*a)}return e.box(e.types.trun,o)},e.mdat=function(t){return e.box(e.types.mdat,t)},e}();y.init();var A=y,b=function(){function e(){}return e.getSilentFrame=function(e,t){if("mp4a.40.2"===e){if(1===t)return new Uint8Array([0,200,0,128,35,128]);if(2===t)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(3===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(4===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(5===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(6===t)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224])}else{if(1===t)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(2===t)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(3===t)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94])}return null},e}(),E=i(47),S=function(){function e(e){this.TAG="MP4Remuxer",this._config=e,this._isLive=!0===e.isLive,this._dtsBase=-1,this._dtsBaseInited=!1,this._audioDtsBase=1/0,this._videoDtsBase=1/0,this._audioNextDts=void 0,this._videoNextDts=void 0,this._audioStashedLastSample=null,this._videoStashedLastSample=null,this._audioMeta=null,this._videoMeta=null,this._audioSegmentInfoList=new E.Sc("audio"),this._videoSegmentInfoList=new E.Sc("video"),this._onInitSegment=null,this._onMediaSegment=null,this._forceFirstIDR=!(!o.A.chrome||!(o.A.version.major<50||50===o.A.version.major&&o.A.version.build<2661)),this._fillSilentAfterSeek=o.A.msedge||o.A.msie,this._mp3UseMpegAudio=!o.A.firefox,this._fillAudioTimestampGap=this._config.fixAudioTimestampGap}return e.prototype.destroy=function(){this._dtsBase=-1,this._dtsBaseInited=!1,this._audioMeta=null,this._videoMeta=null,this._audioSegmentInfoList.clear(),this._audioSegmentInfoList=null,this._videoSegmentInfoList.clear(),this._videoSegmentInfoList=null,this._onInitSegment=null,this._onMediaSegment=null},e.prototype.bindDataSource=function(e){return e.onDataAvailable=this.remux.bind(this),e.onTrackMetadata=this._onTrackMetadataReceived.bind(this),this},Object.defineProperty(e.prototype,"onInitSegment",{get:function(){return this._onInitSegment},set:function(e){this._onInitSegment=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onMediaSegment",{get:function(){return this._onMediaSegment},set:function(e){this._onMediaSegment=e},enumerable:!1,configurable:!0}),e.prototype.insertDiscontinuity=function(){this._audioNextDts=this._videoNextDts=void 0},e.prototype.seek=function(e){this._audioStashedLastSample=null,this._videoStashedLastSample=null,this._videoSegmentInfoList.clear(),this._audioSegmentInfoList.clear()},e.prototype.remux=function(e,t){if(!this._onMediaSegment)throw new d.j4("MP4Remuxer: onMediaSegment callback must be specificed!");this._dtsBaseInited||this._calculateDtsBase(e,t),this._remuxVideo(t),this._remuxAudio(e)},e.prototype._onTrackMetadataReceived=function(e,t){var i=null,n="mp4",r=t.codec;if("audio"===e)this._audioMeta=t,"mp3"===t.codec&&this._mp3UseMpegAudio?(n="mpeg",r="",i=new Uint8Array):i=A.generateInitSegment(t);else{if("video"!==e)return;this._videoMeta=t,i=A.generateInitSegment(t)}if(!this._onInitSegment)throw new d.j4("MP4Remuxer: onInitSegment callback must be specified!");this._onInitSegment(e,{type:e,data:i.buffer,codec:r,container:e+"/"+n,mediaDuration:t.duration})},e.prototype._calculateDtsBase=function(e,t){this._dtsBaseInited||(e.samples&&e.samples.length&&(this._audioDtsBase=e.samples[0].dts),t.samples&&t.samples.length&&(this._videoDtsBase=t.samples[0].dts),this._dtsBase=Math.min(this._audioDtsBase,this._videoDtsBase),this._dtsBaseInited=!0)},e.prototype.flushStashedSamples=function(){var e=this._videoStashedLastSample,t=this._audioStashedLastSample,i={type:"video",id:1,sequenceNumber:0,samples:[],length:0};null!=e&&(i.samples.push(e),i.length=e.length);var n={type:"audio",id:2,sequenceNumber:0,samples:[],length:0};null!=t&&(n.samples.push(t),n.length=t.length),this._videoStashedLastSample=null,this._audioStashedLastSample=null,this._remuxVideo(i,!0),this._remuxAudio(n,!0)},e.prototype._remuxAudio=function(e,t){if(null!=this._audioMeta){var i,n=e,r=n.samples,a=void 0,h=-1,u=this._audioMeta.refSampleDuration,l="mp3"===this._audioMeta.codec&&this._mp3UseMpegAudio,d=this._dtsBaseInited&&void 0===this._audioNextDts,c=!1;if(r&&0!==r.length&&(1!==r.length||t)){var _=0,f=null,p=0;l?(_=0,p=n.length):(_=8,p=8+n.length);var m=null;if(r.length>1&&(p-=(m=r.pop()).length),null!=this._audioStashedLastSample){var g=this._audioStashedLastSample;this._audioStashedLastSample=null,r.unshift(g),p+=g.length}null!=m&&(this._audioStashedLastSample=m);var v=r[0].dts-this._dtsBase;if(this._audioNextDts)a=v-this._audioNextDts;else if(this._audioSegmentInfoList.isEmpty())a=0,this._fillSilentAfterSeek&&!this._videoSegmentInfoList.isEmpty()&&"mp3"!==this._audioMeta.originalCodec&&(c=!0);else{var y=this._audioSegmentInfoList.getLastSampleBefore(v);if(null!=y){var S=v-(y.originalDts+y.duration);S<=3&&(S=0),a=v-(y.dts+y.duration+S)}else a=0}if(c){var L=v-a,R=this._videoSegmentInfoList.getLastSegmentBefore(v);if(null!=R&&R.beginDts=3*u&&this._fillAudioTimestampGap&&!o.A.safari){I=!0;var P,U=Math.floor(a/u);s.A.w(this.TAG,"Large audio timestamp gap detected, may cause AV sync to drift. Silent frames will be generated to avoid unsync.\noriginalDts: "+D+" ms, curRefDts: "+x+" ms, dtsCorrection: "+Math.round(a)+" ms, generate: "+U+" frames"),w=Math.floor(x),M=Math.floor(x+u)-w,null==(P=b.getSilentFrame(this._audioMeta.originalCodec,this._audioMeta.channelCount))&&(s.A.w(this.TAG,"Unable to generate silent frame for "+this._audioMeta.originalCodec+" with "+this._audioMeta.channelCount+" channels, repeat last frame"),P=k),B=[];for(var N=0;N=1?T[T.length-1].duration:Math.floor(u);this._audioNextDts=w+M}-1===h&&(h=w),T.push({dts:w,pts:w,cts:0,unit:g.unit,size:g.unit.byteLength,duration:M,originalDts:D,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0}}),I&&T.push.apply(T,B)}}if(0===T.length)return n.samples=[],void(n.length=0);l?f=new Uint8Array(p):((f=new Uint8Array(p))[0]=p>>>24&255,f[1]=p>>>16&255,f[2]=p>>>8&255,f[3]=255&p,f.set(A.types.mdat,4));for(C=0;C1&&(d-=(c=s.pop()).length),null!=this._videoStashedLastSample){var _=this._videoStashedLastSample;this._videoStashedLastSample=null,s.unshift(_),d+=_.length}null!=c&&(this._videoStashedLastSample=c);var f=s[0].dts-this._dtsBase;if(this._videoNextDts)o=f-this._videoNextDts;else if(this._videoSegmentInfoList.isEmpty())o=0;else{var p=this._videoSegmentInfoList.getLastSampleBefore(f);if(null!=p){var m=f-(p.originalDts+p.duration);m<=3&&(m=0),o=f-(p.dts+p.duration+m)}else o=0}for(var g=new E.EZ,v=[],y=0;y=1?v[v.length-1].duration:Math.floor(this._videoMeta.refSampleDuration);if(S){var T=new E.$_(L,w,O,_.dts,!0);T.fileposition=_.fileposition,g.appendSyncPoint(T)}v.push({dts:L,pts:w,cts:R,units:_.units,size:_.length,isKeyframe:S,duration:O,originalDts:b,flags:{isLeading:0,dependsOn:S?2:1,isDependedOn:S?1:0,hasRedundancy:0,isNonSync:S?0:1}})}(l=new Uint8Array(d))[0]=d>>>24&255,l[1]=d>>>16&255,l[2]=d>>>8&255,l[3]=255&d,l.set(A.types.mdat,4);for(y=0;y0)this._demuxer.bindDataSource(this._ioctl),this._demuxer.timestampBase=this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase,r=this._demuxer.parseChunks(e,t);else if((n=v.probe(e)).match){this._demuxer=new v(n,this._config),this._remuxer||(this._remuxer=new S(this._config));var o=this._mediaDataSource;null==o.duration||isNaN(o.duration)||(this._demuxer.overridedDuration=o.duration),"boolean"==typeof o.hasAudio&&(this._demuxer.overridedHasAudio=o.hasAudio),"boolean"==typeof o.hasVideo&&(this._demuxer.overridedHasVideo=o.hasVideo),this._demuxer.timestampBase=o.segments[this._currentSegmentIndex].timestampBase,this._demuxer.onError=this._onDemuxException.bind(this),this._demuxer.onMediaInfo=this._onMediaInfo.bind(this),this._demuxer.onMetaDataArrived=this._onMetaDataArrived.bind(this),this._demuxer.onScriptDataArrived=this._onScriptDataArrived.bind(this),this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)),this._remuxer.onInitSegment=this._onRemuxerInitSegmentArrival.bind(this),this._remuxer.onMediaSegment=this._onRemuxerMediaSegmentArrival.bind(this),r=this._demuxer.parseChunks(e,t)}else n=null,s.A.e(this.TAG,"Non-FLV, Unsupported media type!"),Promise.resolve().then((function(){i._internalAbort()})),this._emitter.emit(R.A.DEMUX_ERROR,m.A.FORMAT_UNSUPPORTED,"Non-FLV, Unsupported media type"),r=0;return r},e.prototype._onMediaInfo=function(e){var t=this;null==this._mediaInfo&&(this._mediaInfo=Object.assign({},e),this._mediaInfo.keyframesIndex=null,this._mediaInfo.segments=[],this._mediaInfo.segmentCount=this._mediaDataSource.segments.length,Object.setPrototypeOf(this._mediaInfo,a.A.prototype));var i=Object.assign({},e);Object.setPrototypeOf(i,a.A.prototype),this._mediaInfo.segments[this._currentSegmentIndex]=i,this._reportSegmentMediaInfo(this._currentSegmentIndex),null!=this._pendingSeekTime&&Promise.resolve().then((function(){var e=t._pendingSeekTime;t._pendingSeekTime=null,t.seek(e)}))},e.prototype._onMetaDataArrived=function(e){this._emitter.emit(R.A.METADATA_ARRIVED,e)},e.prototype._onScriptDataArrived=function(e){this._emitter.emit(R.A.SCRIPTDATA_ARRIVED,e)},e.prototype._onIOSeeked=function(){this._remuxer.insertDiscontinuity()},e.prototype._onIOComplete=function(e){var t=e+1;t0&&i[0].originalDts===n&&(n=i[0].pts),this._emitter.emit(R.A.RECOMMEND_SEEKPOINT,n)}},e.prototype._enableStatisticsReporter=function(){null==this._statisticsReporter&&(this._statisticsReporter=self.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval))},e.prototype._disableStatisticsReporter=function(){this._statisticsReporter&&(self.clearInterval(this._statisticsReporter),this._statisticsReporter=null)},e.prototype._reportSegmentMediaInfo=function(e){var t=this._mediaInfo.segments[e],i=Object.assign({},t);i.duration=this._mediaInfo.duration,i.segmentCount=this._mediaInfo.segmentCount,delete i.segments,delete i.keyframesIndex,this._emitter.emit(R.A.MEDIA_INFO,i)},e.prototype._reportStatisticsInfo=function(){var e={};e.url=this._ioctl.currentURL,e.hasRedirect=this._ioctl.hasRedirect,e.hasRedirect&&(e.redirectedURL=this._ioctl.currentRedirectedURL),e.speed=this._ioctl.currentSpeed,e.loaderType=this._ioctl.loaderType,e.currentSegmentIndex=this._currentSegmentIndex,e.totalSegmentCount=this._mediaDataSource.segments.length,this._emitter.emit(R.A.STATISTICS_INFO,e)},e}()},130:function(e,t){"use strict";t.A={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",METADATA_ARRIVED:"metadata_arrived",SCRIPTDATA_ARRIVED:"scriptdata_arrived",STATISTICS_INFO:"statistics_info",RECOMMEND_SEEKPOINT:"recommend_seekpoint"}},137:function(e,t,i){"use strict";i(947),i(811),i(214),i(130)},827:function(e,t){"use strict";t.A={OK:"OK",FORMAT_ERROR:"FormatError",FORMAT_UNSUPPORTED:"FormatUnsupported",CODEC_UNSUPPORTED:"CodecUnsupported"}},148:function(e,t,i){"use strict";i.d(t,{default:function(){return I}});var n=i(811),r=i(653),s={enableWorker:!1,enableStashBuffer:!0,stashInitialSize:void 0,isLive:!1,lazyLoad:!0,lazyLoadMaxDuration:180,lazyLoadRecoverDuration:30,deferLoadAfterSourceOpen:!0,autoCleanupMaxBackwardDuration:180,autoCleanupMinBackwardDuration:120,statisticsInfoReportInterval:600,fixAudioTimestampGap:!0,accurateSeek:!1,seekType:"range",seekParamStart:"bstart",seekParamEnd:"bend",rangeLoadZeroStart:!1,customSeekHandler:void 0,reuseRedirectedURL:!1,headers:void 0,customLoader:void 0};function o(){return Object.assign({},s)}var a=function(){function e(){}return e.supportMSEH264Playback=function(){return window.MediaSource&&window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')},e.supportNetworkStreamIO=function(){var e=new r.A({},o()),t=e.loaderType;return e.destroy(),"fetch-stream-loader"==t||"xhr-moz-chunked-loader"==t},e.getNetworkLoaderTypeName=function(){var e=new r.A({},o()),t=e.loaderType;return e.destroy(),t},e.supportNativeMediaPlayback=function(t){null==e.videoElement&&(e.videoElement=window.document.createElement("video"));var i=e.videoElement.canPlayType(t);return"probably"===i||"maybe"==i},e.getFeatureList=function(){var t={mseFlvPlayback:!1,mseLiveFlvPlayback:!1,networkStreamIO:!1,networkLoaderName:"",nativeMP4H264Playback:!1,nativeWebmVP8Playback:!1,nativeWebmVP9Playback:!1};return t.mseFlvPlayback=e.supportMSEH264Playback(),t.networkStreamIO=e.supportNetworkStreamIO(),t.networkLoaderName=e.getNetworkLoaderTypeName(),t.mseLiveFlvPlayback=t.mseFlvPlayback&&t.networkStreamIO,t.nativeMP4H264Playback=e.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"'),t.nativeWebmVP8Playback=e.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"'),t.nativeWebmVP9Playback=e.supportNativeMediaPlayback('video/webm; codecs="vp9"'),t},e}(),h=a,u=i(470),l=i(211),d=i.n(l),c=i(856),_=i(994),f={ERROR:"error",LOADING_COMPLETE:"loading_complete",RECOVERED_EARLY_EOF:"recovered_early_eof",MEDIA_INFO:"media_info",METADATA_ARRIVED:"metadata_arrived",SCRIPTDATA_ARRIVED:"scriptdata_arrived",STATISTICS_INFO:"statistics_info"},p=i(348),m=i.n(p),g=i(947),v=i(214),y=i(130),A=i(955),b=function(){function e(e,t){if(this.TAG="Transmuxer",this._emitter=new(d()),t.enableWorker&&"undefined"!=typeof Worker)try{this._worker=m()(137),this._workerDestroying=!1,this._worker.addEventListener("message",this._onWorkerMessage.bind(this)),this._worker.postMessage({cmd:"init",param:[e,t]}),this.e={onLoggingConfigChanged:this._onLoggingConfigChanged.bind(this)},g.A.registerListener(this.e.onLoggingConfigChanged),this._worker.postMessage({cmd:"logging_config",param:g.A.getConfig()})}catch(i){c.A.e(this.TAG,"Error while initialize transmuxing worker, fallback to inline transmuxing"),this._worker=null,this._controller=new v.A(e,t)}else this._controller=new v.A(e,t);if(this._controller){var i=this._controller;i.on(y.A.IO_ERROR,this._onIOError.bind(this)),i.on(y.A.DEMUX_ERROR,this._onDemuxError.bind(this)),i.on(y.A.INIT_SEGMENT,this._onInitSegment.bind(this)),i.on(y.A.MEDIA_SEGMENT,this._onMediaSegment.bind(this)),i.on(y.A.LOADING_COMPLETE,this._onLoadingComplete.bind(this)),i.on(y.A.RECOVERED_EARLY_EOF,this._onRecoveredEarlyEof.bind(this)),i.on(y.A.MEDIA_INFO,this._onMediaInfo.bind(this)),i.on(y.A.METADATA_ARRIVED,this._onMetaDataArrived.bind(this)),i.on(y.A.SCRIPTDATA_ARRIVED,this._onScriptDataArrived.bind(this)),i.on(y.A.STATISTICS_INFO,this._onStatisticsInfo.bind(this)),i.on(y.A.RECOMMEND_SEEKPOINT,this._onRecommendSeekpoint.bind(this))}}return e.prototype.destroy=function(){this._worker?this._workerDestroying||(this._workerDestroying=!0,this._worker.postMessage({cmd:"destroy"}),g.A.removeListener(this.e.onLoggingConfigChanged),this.e=null):(this._controller.destroy(),this._controller=null),this._emitter.removeAllListeners(),this._emitter=null},e.prototype.on=function(e,t){this._emitter.addListener(e,t)},e.prototype.off=function(e,t){this._emitter.removeListener(e,t)},e.prototype.hasWorker=function(){return null!=this._worker},e.prototype.open=function(){this._worker?this._worker.postMessage({cmd:"start"}):this._controller.start()},e.prototype.close=function(){this._worker?this._worker.postMessage({cmd:"stop"}):this._controller.stop()},e.prototype.seek=function(e){this._worker?this._worker.postMessage({cmd:"seek",param:e}):this._controller.seek(e)},e.prototype.pause=function(){this._worker?this._worker.postMessage({cmd:"pause"}):this._controller.pause()},e.prototype.resume=function(){this._worker?this._worker.postMessage({cmd:"resume"}):this._controller.resume()},e.prototype._onInitSegment=function(e,t){var i=this;Promise.resolve().then((function(){i._emitter.emit(y.A.INIT_SEGMENT,e,t)}))},e.prototype._onMediaSegment=function(e,t){var i=this;Promise.resolve().then((function(){i._emitter.emit(y.A.MEDIA_SEGMENT,e,t)}))},e.prototype._onLoadingComplete=function(){var e=this;Promise.resolve().then((function(){e._emitter.emit(y.A.LOADING_COMPLETE)}))},e.prototype._onRecoveredEarlyEof=function(){var e=this;Promise.resolve().then((function(){e._emitter.emit(y.A.RECOVERED_EARLY_EOF)}))},e.prototype._onMediaInfo=function(e){var t=this;Promise.resolve().then((function(){t._emitter.emit(y.A.MEDIA_INFO,e)}))},e.prototype._onMetaDataArrived=function(e){var t=this;Promise.resolve().then((function(){t._emitter.emit(y.A.METADATA_ARRIVED,e)}))},e.prototype._onScriptDataArrived=function(e){var t=this;Promise.resolve().then((function(){t._emitter.emit(y.A.SCRIPTDATA_ARRIVED,e)}))},e.prototype._onStatisticsInfo=function(e){var t=this;Promise.resolve().then((function(){t._emitter.emit(y.A.STATISTICS_INFO,e)}))},e.prototype._onIOError=function(e,t){var i=this;Promise.resolve().then((function(){i._emitter.emit(y.A.IO_ERROR,e,t)}))},e.prototype._onDemuxError=function(e,t){var i=this;Promise.resolve().then((function(){i._emitter.emit(y.A.DEMUX_ERROR,e,t)}))},e.prototype._onRecommendSeekpoint=function(e){var t=this;Promise.resolve().then((function(){t._emitter.emit(y.A.RECOMMEND_SEEKPOINT,e)}))},e.prototype._onLoggingConfigChanged=function(e){this._worker&&this._worker.postMessage({cmd:"logging_config",param:e})},e.prototype._onWorkerMessage=function(e){var t=e.data,i=t.data;if("destroyed"===t.msg||this._workerDestroying)return this._workerDestroying=!1,this._worker.terminate(),void(this._worker=null);switch(t.msg){case y.A.INIT_SEGMENT:case y.A.MEDIA_SEGMENT:this._emitter.emit(t.msg,i.type,i.data);break;case y.A.LOADING_COMPLETE:case y.A.RECOVERED_EARLY_EOF:this._emitter.emit(t.msg);break;case y.A.MEDIA_INFO:Object.setPrototypeOf(i,A.A.prototype),this._emitter.emit(t.msg,i);break;case y.A.METADATA_ARRIVED:case y.A.SCRIPTDATA_ARRIVED:case y.A.STATISTICS_INFO:this._emitter.emit(t.msg,i);break;case y.A.IO_ERROR:case y.A.DEMUX_ERROR:this._emitter.emit(t.msg,i.type,i.info);break;case y.A.RECOMMEND_SEEKPOINT:this._emitter.emit(t.msg,i);break;case"logcat_callback":c.A.emitter.emit("log",i.type,i.logcat)}},e}(),E={ERROR:"error",SOURCE_OPEN:"source_open",UPDATE_END:"update_end",BUFFER_FULL:"buffer_full"},S=i(47),L=i(867),R=function(){function e(e){this.TAG="MSEController",this._config=e,this._emitter=new(d()),this._config.isLive&&null==this._config.autoCleanupSourceBuffer&&(this._config.autoCleanupSourceBuffer=!0),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=!1,this._hasPendingEos=!1,this._requireSetMediaDuration=!1,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 S.EX}return e.prototype.destroy=function(){(this._mediaElement||this._mediaSource)&&this.detachMediaElement(),this.e=null,this._emitter.removeAllListeners(),this._emitter=null},e.prototype.on=function(e,t){this._emitter.addListener(e,t)},e.prototype.off=function(e,t){this._emitter.removeListener(e,t)},e.prototype.attachMediaElement=function(e){if(this._mediaSource)throw new L.j4("MediaSource has been attached to an HTMLMediaElement!");var t=this._mediaSource=new window.MediaSource;t.addEventListener("sourceopen",this.e.onSourceOpen),t.addEventListener("sourceended",this.e.onSourceEnded),t.addEventListener("sourceclose",this.e.onSourceClose),this._mediaElement=e,this._mediaSourceObjectURL=window.URL.createObjectURL(this._mediaSource),e.src=this._mediaSourceObjectURL},e.prototype.detachMediaElement=function(){if(this._mediaSource){var e=this._mediaSource;for(var t in this._sourceBuffers){var i=this._pendingSegments[t];i.splice(0,i.length),this._pendingSegments[t]=null,this._pendingRemoveRanges[t]=null,this._lastInitSegments[t]=null;var n=this._sourceBuffers[t];if(n){if("closed"!==e.readyState){try{e.removeSourceBuffer(n)}catch(e){c.A.e(this.TAG,e.message)}n.removeEventListener("error",this.e.onSourceBufferError),n.removeEventListener("updateend",this.e.onSourceBufferUpdateEnd)}this._mimeTypes[t]=null,this._sourceBuffers[t]=null}}if("open"===e.readyState)try{e.endOfStream()}catch(e){c.A.e(this.TAG,e.message)}e.removeEventListener("sourceopen",this.e.onSourceOpen),e.removeEventListener("sourceended",this.e.onSourceEnded),e.removeEventListener("sourceclose",this.e.onSourceClose),this._pendingSourceBufferInit=[],this._isBufferFull=!1,this._idrList.clear(),this._mediaSource=null}this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src"),this._mediaElement=null),this._mediaSourceObjectURL&&(window.URL.revokeObjectURL(this._mediaSourceObjectURL),this._mediaSourceObjectURL=null)},e.prototype.appendInitSegment=function(e,t){if(!this._mediaSource||"open"!==this._mediaSource.readyState)return this._pendingSourceBufferInit.push(e),void this._pendingSegments[e.type].push(e);var i=e,n=""+i.container;i.codec&&i.codec.length>0&&(n+=";codecs="+i.codec);var r=!1;if(c.A.v(this.TAG,"Received Initialization Segment, mimeType: "+n),this._lastInitSegments[i.type]=i,n!==this._mimeTypes[i.type]){if(this._mimeTypes[i.type])c.A.v(this.TAG,"Notice: "+i.type+" mimeType changed, origin: "+this._mimeTypes[i.type]+", target: "+n);else{r=!0;try{var s=this._sourceBuffers[i.type]=this._mediaSource.addSourceBuffer(n);s.addEventListener("error",this.e.onSourceBufferError),s.addEventListener("updateend",this.e.onSourceBufferUpdateEnd)}catch(e){return c.A.e(this.TAG,e.message),void this._emitter.emit(E.ERROR,{code:e.code,msg:e.message})}}this._mimeTypes[i.type]=n}t||this._pendingSegments[i.type].push(i),r||this._sourceBuffers[i.type]&&!this._sourceBuffers[i.type].updating&&this._doAppendSegments(),_.A.safari&&"audio/mpeg"===i.container&&i.mediaDuration>0&&(this._requireSetMediaDuration=!0,this._pendingMediaDuration=i.mediaDuration/1e3,this._updateMediaSourceDuration())},e.prototype.appendMediaSegment=function(e){var t=e;this._pendingSegments[t.type].push(t),this._config.autoCleanupSourceBuffer&&this._needCleanupSourceBuffer()&&this._doCleanupSourceBuffer();var i=this._sourceBuffers[t.type];!i||i.updating||this._hasPendingRemoveRanges()||this._doAppendSegments()},e.prototype.seek=function(e){for(var t in this._sourceBuffers)if(this._sourceBuffers[t]){var i=this._sourceBuffers[t];if("open"===this._mediaSource.readyState)try{i.abort()}catch(e){c.A.e(this.TAG,e.message)}this._idrList.clear();var n=this._pendingSegments[t];if(n.splice(0,n.length),"closed"!==this._mediaSource.readyState){for(var r=0;r=1&&e-n.start(0)>=this._config.autoCleanupMaxBackwardDuration)return!0}}return!1},e.prototype._doCleanupSourceBuffer=function(){var e=this._mediaElement.currentTime;for(var t in this._sourceBuffers){var i=this._sourceBuffers[t];if(i){for(var n=i.buffered,r=!1,s=0;s=this._config.autoCleanupMaxBackwardDuration){r=!0;var h=e-this._config.autoCleanupMinBackwardDuration;this._pendingRemoveRanges[t].push({start:o,end:h})}}else a0&&(isNaN(t)||i>t)&&(c.A.v(this.TAG,"Update MediaSource duration from "+t+" to "+i),this._mediaSource.duration=i),this._requireSetMediaDuration=!1,this._pendingMediaDuration=0}},e.prototype._doRemoveRanges=function(){for(var e in this._pendingRemoveRanges)if(this._sourceBuffers[e]&&!this._sourceBuffers[e].updating)for(var t=this._sourceBuffers[e],i=this._pendingRemoveRanges[e];i.length&&!t.updating;){var n=i.shift();t.remove(n.start,n.end)}},e.prototype._doAppendSegments=function(){var e=this._pendingSegments;for(var t in e)if(this._sourceBuffers[t]&&!this._sourceBuffers[t].updating&&e[t].length>0){var i=e[t].shift();if(i.timestampOffset){var n=this._sourceBuffers[t].timestampOffset,r=i.timestampOffset/1e3;Math.abs(n-r)>.1&&(c.A.v(this.TAG,"Update MPEG audio timestampOffset from "+n+" to "+r),this._sourceBuffers[t].timestampOffset=r),delete i.timestampOffset}if(!i.data||0===i.data.byteLength)continue;try{this._sourceBuffers[t].appendBuffer(i.data),this._isBufferFull=!1,"video"===t&&i.hasOwnProperty("info")&&this._idrList.appendArray(i.info.syncPoints)}catch(e){this._pendingSegments[t].unshift(i),22===e.code?(this._isBufferFull||this._emitter.emit(E.BUFFER_FULL),this._isBufferFull=!0):(c.A.e(this.TAG,e.message),this._emitter.emit(E.ERROR,{code:e.code,msg:e.message}))}}},e.prototype._onSourceOpen=function(){if(c.A.v(this.TAG,"MediaSource onSourceOpen"),this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._pendingSourceBufferInit.length>0)for(var e=this._pendingSourceBufferInit;e.length;){var t=e.shift();this.appendInitSegment(t,!0)}this._hasPendingSegments()&&this._doAppendSegments(),this._emitter.emit(E.SOURCE_OPEN)},e.prototype._onSourceEnded=function(){c.A.v(this.TAG,"MediaSource onSourceEnded")},e.prototype._onSourceClose=function(){c.A.v(this.TAG,"MediaSource onSourceClose"),this._mediaSource&&null!=this.e&&(this._mediaSource.removeEventListener("sourceopen",this.e.onSourceOpen),this._mediaSource.removeEventListener("sourceended",this.e.onSourceEnded),this._mediaSource.removeEventListener("sourceclose",this.e.onSourceClose))},e.prototype._hasPendingSegments=function(){var e=this._pendingSegments;return e.video.length>0||e.audio.length>0},e.prototype._hasPendingRemoveRanges=function(){var e=this._pendingRemoveRanges;return e.video.length>0||e.audio.length>0},e.prototype._onSourceBufferUpdateEnd=function(){this._requireSetMediaDuration?this._updateMediaSourceDuration():this._hasPendingRemoveRanges()?this._doRemoveRanges():this._hasPendingSegments()?this._doAppendSegments():this._hasPendingEos&&this.endOfStream(),this._emitter.emit(E.UPDATE_END)},e.prototype._onSourceBufferError=function(e){c.A.e(this.TAG,"SourceBuffer Error: "+e)},e}(),w=i(827),O={NETWORK_ERROR:"NetworkError",MEDIA_ERROR:"MediaError",OTHER_ERROR:"OtherError"},T={NETWORK_EXCEPTION:u.Xv.EXCEPTION,NETWORK_STATUS_CODE_INVALID:u.Xv.HTTP_STATUS_CODE_INVALID,NETWORK_TIMEOUT:u.Xv.CONNECTING_TIMEOUT,NETWORK_UNRECOVERABLE_EARLY_EOF:u.Xv.UNRECOVERABLE_EARLY_EOF,MEDIA_MSE_ERROR:"MediaMSEError",MEDIA_FORMAT_ERROR:w.A.FORMAT_ERROR,MEDIA_FORMAT_UNSUPPORTED:w.A.FORMAT_UNSUPPORTED,MEDIA_CODEC_UNSUPPORTED:w.A.CODEC_UNSUPPORTED},C=function(){function e(e,t){if(this.TAG="FlvPlayer",this._type="FlvPlayer",this._emitter=new(d()),this._config=o(),"object"==typeof t&&Object.assign(this._config,t),"flv"!==e.type.toLowerCase())throw new L.Qn("FlvPlayer requires an flv MediaDataSource input!");!0===e.isLive&&(this._config.isLive=!0),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)},self.performance&&self.performance.now?this._now=self.performance.now.bind(self.performance):this._now=Date.now,this._pendingSeekTime=null,this._requestSetTime=!1,this._seekpointRecord=null,this._progressChecker=null,this._mediaDataSource=e,this._mediaElement=null,this._msectl=null,this._transmuxer=null,this._mseSourceOpened=!1,this._hasPendingLoad=!1,this._receivedCanPlay=!1,this._mediaInfo=null,this._statisticsInfo=null;var i=_.A.chrome&&(_.A.version.major<50||50===_.A.version.major&&_.A.version.build<2661);this._alwaysSeekKeyframe=!!(i||_.A.msedge||_.A.msie),this._alwaysSeekKeyframe&&(this._config.accurateSeek=!1)}return e.prototype.destroy=function(){null!=this._progressChecker&&(window.clearInterval(this._progressChecker),this._progressChecker=null),this._transmuxer&&this.unload(),this._mediaElement&&this.detachMediaElement(),this.e=null,this._mediaDataSource=null,this._emitter.removeAllListeners(),this._emitter=null},e.prototype.on=function(e,t){var i=this;e===f.MEDIA_INFO?null!=this._mediaInfo&&Promise.resolve().then((function(){i._emitter.emit(f.MEDIA_INFO,i.mediaInfo)})):e===f.STATISTICS_INFO&&null!=this._statisticsInfo&&Promise.resolve().then((function(){i._emitter.emit(f.STATISTICS_INFO,i.statisticsInfo)})),this._emitter.addListener(e,t)},e.prototype.off=function(e,t){this._emitter.removeListener(e,t)},e.prototype.attachMediaElement=function(e){var t=this;if(this._mediaElement=e,e.addEventListener("loadedmetadata",this.e.onvLoadedMetadata),e.addEventListener("seeking",this.e.onvSeeking),e.addEventListener("canplay",this.e.onvCanPlay),e.addEventListener("stalled",this.e.onvStalled),e.addEventListener("progress",this.e.onvProgress),this._msectl=new R(this._config),this._msectl.on(E.UPDATE_END,this._onmseUpdateEnd.bind(this)),this._msectl.on(E.BUFFER_FULL,this._onmseBufferFull.bind(this)),this._msectl.on(E.SOURCE_OPEN,(function(){t._mseSourceOpened=!0,t._hasPendingLoad&&(t._hasPendingLoad=!1,t.load())})),this._msectl.on(E.ERROR,(function(e){t._emitter.emit(f.ERROR,O.MEDIA_ERROR,T.MEDIA_MSE_ERROR,e)})),this._msectl.attachMediaElement(e),null!=this._pendingSeekTime)try{e.currentTime=this._pendingSeekTime,this._pendingSeekTime=null}catch(e){}},e.prototype.detachMediaElement=function(){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),this._msectl&&(this._msectl.destroy(),this._msectl=null)},e.prototype.load=function(){var e=this;if(!this._mediaElement)throw new L.j4("HTMLMediaElement must be attached before load()!");if(this._transmuxer)throw new L.j4("FlvPlayer.load() has been called, please call unload() first!");this._hasPendingLoad||(this._config.deferLoadAfterSourceOpen&&!1===this._mseSourceOpened?this._hasPendingLoad=!0:(this._mediaElement.readyState>0&&(this._requestSetTime=!0,this._mediaElement.currentTime=0),this._transmuxer=new b(this._mediaDataSource,this._config),this._transmuxer.on(y.A.INIT_SEGMENT,(function(t,i){e._msectl.appendInitSegment(i)})),this._transmuxer.on(y.A.MEDIA_SEGMENT,(function(t,i){if(e._msectl.appendMediaSegment(i),e._config.lazyLoad&&!e._config.isLive){var n=e._mediaElement.currentTime;i.info.endDts>=1e3*(n+e._config.lazyLoadMaxDuration)&&null==e._progressChecker&&(c.A.v(e.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),e._suspendTransmuxer())}})),this._transmuxer.on(y.A.LOADING_COMPLETE,(function(){e._msectl.endOfStream(),e._emitter.emit(f.LOADING_COMPLETE)})),this._transmuxer.on(y.A.RECOVERED_EARLY_EOF,(function(){e._emitter.emit(f.RECOVERED_EARLY_EOF)})),this._transmuxer.on(y.A.IO_ERROR,(function(t,i){e._emitter.emit(f.ERROR,O.NETWORK_ERROR,t,i)})),this._transmuxer.on(y.A.DEMUX_ERROR,(function(t,i){e._emitter.emit(f.ERROR,O.MEDIA_ERROR,t,{code:-1,msg:i})})),this._transmuxer.on(y.A.MEDIA_INFO,(function(t){e._mediaInfo=t,e._emitter.emit(f.MEDIA_INFO,Object.assign({},t))})),this._transmuxer.on(y.A.METADATA_ARRIVED,(function(t){e._emitter.emit(f.METADATA_ARRIVED,t)})),this._transmuxer.on(y.A.SCRIPTDATA_ARRIVED,(function(t){e._emitter.emit(f.SCRIPTDATA_ARRIVED,t)})),this._transmuxer.on(y.A.STATISTICS_INFO,(function(t){e._statisticsInfo=e._fillStatisticsInfo(t),e._emitter.emit(f.STATISTICS_INFO,Object.assign({},e._statisticsInfo))})),this._transmuxer.on(y.A.RECOMMEND_SEEKPOINT,(function(t){e._mediaElement&&!e._config.accurateSeek&&(e._requestSetTime=!0,e._mediaElement.currentTime=t/1e3)})),this._transmuxer.open()))},e.prototype.unload=function(){this._mediaElement&&this._mediaElement.pause(),this._msectl&&this._msectl.seek(0),this._transmuxer&&(this._transmuxer.close(),this._transmuxer.destroy(),this._transmuxer=null)},e.prototype.play=function(){return this._mediaElement.play()},e.prototype.pause=function(){this._mediaElement.pause()},Object.defineProperty(e.prototype,"type",{get:function(){return this._type},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"buffered",{get:function(){return this._mediaElement.buffered},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"duration",{get:function(){return this._mediaElement.duration},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"volume",{get:function(){return this._mediaElement.volume},set:function(e){this._mediaElement.volume=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"muted",{get:function(){return this._mediaElement.muted},set:function(e){this._mediaElement.muted=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentTime",{get:function(){return this._mediaElement?this._mediaElement.currentTime:0},set:function(e){this._mediaElement?this._internalSeek(e):this._pendingSeekTime=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"mediaInfo",{get:function(){return Object.assign({},this._mediaInfo)},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"statisticsInfo",{get:function(){return null==this._statisticsInfo&&(this._statisticsInfo={}),this._statisticsInfo=this._fillStatisticsInfo(this._statisticsInfo),Object.assign({},this._statisticsInfo)},enumerable:!1,configurable:!0}),e.prototype._fillStatisticsInfo=function(e){if(e.playerType=this._type,!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,i=0,n=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();i=r.totalVideoFrames,n=r.droppedVideoFrames}else null!=this._mediaElement.webkitDecodedFrameCount?(i=this._mediaElement.webkitDecodedFrameCount,n=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=i,e.droppedFrames=n),e},e.prototype._onmseUpdateEnd=function(){if(this._config.lazyLoad&&!this._config.isLive){for(var e=this._mediaElement.buffered,t=this._mediaElement.currentTime,i=0,n=0;n=t+this._config.lazyLoadMaxDuration&&null==this._progressChecker&&(c.A.v(this.TAG,"Maximum buffering duration exceeded, suspend transmuxing task"),this._suspendTransmuxer())}},e.prototype._onmseBufferFull=function(){c.A.v(this.TAG,"MSE SourceBuffer is full, suspend transmuxing task"),null==this._progressChecker&&this._suspendTransmuxer()},e.prototype._suspendTransmuxer=function(){this._transmuxer&&(this._transmuxer.pause(),null==this._progressChecker&&(this._progressChecker=window.setInterval(this._checkProgressAndResume.bind(this),1e3)))},e.prototype._checkProgressAndResume=function(){for(var e=this._mediaElement.currentTime,t=this._mediaElement.buffered,i=!1,n=0;n=r&&e=s-this._config.lazyLoadRecoverDuration&&(i=!0);break}}i&&(window.clearInterval(this._progressChecker),this._progressChecker=null,i&&(c.A.v(this.TAG,"Continue loading from paused position"),this._transmuxer.resume()))},e.prototype._isTimepointBuffered=function(e){for(var t=this._mediaElement.buffered,i=0;i=n&&e0){var r=this._mediaElement.buffered.start(0);(r<1&&e0&&t.currentTime0){var n=i.start(0);if(n<1&&t0&&(this._mediaElement.currentTime=0),this._mediaElement.preload="auto",this._mediaElement.load(),this._statisticsReporter=window.setInterval(this._reportStatisticsInfo.bind(this),this._config.statisticsInfoReportInterval)},e.prototype.unload=function(){this._mediaElement&&(this._mediaElement.src="",this._mediaElement.removeAttribute("src")),null!=this._statisticsReporter&&(window.clearInterval(this._statisticsReporter),this._statisticsReporter=null)},e.prototype.play=function(){return this._mediaElement.play()},e.prototype.pause=function(){this._mediaElement.pause()},Object.defineProperty(e.prototype,"type",{get:function(){return this._type},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"buffered",{get:function(){return this._mediaElement.buffered},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"duration",{get:function(){return this._mediaElement.duration},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"volume",{get:function(){return this._mediaElement.volume},set:function(e){this._mediaElement.volume=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"muted",{get:function(){return this._mediaElement.muted},set:function(e){this._mediaElement.muted=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentTime",{get:function(){return this._mediaElement?this._mediaElement.currentTime:0},set:function(e){this._mediaElement?this._mediaElement.currentTime=e:this._pendingSeekTime=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"mediaInfo",{get:function(){var e={mimeType:(this._mediaElement instanceof HTMLAudioElement?"audio/":"video/")+this._mediaDataSource.type};return this._mediaElement&&(e.duration=Math.floor(1e3*this._mediaElement.duration),this._mediaElement instanceof HTMLVideoElement&&(e.width=this._mediaElement.videoWidth,e.height=this._mediaElement.videoHeight)),e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"statisticsInfo",{get:function(){var e={playerType:this._type,url:this._mediaDataSource.url};if(!(this._mediaElement instanceof HTMLVideoElement))return e;var t=!0,i=0,n=0;if(this._mediaElement.getVideoPlaybackQuality){var r=this._mediaElement.getVideoPlaybackQuality();i=r.totalVideoFrames,n=r.droppedVideoFrames}else null!=this._mediaElement.webkitDecodedFrameCount?(i=this._mediaElement.webkitDecodedFrameCount,n=this._mediaElement.webkitDroppedFrameCount):t=!1;return t&&(e.decodedFrames=i,e.droppedFrames=n),e},enumerable:!1,configurable:!0}),e.prototype._onvLoadedMetadata=function(e){null!=this._pendingSeekTime&&(this._mediaElement.currentTime=this._pendingSeekTime,this._pendingSeekTime=null),this._emitter.emit(f.MEDIA_INFO,this.mediaInfo)},e.prototype._reportStatisticsInfo=function(){this._emitter.emit(f.STATISTICS_INFO,this.statisticsInfo)},e}();n.A.install();var D={createPlayer:function(e,t){var i=e;if(null==i||"object"!=typeof i)throw new L.Qn("MediaDataSource must be an javascript object!");if(!i.hasOwnProperty("type"))throw new L.Qn("MediaDataSource must has type field to indicate video file type!");return"flv"===i.type?new C(i,t):new k(i,t)},isSupported:function(){return h.supportMSEH264Playback()},getFeatureList:function(){return h.getFeatureList()}};D.BaseLoader=u.HC,D.LoaderStatus=u.eO,D.LoaderErrors=u.Xv,D.Events=f,D.ErrorTypes=O,D.ErrorDetails=T,D.FlvPlayer=C,D.NativePlayer=k,D.LoggingControl=g.A,Object.defineProperty(D,"version",{enumerable:!0,get:function(){return"1.6.2"}});var I=D},976:function(e,t,i){e.exports=i(148).default},653:function(e,t,i){"use strict";i.d(t,{A:function(){return y}});var n,r=i(856),s=function(){function e(){this._firstCheckpoint=0,this._lastCheckpoint=0,this._intervalBytes=0,this._totalBytes=0,this._lastSecondBytes=0,self.performance&&self.performance.now?this._now=self.performance.now.bind(self.performance):this._now=Date.now}return e.prototype.reset=function(){this._firstCheckpoint=this._lastCheckpoint=0,this._totalBytes=this._intervalBytes=0,this._lastSecondBytes=0},e.prototype.addBytes=function(e){0===this._firstCheckpoint?(this._firstCheckpoint=this._now(),this._lastCheckpoint=this._firstCheckpoint,this._intervalBytes+=e,this._totalBytes+=e):this._now()-this._lastCheckpoint<1e3?(this._intervalBytes+=e,this._totalBytes+=e):(this._lastSecondBytes=this._intervalBytes,this._intervalBytes=e,this._totalBytes+=e,this._lastCheckpoint=this._now())},Object.defineProperty(e.prototype,"currentKBps",{get:function(){this.addBytes(0);var e=(this._now()-this._lastCheckpoint)/1e3;return 0==e&&(e=1),this._intervalBytes/e/1024},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"lastSecondKBps",{get:function(){return this.addBytes(0),0!==this._lastSecondBytes?this._lastSecondBytes/1024:this._now()-this._lastCheckpoint>=500?this.currentKBps:0},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"averageKBps",{get:function(){var e=(this._now()-this._firstCheckpoint)/1e3;return this._totalBytes/e/1024},enumerable:!1,configurable:!0}),e}(),o=i(470),a=i(994),h=i(867),u=(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),l=function(e){function t(t,i){var n=e.call(this,"fetch-stream-loader")||this;return n.TAG="FetchStreamLoader",n._seekHandler=t,n._config=i,n._needStash=!0,n._requestAbort=!1,n._contentLength=null,n._receivedLength=0,n}return u(t,e),t.isSupported=function(){try{var e=a.A.msedge&&a.A.version.minor>=15048,t=!a.A.msedge||e;return self.fetch&&self.ReadableStream&&t}catch(e){return!1}},t.prototype.destroy=function(){this.isWorking()&&this.abort(),e.prototype.destroy.call(this)},t.prototype.open=function(e,t){var i=this;this._dataSource=e,this._range=t;var n=e.url;this._config.reuseRedirectedURL&&null!=e.redirectedURL&&(n=e.redirectedURL);var r=this._seekHandler.getConfig(n,t),s=new self.Headers;if("object"==typeof r.headers){var a=r.headers;for(var u in a)a.hasOwnProperty(u)&&s.append(u,a[u])}var l={method:"GET",headers:s,mode:"cors",cache:"default",referrerPolicy:"no-referrer-when-downgrade"};if("object"==typeof this._config.headers)for(var u in this._config.headers)s.append(u,this._config.headers[u]);!1===e.cors&&(l.mode="same-origin"),e.withCredentials&&(l.credentials="include"),e.referrerPolicy&&(l.referrerPolicy=e.referrerPolicy),self.AbortController&&(this._abortController=new self.AbortController,l.signal=this._abortController.signal),this._status=o.eO.kConnecting,self.fetch(r.url,l).then((function(e){if(i._requestAbort)return i._status=o.eO.kIdle,void e.body.cancel();if(e.ok&&e.status>=200&&e.status<=299){if(e.url!==r.url&&i._onURLRedirect){var t=i._seekHandler.removeURLParameters(e.url);i._onURLRedirect(t)}var n=e.headers.get("Content-Length");return null!=n&&(i._contentLength=parseInt(n),0!==i._contentLength&&i._onContentLengthKnown&&i._onContentLengthKnown(i._contentLength)),i._pump.call(i,e.body.getReader())}if(i._status=o.eO.kError,!i._onError)throw new h.Al("FetchStreamLoader: Http code invalid, "+e.status+" "+e.statusText);i._onError(o.Xv.HTTP_STATUS_CODE_INVALID,{code:e.status,msg:e.statusText})})).catch((function(e){if(!i._abortController||!i._abortController.signal.aborted){if(i._status=o.eO.kError,!i._onError)throw e;i._onError(o.Xv.EXCEPTION,{code:-1,msg:e.message})}}))},t.prototype.abort=function(){if(this._requestAbort=!0,(this._status!==o.eO.kBuffering||!a.A.chrome)&&this._abortController)try{this._abortController.abort()}catch(e){}},t.prototype._pump=function(e){var t=this;return e.read().then((function(i){if(i.done)if(null!==t._contentLength&&t._receivedLength299)){if(this._status=o.eO.kError,!this._onError)throw new h.Al("MozChunkedLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(o.Xv.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}else this._status=o.eO.kBuffering}},t.prototype._onProgress=function(e){if(this._status!==o.eO.kError){null===this._contentLength&&null!==e.total&&0!==e.total&&(this._contentLength=e.total,this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength));var t=e.target.response,i=this._range.from+this._receivedLength;this._receivedLength+=t.byteLength,this._onDataArrival&&this._onDataArrival(t,i,this._receivedLength)}},t.prototype._onLoadEnd=function(e){!0!==this._requestAbort?this._status!==o.eO.kError&&(this._status=o.eO.kComplete,this._onComplete&&this._onComplete(this._range.from,this._range.from+this._receivedLength-1)):this._requestAbort=!1},t.prototype._onXhrError=function(e){this._status=o.eO.kError;var t=0,i=null;if(this._contentLength&&e.loaded=this._contentLength&&(i=this._range.from+this._contentLength-1),this._currentRequestRange={from:t,to:i},this._internalOpen(this._dataSource,this._currentRequestRange)},t.prototype._internalOpen=function(e,t){this._lastTimeLoaded=0;var i=e.url;this._config.reuseRedirectedURL&&(null!=this._currentRedirectedURL?i=this._currentRedirectedURL:null!=e.redirectedURL&&(i=e.redirectedURL));var n=this._seekHandler.getConfig(i,t);this._currentRequestURL=n.url;var r=this._xhr=new XMLHttpRequest;if(r.open("GET",n.url,!0),r.responseType="arraybuffer",r.onreadystatechange=this._onReadyStateChange.bind(this),r.onprogress=this._onProgress.bind(this),r.onload=this._onLoad.bind(this),r.onerror=this._onXhrError.bind(this),e.withCredentials&&(r.withCredentials=!0),"object"==typeof n.headers){var s=n.headers;for(var o in s)s.hasOwnProperty(o)&&r.setRequestHeader(o,s[o])}if("object"==typeof this._config.headers){s=this._config.headers;for(var o in s)s.hasOwnProperty(o)&&r.setRequestHeader(o,s[o])}r.send()},t.prototype.abort=function(){this._requestAbort=!0,this._internalAbort(),this._status=o.eO.kComplete},t.prototype._internalAbort=function(){this._xhr&&(this._xhr.onreadystatechange=null,this._xhr.onprogress=null,this._xhr.onload=null,this._xhr.onerror=null,this._xhr.abort(),this._xhr=null)},t.prototype._onReadyStateChange=function(e){var t=e.target;if(2===t.readyState){if(null!=t.responseURL){var i=this._seekHandler.removeURLParameters(t.responseURL);t.responseURL!==this._currentRequestURL&&i!==this._currentRedirectedURL&&(this._currentRedirectedURL=i,this._onURLRedirect&&this._onURLRedirect(i))}if(t.status>=200&&t.status<=299){if(this._waitForTotalLength)return;this._status=o.eO.kBuffering}else{if(this._status=o.eO.kError,!this._onError)throw new h.Al("RangeLoader: Http code invalid, "+t.status+" "+t.statusText);this._onError(o.Xv.HTTP_STATUS_CODE_INVALID,{code:t.status,msg:t.statusText})}}},t.prototype._onProgress=function(e){if(this._status!==o.eO.kError){if(null===this._contentLength){var t=!1;if(this._waitForTotalLength){this._waitForTotalLength=!1,this._totalLengthReceived=!0,t=!0;var i=e.total;this._internalAbort(),null!=i&0!==i&&(this._totalLength=i)}if(-1===this._range.to?this._contentLength=this._totalLength-this._range.from:this._contentLength=this._range.to-this._range.from+1,t)return void this._openSubRange();this._onContentLengthKnown&&this._onContentLengthKnown(this._contentLength)}var n=e.loaded-this._lastTimeLoaded;this._lastTimeLoaded=e.loaded,this._speedSampler.addBytes(n)}},t.prototype._normalizeSpeed=function(e){var t=this._chunkSizeKBList,i=t.length-1,n=0,r=0,s=i;if(e=t[n]&&e=3&&(t=this._speedSampler.currentKBps)),0!==t){var i=this._normalizeSpeed(t);this._currentSpeedNormalized!==i&&(this._currentSpeedNormalized=i,this._currentChunkSizeKB=i)}var n=e.target.response,r=this._range.from+this._receivedLength;this._receivedLength+=n.byteLength;var s=!1;null!=this._contentLength&&this._receivedLength0&&this._receivedLength0)for(var s=i.split("&"),o=0;o0;a[0]!==this._startName&&a[0]!==this._endName&&(h&&(r+="&"),r+=s[o])}return 0===r.length?t:t+"?"+r},e}(),y=function(){function e(e,t,i){this.TAG="IOController",this._config=t,this._extraData=i,this._stashInitialSize=393216,null!=t.stashInitialSize&&t.stashInitialSize>0&&(this._stashInitialSize=t.stashInitialSize),this._stashUsed=0,this._stashSize=this._stashInitialSize,this._bufferSize=3145728,this._stashBuffer=new ArrayBuffer(this._bufferSize),this._stashByteStart=0,this._enableStash=!0,!1===t.enableStashBuffer&&(this._enableStash=!1),this._loader=null,this._loaderClass=null,this._seekHandler=null,this._dataSource=e,this._isWebSocketURL=/wss?:\/\/(.+?)/.test(e.url),this._refTotalLength=e.filesize?e.filesize:null,this._totalLength=this._refTotalLength,this._fullRequestFlag=!1,this._currentRange=null,this._redirectedURL=null,this._speedNormalized=0,this._speedSampler=new s,this._speedNormalizeList=[64,128,256,384,512,768,1024,1536,2048,3072,4096],this._isEarlyEofReconnecting=!1,this._paused=!1,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()}return e.prototype.destroy=function(){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=!1,this._onDataArrival=null,this._onSeeked=null,this._onError=null,this._onComplete=null,this._onRedirect=null,this._onRecoveredEarlyEof=null,this._extraData=null},e.prototype.isWorking=function(){return this._loader&&this._loader.isWorking()&&!this._paused},e.prototype.isPaused=function(){return this._paused},Object.defineProperty(e.prototype,"status",{get:function(){return this._loader.status},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"extraData",{get:function(){return this._extraData},set:function(e){this._extraData=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onDataArrival",{get:function(){return this._onDataArrival},set:function(e){this._onDataArrival=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onSeeked",{get:function(){return this._onSeeked},set:function(e){this._onSeeked=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onError",{get:function(){return this._onError},set:function(e){this._onError=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onComplete",{get:function(){return this._onComplete},set:function(e){this._onComplete=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onRedirect",{get:function(){return this._onRedirect},set:function(e){this._onRedirect=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"onRecoveredEarlyEof",{get:function(){return this._onRecoveredEarlyEof},set:function(e){this._onRecoveredEarlyEof=e},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentURL",{get:function(){return this._dataSource.url},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"hasRedirect",{get:function(){return null!=this._redirectedURL||null!=this._dataSource.redirectedURL},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentRedirectedURL",{get:function(){return this._redirectedURL||this._dataSource.redirectedURL},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentSpeed",{get:function(){return this._loaderClass===f?this._loader.currentSpeed:this._speedSampler.lastSecondKBps},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"loaderType",{get:function(){return this._loader.type},enumerable:!1,configurable:!0}),e.prototype._selectSeekHandler=function(){var e=this._config;if("range"===e.seekType)this._seekHandler=new g(this._config.rangeLoadZeroStart);else if("param"===e.seekType){var t=e.seekParamStart||"bstart",i=e.seekParamEnd||"bend";this._seekHandler=new v(t,i)}else{if("custom"!==e.seekType)throw new h.Qn("Invalid seekType in config: "+e.seekType);if("function"!=typeof e.customSeekHandler)throw new h.Qn("Custom seekType specified in config but invalid customSeekHandler!");this._seekHandler=new e.customSeekHandler}},e.prototype._selectLoader=function(){if(null!=this._config.customLoader)this._loaderClass=this._config.customLoader;else if(this._isWebSocketURL)this._loaderClass=m;else if(l.isSupported())this._loaderClass=l;else if(c.isSupported())this._loaderClass=c;else{if(!f.isSupported())throw new h.Al("Your browser doesn't support xhr with arraybuffer responseType!");this._loaderClass=f}},e.prototype._createLoader=function(){this._loader=new this._loaderClass(this._seekHandler,this._config),!1===this._loader.needStashBuffer&&(this._enableStash=!1),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)},e.prototype.open=function(e){this._currentRange={from:0,to:-1},e&&(this._currentRange.from=e),this._speedSampler.reset(),e||(this._fullRequestFlag=!0),this._loader.open(this._dataSource,Object.assign({},this._currentRange))},e.prototype.abort=function(){this._loader.abort(),this._paused&&(this._paused=!1,this._resumeFrom=0)},e.prototype.pause=function(){this.isWorking()&&(this._loader.abort(),0!==this._stashUsed?(this._resumeFrom=this._stashByteStart,this._currentRange.to=this._stashByteStart-1):this._resumeFrom=this._currentRange.to+1,this._stashUsed=0,this._stashByteStart=0,this._paused=!0)},e.prototype.resume=function(){if(this._paused){this._paused=!1;var e=this._resumeFrom;this._resumeFrom=0,this._internalSeek(e,!0)}},e.prototype.seek=function(e){this._paused=!1,this._stashUsed=0,this._stashByteStart=0,this._internalSeek(e,!0)},e.prototype._internalSeek=function(e,t){this._loader.isWorking()&&this._loader.abort(),this._flushStashBuffer(t),this._loader.destroy(),this._loader=null;var i={from:e,to:-1};this._currentRange={from:i.from,to:-1},this._speedSampler.reset(),this._stashSize=this._stashInitialSize,this._createLoader(),this._loader.open(this._dataSource,i),this._onSeeked&&this._onSeeked()},e.prototype.updateUrl=function(e){if(!e||"string"!=typeof e||0===e.length)throw new h.Qn("Url must be a non-empty string!");this._dataSource.url=e},e.prototype._expandBuffer=function(e){for(var t=this._stashSize;t+10485760){var n=new Uint8Array(this._stashBuffer,0,this._stashUsed);new Uint8Array(i,0,t).set(n,0)}this._stashBuffer=i,this._bufferSize=t}},e.prototype._normalizeSpeed=function(e){var t=this._speedNormalizeList,i=t.length-1,n=0,r=0,s=i;if(e=t[n]&&e=512&&e<=1024?Math.floor(1.5*e):2*e)>8192&&(t=8192);var i=1024*t+1048576;this._bufferSize0){var s=this._stashBuffer.slice(0,this._stashUsed);if((u=this._dispatchChunks(s,this._stashByteStart))0){l=new Uint8Array(s,u);a.set(l,0),this._stashUsed=l.byteLength,this._stashByteStart+=u}}else this._stashUsed=0,this._stashByteStart+=u;this._stashUsed+e.byteLength>this._bufferSize&&(this._expandBuffer(this._stashUsed+e.byteLength),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength}else{if((u=this._dispatchChunks(e,t))this._bufferSize&&(this._expandBuffer(o),a=new Uint8Array(this._stashBuffer,0,this._bufferSize)),a.set(new Uint8Array(e,u),0),this._stashUsed+=o,this._stashByteStart=t+u}}else if(0===this._stashUsed){var o;if((u=this._dispatchChunks(e,t))this._bufferSize&&this._expandBuffer(o),(a=new Uint8Array(this._stashBuffer,0,this._bufferSize)).set(new Uint8Array(e,u),0),this._stashUsed+=o,this._stashByteStart=t+u}else{var a,u;if(this._stashUsed+e.byteLength>this._bufferSize&&this._expandBuffer(this._stashUsed+e.byteLength),(a=new Uint8Array(this._stashBuffer,0,this._bufferSize)).set(new Uint8Array(e),this._stashUsed),this._stashUsed+=e.byteLength,(u=this._dispatchChunks(this._stashBuffer.slice(0,this._stashUsed),this._stashByteStart))0){var l=new Uint8Array(this._stashBuffer,u);a.set(l,0)}this._stashUsed-=u,this._stashByteStart+=u}}},e.prototype._flushStashBuffer=function(e){if(this._stashUsed>0){var t=this._stashBuffer.slice(0,this._stashUsed),i=this._dispatchChunks(t,this._stashByteStart),n=t.byteLength-i;if(i0){var s=new Uint8Array(this._stashBuffer,0,this._bufferSize),o=new Uint8Array(t,i);s.set(o,0),this._stashUsed=o.byteLength,this._stashByteStart+=i}return 0}r.A.w(this.TAG,n+" bytes unconsumed data remain when flush buffer, dropped")}return this._stashUsed=0,this._stashByteStart=0,n}return 0},e.prototype._onLoaderComplete=function(e,t){this._flushStashBuffer(!0),this._onComplete&&this._onComplete(this._extraData)},e.prototype._onLoaderError=function(e,t){switch(r.A.e(this.TAG,"Loader error, code = "+t.code+", msg = "+t.msg),this._flushStashBuffer(!1),this._isEarlyEofReconnecting&&(this._isEarlyEofReconnecting=!1,e=o.Xv.UNRECOVERABLE_EARLY_EOF),e){case o.Xv.EARLY_EOF:if(!this._config.isLive&&this._totalLength){var i=this._currentRange.to+1;return void(i=0&&/(rv)(?::| )([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(firefox)[ \/]([\w.]+)/.exec(e)||[],n=/(ipad)/.exec(e)||/(ipod)/.exec(e)||/(windows phone)/.exec(e)||/(iphone)/.exec(e)||/(kindle)/.exec(e)||/(android)/.exec(e)||/(windows)/.exec(e)||/(mac)/.exec(e)||/(linux)/.exec(e)||/(cros)/.exec(e)||[],r={browser:t[5]||t[3]||t[1]||"",version:t[2]||t[4]||"0",majorVersion:t[4]||t[2]||"0",platform:n[0]||""},s={};if(r.browser){s[r.browser]=!0;var o=r.majorVersion.split(".");s.version={major:parseInt(r.majorVersion,10),string:r.version},o.length>1&&(s.version.minor=parseInt(o[1],10)),o.length>2&&(s.version.build=parseInt(o[2],10))}if(r.platform&&(s[r.platform]=!0),(s.chrome||s.opr||s.safari)&&(s.webkit=!0),s.rv||s.iemobile){s.rv&&delete s.rv;var a="msie";r.browser=a,s[a]=!0}if(s.edge){delete s.edge;var h="msedge";r.browser=h,s[h]=!0}if(s.opr){var u="opera";r.browser=u,s[u]=!0}if(s.safari&&s.android){var l="android";r.browser=l,s[l]=!0}for(var d in s.name=r.browser,s.platform=r.platform,i)i.hasOwnProperty(d)&&delete i[d];Object.assign(i,s)}(),t.A=i},867:function(e,t,i){"use strict";i.d(t,{Al:function(){return s},Qn:function(){return a},Xu:function(){return h},j4:function(){return o}});var n,r=(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=function(){function e(e){this._message=e}return Object.defineProperty(e.prototype,"name",{get:function(){return"RuntimeException"},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"message",{get:function(){return this._message},enumerable:!1,configurable:!0}),e.prototype.toString=function(){return this.name+": "+this.message},e}(),o=function(e){function t(t){return e.call(this,t)||this}return r(t,e),Object.defineProperty(t.prototype,"name",{get:function(){return"IllegalStateException"},enumerable:!1,configurable:!0}),t}(s),a=function(e){function t(t){return e.call(this,t)||this}return r(t,e),Object.defineProperty(t.prototype,"name",{get:function(){return"InvalidArgumentException"},enumerable:!1,configurable:!0}),t}(s),h=function(e){function t(t){return e.call(this,t)||this}return r(t,e),Object.defineProperty(t.prototype,"name",{get:function(){return"NotImplementedException"},enumerable:!1,configurable:!0}),t}(s)},856:function(e,t,i){"use strict";var n=i(211),r=i.n(n),s=function(){function e(){}return e.e=function(t,i){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var n="["+t+"] > "+i;e.ENABLE_CALLBACK&&e.emitter.emit("log","error",n),e.ENABLE_ERROR&&(console.error?console.error(n):console.warn?console.warn(n):console.log(n))},e.i=function(t,i){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var n="["+t+"] > "+i;e.ENABLE_CALLBACK&&e.emitter.emit("log","info",n),e.ENABLE_INFO&&(console.info?console.info(n):console.log(n))},e.w=function(t,i){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var n="["+t+"] > "+i;e.ENABLE_CALLBACK&&e.emitter.emit("log","warn",n),e.ENABLE_WARN&&(console.warn?console.warn(n):console.log(n))},e.d=function(t,i){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var n="["+t+"] > "+i;e.ENABLE_CALLBACK&&e.emitter.emit("log","debug",n),e.ENABLE_DEBUG&&(console.debug?console.debug(n):console.log(n))},e.v=function(t,i){t&&!e.FORCE_GLOBAL_TAG||(t=e.GLOBAL_TAG);var n="["+t+"] > "+i;e.ENABLE_CALLBACK&&e.emitter.emit("log","verbose",n),e.ENABLE_VERBOSE&&console.log(n)},e}();s.GLOBAL_TAG="flv.js",s.FORCE_GLOBAL_TAG=!1,s.ENABLE_ERROR=!0,s.ENABLE_INFO=!0,s.ENABLE_WARN=!0,s.ENABLE_DEBUG=!0,s.ENABLE_VERBOSE=!0,s.ENABLE_CALLBACK=!1,s.emitter=new(r()),t.A=s},947:function(e,t,i){"use strict";var n=i(211),r=i.n(n),s=i(856),o=function(){function e(){}return Object.defineProperty(e,"forceGlobalTag",{get:function(){return s.A.FORCE_GLOBAL_TAG},set:function(t){s.A.FORCE_GLOBAL_TAG=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"globalTag",{get:function(){return s.A.GLOBAL_TAG},set:function(t){s.A.GLOBAL_TAG=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableAll",{get:function(){return s.A.ENABLE_VERBOSE&&s.A.ENABLE_DEBUG&&s.A.ENABLE_INFO&&s.A.ENABLE_WARN&&s.A.ENABLE_ERROR},set:function(t){s.A.ENABLE_VERBOSE=t,s.A.ENABLE_DEBUG=t,s.A.ENABLE_INFO=t,s.A.ENABLE_WARN=t,s.A.ENABLE_ERROR=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableDebug",{get:function(){return s.A.ENABLE_DEBUG},set:function(t){s.A.ENABLE_DEBUG=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableVerbose",{get:function(){return s.A.ENABLE_VERBOSE},set:function(t){s.A.ENABLE_VERBOSE=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableInfo",{get:function(){return s.A.ENABLE_INFO},set:function(t){s.A.ENABLE_INFO=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableWarn",{get:function(){return s.A.ENABLE_WARN},set:function(t){s.A.ENABLE_WARN=t,e._notifyChange()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"enableError",{get:function(){return s.A.ENABLE_ERROR},set:function(t){s.A.ENABLE_ERROR=t,e._notifyChange()},enumerable:!1,configurable:!0}),e.getConfig=function(){return{globalTag:s.A.GLOBAL_TAG,forceGlobalTag:s.A.FORCE_GLOBAL_TAG,enableVerbose:s.A.ENABLE_VERBOSE,enableDebug:s.A.ENABLE_DEBUG,enableInfo:s.A.ENABLE_INFO,enableWarn:s.A.ENABLE_WARN,enableError:s.A.ENABLE_ERROR,enableCallback:s.A.ENABLE_CALLBACK}},e.applyConfig=function(e){s.A.GLOBAL_TAG=e.globalTag,s.A.FORCE_GLOBAL_TAG=e.forceGlobalTag,s.A.ENABLE_VERBOSE=e.enableVerbose,s.A.ENABLE_DEBUG=e.enableDebug,s.A.ENABLE_INFO=e.enableInfo,s.A.ENABLE_WARN=e.enableWarn,s.A.ENABLE_ERROR=e.enableError,s.A.ENABLE_CALLBACK=e.enableCallback},e._notifyChange=function(){var t=e.emitter;if(t.listenerCount("change")>0){var i=e.getConfig();t.emit("change",i)}},e.registerListener=function(t){e.emitter.addListener("change",t)},e.removeListener=function(t){e.emitter.removeListener("change",t)},e.addLogListener=function(t){s.A.emitter.addListener("log",t),s.A.emitter.listenerCount("log")>0&&(s.A.ENABLE_CALLBACK=!0,e._notifyChange())},e.removeLogListener=function(t){s.A.emitter.removeListener("log",t),0===s.A.emitter.listenerCount("log")&&(s.A.ENABLE_CALLBACK=!1,e._notifyChange())},e}();o.emitter=new(r()),t.A=o},811:function(e,t,i){"use strict";var n=function(){function e(){}return e.install=function(){Object.setPrototypeOf=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},Object.assign=Object.assign||function(e){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),i=1;i postsJSON\n values[1] // => commentsJSON\n\n return values;\n });\n ```\n\n @class Promise\n @param {Function} resolver\n Useful for tooling.\n @constructor\n*/\n\nvar Promise$1 = function () {\n function Promise(resolver) {\n this[PROMISE_ID] = nextId();\n this._result = this._state = undefined;\n this._subscribers = [];\n\n if (noop !== resolver) {\n typeof resolver !== 'function' && needsResolver();\n this instanceof Promise ? initializePromise(this, resolver) : needsNew();\n }\n }\n\n /**\n The primary way of interacting with a promise is through its `then` method,\n which registers callbacks to receive either a promise's eventual value or the\n reason why the promise cannot be fulfilled.\n ```js\n findUser().then(function(user){\n // user is available\n }, function(reason){\n // user is unavailable, and you are given the reason why\n });\n ```\n Chaining\n --------\n The return value of `then` is itself a promise. This second, 'downstream'\n promise is resolved with the return value of the first promise's fulfillment\n or rejection handler, or rejected if the handler throws an exception.\n ```js\n findUser().then(function (user) {\n return user.name;\n }, function (reason) {\n return 'default name';\n }).then(function (userName) {\n // If `findUser` fulfilled, `userName` will be the user's name, otherwise it\n // will be `'default name'`\n });\n findUser().then(function (user) {\n throw new Error('Found user, but still unhappy');\n }, function (reason) {\n throw new Error('`findUser` rejected and we're unhappy');\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.\n // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.\n });\n ```\n If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.\n ```js\n findUser().then(function (user) {\n throw new PedagogicalException('Upstream error');\n }).then(function (value) {\n // never reached\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // The `PedgagocialException` is propagated all the way down to here\n });\n ```\n Assimilation\n ------------\n Sometimes the value you want to propagate to a downstream promise can only be\n retrieved asynchronously. This can be achieved by returning a promise in the\n fulfillment or rejection handler. The downstream promise will then be pending\n until the returned promise is settled. This is called *assimilation*.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // The user's comments are now available\n });\n ```\n If the assimliated promise rejects, then the downstream promise will also reject.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // If `findCommentsByAuthor` fulfills, we'll have the value here\n }, function (reason) {\n // If `findCommentsByAuthor` rejects, we'll have the reason here\n });\n ```\n Simple Example\n --------------\n Synchronous Example\n ```javascript\n let result;\n try {\n result = findResult();\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n findResult(function(result, err){\n if (err) {\n // failure\n } else {\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findResult().then(function(result){\n // success\n }, function(reason){\n // failure\n });\n ```\n Advanced Example\n --------------\n Synchronous Example\n ```javascript\n let author, books;\n try {\n author = findAuthor();\n books = findBooksByAuthor(author);\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n function foundBooks(books) {\n }\n function failure(reason) {\n }\n findAuthor(function(author, err){\n if (err) {\n failure(err);\n // failure\n } else {\n try {\n findBoooksByAuthor(author, function(books, err) {\n if (err) {\n failure(err);\n } else {\n try {\n foundBooks(books);\n } catch(reason) {\n failure(reason);\n }\n }\n });\n } catch(error) {\n failure(err);\n }\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findAuthor().\n then(findBooksByAuthor).\n then(function(books){\n // found books\n }).catch(function(reason){\n // something went wrong\n });\n ```\n @method then\n @param {Function} onFulfilled\n @param {Function} onRejected\n Useful for tooling.\n @return {Promise}\n */\n\n /**\n `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same\n as the catch block of a try/catch statement.\n ```js\n function findAuthor(){\n throw new Error('couldn't find that author');\n }\n // synchronous\n try {\n findAuthor();\n } catch(reason) {\n // something went wrong\n }\n // async with promises\n findAuthor().catch(function(reason){\n // something went wrong\n });\n ```\n @method catch\n @param {Function} onRejection\n Useful for tooling.\n @return {Promise}\n */\n\n\n Promise.prototype.catch = function _catch(onRejection) {\n return this.then(null, onRejection);\n };\n\n /**\n `finally` will be invoked regardless of the promise's fate just as native\n try/catch/finally behaves\n \n Synchronous example:\n \n ```js\n findAuthor() {\n if (Math.random() > 0.5) {\n throw new Error();\n }\n return new Author();\n }\n \n try {\n return findAuthor(); // succeed or fail\n } catch(error) {\n return findOtherAuther();\n } finally {\n // always runs\n // doesn't affect the return value\n }\n ```\n \n Asynchronous example:\n \n ```js\n findAuthor().catch(function(reason){\n return findOtherAuther();\n }).finally(function(){\n // author was either found, or not\n });\n ```\n \n @method finally\n @param {Function} callback\n @return {Promise}\n */\n\n\n Promise.prototype.finally = function _finally(callback) {\n var promise = this;\n var constructor = promise.constructor;\n\n if (isFunction(callback)) {\n return promise.then(function (value) {\n return constructor.resolve(callback()).then(function () {\n return value;\n });\n }, function (reason) {\n return constructor.resolve(callback()).then(function () {\n throw reason;\n });\n });\n }\n\n return promise.then(callback, callback);\n };\n\n return Promise;\n}();\n\nPromise$1.prototype.then = then;\nPromise$1.all = all;\nPromise$1.race = race;\nPromise$1.resolve = resolve$1;\nPromise$1.reject = reject$1;\nPromise$1._setScheduler = setScheduler;\nPromise$1._setAsap = setAsap;\nPromise$1._asap = asap;\n\n/*global self*/\nfunction polyfill() {\n var local = void 0;\n\n if (typeof global !== 'undefined') {\n local = global;\n } else if (typeof self !== 'undefined') {\n local = self;\n } else {\n try {\n local = Function('return this')();\n } catch (e) {\n throw new Error('polyfill failed because global object is unavailable in this environment');\n }\n }\n\n var P = local.Promise;\n\n if (P) {\n var promiseToString = null;\n try {\n promiseToString = Object.prototype.toString.call(P.resolve());\n } catch (e) {\n // silently ignored\n }\n\n if (promiseToString === '[object Promise]' && !P.cast) {\n return;\n }\n }\n\n local.Promise = Promise$1;\n}\n\n// Strange compat..\nPromise$1.polyfill = polyfill;\nPromise$1.Promise = Promise$1;\n\nreturn Promise$1;\n\n})));\n\n\n\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function errorListener(err) {\n emitter.removeListener(name, resolver);\n reject(err);\n }\n\n function resolver() {\n if (typeof emitter.removeListener === 'function') {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n\n eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });\n if (name !== 'error') {\n addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });\n }\n });\n}\n\nfunction addErrorHandlerIfEventEmitter(emitter, handler, flags) {\n if (typeof emitter.on === 'function') {\n eventTargetAgnosticAddListener(emitter, 'error', handler, flags);\n }\n}\n\nfunction eventTargetAgnosticAddListener(emitter, name, listener, flags) {\n if (typeof emitter.on === 'function') {\n if (flags.once) {\n emitter.once(name, listener);\n } else {\n emitter.on(name, listener);\n }\n } else if (typeof emitter.addEventListener === 'function') {\n // EventTarget does not have `error` event semantics like Node\n // EventEmitters, we do not listen for `error` events here.\n emitter.addEventListener(name, function wrapListener(arg) {\n // IE does not have builtin `{ once: true }` support so we\n // have to do it manually.\n if (flags.once) {\n emitter.removeEventListener(name, wrapListener);\n }\n listener(arg);\n });\n } else {\n throw new TypeError('The \"emitter\" argument must be of type EventEmitter. Received type ' + typeof emitter);\n }\n}\n","function webpackBootstrapFunc (modules) {\n/******/ // The module cache\n/******/ var installedModules = {};\n\n/******/ // The require function\n/******/ function __webpack_require__(moduleId) {\n\n/******/ // Check if module is in cache\n/******/ if(installedModules[moduleId])\n/******/ return installedModules[moduleId].exports;\n\n/******/ // Create a new module (and put it into the cache)\n/******/ var module = installedModules[moduleId] = {\n/******/ i: moduleId,\n/******/ l: false,\n/******/ exports: {}\n/******/ };\n\n/******/ // Execute the module function\n/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n/******/ // Flag the module as loaded\n/******/ module.l = true;\n\n/******/ // Return the exports of the module\n/******/ return module.exports;\n/******/ }\n\n/******/ // expose the modules object (__webpack_modules__)\n/******/ __webpack_require__.m = modules;\n\n/******/ // expose the module cache\n/******/ __webpack_require__.c = installedModules;\n\n/******/ // identity function for calling harmony imports with the correct context\n/******/ __webpack_require__.i = function(value) { return value; };\n\n/******/ // define getter function for harmony exports\n/******/ __webpack_require__.d = function(exports, name, getter) {\n/******/ if(!__webpack_require__.o(exports, name)) {\n/******/ Object.defineProperty(exports, name, {\n/******/ configurable: false,\n/******/ enumerable: true,\n/******/ get: getter\n/******/ });\n/******/ }\n/******/ };\n\n/******/ // define __esModule on exports\n/******/ __webpack_require__.r = function(exports) {\n/******/ Object.defineProperty(exports, '__esModule', { value: true });\n/******/ };\n\n/******/ // getDefaultExport function for compatibility with non-harmony modules\n/******/ __webpack_require__.n = function(module) {\n/******/ var getter = module && module.__esModule ?\n/******/ function getDefault() { return module['default']; } :\n/******/ function getModuleExports() { return module; };\n/******/ __webpack_require__.d(getter, 'a', getter);\n/******/ return getter;\n/******/ };\n\n/******/ // Object.prototype.hasOwnProperty.call\n/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n/******/ // __webpack_public_path__\n/******/ __webpack_require__.p = \"/\";\n\n/******/ // on error function for async loading\n/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE)\n return f.default || f // try to call default if defined to also support babel esmodule exports\n}\n\nvar moduleNameReqExp = '[\\\\.|\\\\-|\\\\+|\\\\w|\\/|@]+'\nvar dependencyRegExp = '\\\\(\\\\s*(\\/\\\\*.*?\\\\*\\/)?\\\\s*.*?(' + moduleNameReqExp + ').*?\\\\)' // additional chars when output.pathinfo is true\n\n// http://stackoverflow.com/a/2593661/130442\nfunction quoteRegExp (str) {\n return (str + '').replace(/[.?*+^$[\\]\\\\(){}|-]/g, '\\\\$&')\n}\n\nfunction isNumeric(n) {\n return !isNaN(1 * n); // 1 * n converts integers, integers as string (\"123\"), 1e3 and \"1e3\" to integers and strings to NaN\n}\n\nfunction getModuleDependencies (sources, module, queueName) {\n var retval = {}\n retval[queueName] = []\n\n var fnString = module.toString()\n var wrapperSignature = fnString.match(/^function\\s?\\w*\\(\\w+,\\s*\\w+,\\s*(\\w+)\\)/)\n if (!wrapperSignature) return retval\n var webpackRequireName = wrapperSignature[1]\n\n // main bundle deps\n var re = new RegExp('(\\\\\\\\n|\\\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g')\n var match\n while ((match = re.exec(fnString))) {\n if (match[3] === 'dll-reference') continue\n retval[queueName].push(match[3])\n }\n\n // dll deps\n re = new RegExp('\\\\(' + quoteRegExp(webpackRequireName) + '\\\\(\"(dll-reference\\\\s(' + moduleNameReqExp + '))\"\\\\)\\\\)' + dependencyRegExp, 'g')\n while ((match = re.exec(fnString))) {\n if (!sources[match[2]]) {\n retval[queueName].push(match[1])\n sources[match[2]] = __webpack_require__(match[1]).m\n }\n retval[match[2]] = retval[match[2]] || []\n retval[match[2]].push(match[4])\n }\n\n // convert 1e3 back to 1000 - this can be important after uglify-js converted 1000 to 1e3\n var keys = Object.keys(retval);\n for (var i = 0; i < keys.length; i++) {\n for (var j = 0; j < retval[keys[i]].length; j++) {\n if (isNumeric(retval[keys[i]][j])) {\n retval[keys[i]][j] = 1 * retval[keys[i]][j];\n }\n }\n }\n\n return retval\n}\n\nfunction hasValuesInQueues (queues) {\n var keys = Object.keys(queues)\n return keys.reduce(function (hasValues, key) {\n return hasValues || queues[key].length > 0\n }, false)\n}\n\nfunction getRequiredModules (sources, moduleId) {\n var modulesQueue = {\n main: [moduleId]\n }\n var requiredModules = {\n main: []\n }\n var seenModules = {\n main: {}\n }\n\n while (hasValuesInQueues(modulesQueue)) {\n var queues = Object.keys(modulesQueue)\n for (var i = 0; i < queues.length; i++) {\n var queueName = queues[i]\n var queue = modulesQueue[queueName]\n var moduleToCheck = queue.pop()\n seenModules[queueName] = seenModules[queueName] || {}\n if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue\n seenModules[queueName][moduleToCheck] = true\n requiredModules[queueName] = requiredModules[queueName] || []\n requiredModules[queueName].push(moduleToCheck)\n var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName)\n var newModulesKeys = Object.keys(newModules)\n for (var j = 0; j < newModulesKeys.length; j++) {\n modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || []\n modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]])\n }\n }\n }\n\n return requiredModules\n}\n\nmodule.exports = function (moduleId, options) {\n options = options || {}\n var sources = {\n main: __webpack_modules__\n }\n\n var requiredModules = options.all ? { main: Object.keys(sources.main) } : getRequiredModules(sources, moduleId)\n\n var src = ''\n\n Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) {\n var entryModule = 0\n while (requiredModules[module][entryModule]) {\n entryModule++\n }\n requiredModules[module].push(entryModule)\n sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })'\n src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\\n'\n })\n\n src = src + 'new ((' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '}))(self);'\n\n var blob = new window.Blob([src], { type: 'text/javascript' })\n if (options.bare) { return blob }\n\n var URL = window.URL || window.webkitURL || window.mozURL || window.msURL\n\n var workerUrl = URL.createObjectURL(blob)\n var worker = new window.Worker(workerUrl)\n worker.objectURL = workerUrl\n\n return worker\n}\n","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nclass MediaInfo {\r\n\r\n constructor() {\r\n this.mimeType = null;\r\n this.duration = null;\r\n\r\n this.hasAudio = null;\r\n this.hasVideo = null;\r\n this.audioCodec = null;\r\n this.videoCodec = null;\r\n this.audioDataRate = null;\r\n this.videoDataRate = null;\r\n\r\n this.audioSampleRate = null;\r\n this.audioChannelCount = null;\r\n\r\n this.width = null;\r\n this.height = null;\r\n this.fps = null;\r\n this.profile = null;\r\n this.level = null;\r\n this.refFrames = null;\r\n this.chromaFormat = null;\r\n this.sarNum = null;\r\n this.sarDen = null;\r\n\r\n this.metadata = null;\r\n this.segments = null; // MediaInfo[]\r\n this.segmentCount = null;\r\n this.hasKeyframesIndex = null;\r\n this.keyframesIndex = null;\r\n }\r\n\r\n isComplete() {\r\n let audioInfoComplete = (this.hasAudio === false) ||\r\n (this.hasAudio === true &&\r\n this.audioCodec != null &&\r\n this.audioSampleRate != null &&\r\n this.audioChannelCount != null);\r\n\r\n let videoInfoComplete = (this.hasVideo === false) ||\r\n (this.hasVideo === true &&\r\n this.videoCodec != null &&\r\n this.width != null &&\r\n this.height != null &&\r\n this.fps != null &&\r\n this.profile != null &&\r\n this.level != null &&\r\n this.refFrames != null &&\r\n this.chromaFormat != null &&\r\n this.sarNum != null &&\r\n this.sarDen != null);\r\n\r\n // keyframesIndex may not be present\r\n return this.mimeType != null &&\r\n this.duration != null &&\r\n this.metadata != null &&\r\n this.hasKeyframesIndex != null &&\r\n audioInfoComplete &&\r\n videoInfoComplete;\r\n }\r\n\r\n isSeekable() {\r\n return this.hasKeyframesIndex === true;\r\n }\r\n\r\n getNearestKeyframe(milliseconds) {\r\n if (this.keyframesIndex == null) {\r\n return null;\r\n }\r\n\r\n let table = this.keyframesIndex;\r\n let keyframeIdx = this._search(table.times, milliseconds);\r\n\r\n return {\r\n index: keyframeIdx,\r\n milliseconds: table.times[keyframeIdx],\r\n fileposition: table.filepositions[keyframeIdx]\r\n };\r\n }\r\n\r\n _search(list, value) {\r\n let idx = 0;\r\n\r\n let last = list.length - 1;\r\n let mid = 0;\r\n let lbound = 0;\r\n let ubound = last;\r\n\r\n if (value < list[0]) {\r\n idx = 0;\r\n lbound = ubound + 1; // skip search\r\n }\r\n\r\n while (lbound <= ubound) {\r\n mid = lbound + Math.floor((ubound - lbound) / 2);\r\n if (mid === last || (value >= list[mid] && value < list[mid + 1])) {\r\n idx = mid;\r\n break;\r\n } else if (list[mid] < value) {\r\n lbound = mid + 1;\r\n } else {\r\n ubound = mid - 1;\r\n }\r\n }\r\n\r\n return idx;\r\n }\r\n\r\n}\r\n\r\nexport default MediaInfo;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n// Represents an media sample (audio / video)\r\nexport class SampleInfo {\r\n\r\n constructor(dts, pts, duration, originalDts, isSync) {\r\n this.dts = dts;\r\n this.pts = pts;\r\n this.duration = duration;\r\n this.originalDts = originalDts;\r\n this.isSyncPoint = isSync;\r\n this.fileposition = null;\r\n }\r\n\r\n}\r\n\r\n// Media Segment concept is defined in Media Source Extensions spec.\r\n// Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box.\r\nexport class MediaSegmentInfo {\r\n\r\n constructor() {\r\n this.beginDts = 0;\r\n this.endDts = 0;\r\n this.beginPts = 0;\r\n this.endPts = 0;\r\n this.originalBeginDts = 0;\r\n this.originalEndDts = 0;\r\n this.syncPoints = []; // SampleInfo[n], for video IDR frames only\r\n this.firstSample = null; // SampleInfo\r\n this.lastSample = null; // SampleInfo\r\n }\r\n\r\n appendSyncPoint(sampleInfo) { // also called Random Access Point\r\n sampleInfo.isSyncPoint = true;\r\n this.syncPoints.push(sampleInfo);\r\n }\r\n\r\n}\r\n\r\n// Ordered list for recording video IDR frames, sorted by originalDts\r\nexport class IDRSampleList {\r\n\r\n constructor() {\r\n this._list = [];\r\n }\r\n\r\n clear() {\r\n this._list = [];\r\n }\r\n\r\n appendArray(syncPoints) {\r\n let list = this._list;\r\n\r\n if (syncPoints.length === 0) {\r\n return;\r\n }\r\n\r\n if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) {\r\n this.clear();\r\n }\r\n\r\n Array.prototype.push.apply(list, syncPoints);\r\n }\r\n\r\n getLastSyncPointBeforeDts(dts) {\r\n if (this._list.length == 0) {\r\n return null;\r\n }\r\n\r\n let list = this._list;\r\n let idx = 0;\r\n let last = list.length - 1;\r\n let mid = 0;\r\n let lbound = 0;\r\n let ubound = last;\r\n\r\n if (dts < list[0].dts) {\r\n idx = 0;\r\n lbound = ubound + 1;\r\n }\r\n\r\n while (lbound <= ubound) {\r\n mid = lbound + Math.floor((ubound - lbound) / 2);\r\n if (mid === last || (dts >= list[mid].dts && dts < list[mid + 1].dts)) {\r\n idx = mid;\r\n break;\r\n } else if (list[mid].dts < dts) {\r\n lbound = mid + 1;\r\n } else {\r\n ubound = mid - 1;\r\n }\r\n }\r\n return this._list[idx];\r\n }\r\n\r\n}\r\n\r\n// Data structure for recording information of media segments in single track.\r\nexport class MediaSegmentInfoList {\r\n\r\n constructor(type) {\r\n this._type = type;\r\n this._list = [];\r\n this._lastAppendLocation = -1; // cached last insert location\r\n }\r\n\r\n get type() {\r\n return this._type;\r\n }\r\n\r\n get length() {\r\n return this._list.length;\r\n }\r\n\r\n isEmpty() {\r\n return this._list.length === 0;\r\n }\r\n\r\n clear() {\r\n this._list = [];\r\n this._lastAppendLocation = -1;\r\n }\r\n\r\n _searchNearestSegmentBefore(originalBeginDts) {\r\n let list = this._list;\r\n if (list.length === 0) {\r\n return -2;\r\n }\r\n let last = list.length - 1;\r\n let mid = 0;\r\n let lbound = 0;\r\n let ubound = last;\r\n\r\n let idx = 0;\r\n\r\n if (originalBeginDts < list[0].originalBeginDts) {\r\n idx = -1;\r\n return idx;\r\n }\r\n\r\n while (lbound <= ubound) {\r\n mid = lbound + Math.floor((ubound - lbound) / 2);\r\n if (mid === last || (originalBeginDts > list[mid].lastSample.originalDts &&\r\n (originalBeginDts < list[mid + 1].originalBeginDts))) {\r\n idx = mid;\r\n break;\r\n } else if (list[mid].originalBeginDts < originalBeginDts) {\r\n lbound = mid + 1;\r\n } else {\r\n ubound = mid - 1;\r\n }\r\n }\r\n return idx;\r\n }\r\n\r\n _searchNearestSegmentAfter(originalBeginDts) {\r\n return this._searchNearestSegmentBefore(originalBeginDts) + 1;\r\n }\r\n\r\n append(mediaSegmentInfo) {\r\n let list = this._list;\r\n let msi = mediaSegmentInfo;\r\n let lastAppendIdx = this._lastAppendLocation;\r\n let insertIdx = 0;\r\n\r\n if (lastAppendIdx !== -1 && lastAppendIdx < list.length &&\r\n msi.originalBeginDts >= list[lastAppendIdx].lastSample.originalDts &&\r\n ((lastAppendIdx === list.length - 1) ||\r\n (lastAppendIdx < list.length - 1 &&\r\n msi.originalBeginDts < list[lastAppendIdx + 1].originalBeginDts))) {\r\n insertIdx = lastAppendIdx + 1; // use cached location idx\r\n } else {\r\n if (list.length > 0) {\r\n insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1;\r\n }\r\n }\r\n\r\n this._lastAppendLocation = insertIdx;\r\n this._list.splice(insertIdx, 0, msi);\r\n }\r\n\r\n getLastSegmentBefore(originalBeginDts) {\r\n let idx = this._searchNearestSegmentBefore(originalBeginDts);\r\n if (idx >= 0) {\r\n return this._list[idx];\r\n } else { // -1\r\n return null;\r\n }\r\n }\r\n\r\n getLastSampleBefore(originalBeginDts) {\r\n let segment = this.getLastSegmentBefore(originalBeginDts);\r\n if (segment != null) {\r\n return segment.lastSample;\r\n } else {\r\n return null;\r\n }\r\n }\r\n\r\n getLastSyncPointBefore(originalBeginDts) {\r\n let segmentIdx = this._searchNearestSegmentBefore(originalBeginDts);\r\n let syncPoints = this._list[segmentIdx].syncPoints;\r\n while (syncPoints.length === 0 && segmentIdx > 0) {\r\n segmentIdx--;\r\n syncPoints = this._list[segmentIdx].syncPoints;\r\n }\r\n if (syncPoints.length > 0) {\r\n return syncPoints[syncPoints.length - 1];\r\n } else {\r\n return null;\r\n }\r\n }\r\n\r\n}","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * This file is derived from C++ project libWinTF8 (https://github.com/m13253/libWinTF8)\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nfunction checkContinuation(uint8array, start, checkLength) {\r\n let array = uint8array;\r\n if (start + checkLength < array.length) {\r\n while (checkLength--) {\r\n if ((array[++start] & 0xC0) !== 0x80)\r\n return false;\r\n }\r\n return true;\r\n } else {\r\n return false;\r\n }\r\n}\r\n\r\nfunction decodeUTF8(uint8array) {\r\n let out = [];\r\n let input = uint8array;\r\n let i = 0;\r\n let length = uint8array.length;\r\n\r\n while (i < length) {\r\n if (input[i] < 0x80) {\r\n out.push(String.fromCharCode(input[i]));\r\n ++i;\r\n continue;\r\n } else if (input[i] < 0xC0) {\r\n // fallthrough\r\n } else if (input[i] < 0xE0) {\r\n if (checkContinuation(input, i, 1)) {\r\n let ucs4 = (input[i] & 0x1F) << 6 | (input[i + 1] & 0x3F);\r\n if (ucs4 >= 0x80) {\r\n out.push(String.fromCharCode(ucs4 & 0xFFFF));\r\n i += 2;\r\n continue;\r\n }\r\n }\r\n } else if (input[i] < 0xF0) {\r\n if (checkContinuation(input, i, 2)) {\r\n let ucs4 = (input[i] & 0xF) << 12 | (input[i + 1] & 0x3F) << 6 | input[i + 2] & 0x3F;\r\n if (ucs4 >= 0x800 && (ucs4 & 0xF800) !== 0xD800) {\r\n out.push(String.fromCharCode(ucs4 & 0xFFFF));\r\n i += 3;\r\n continue;\r\n }\r\n }\r\n } else if (input[i] < 0xF8) {\r\n if (checkContinuation(input, i, 3)) {\r\n let ucs4 = (input[i] & 0x7) << 18 | (input[i + 1] & 0x3F) << 12\r\n | (input[i + 2] & 0x3F) << 6 | (input[i + 3] & 0x3F);\r\n if (ucs4 > 0x10000 && ucs4 < 0x110000) {\r\n ucs4 -= 0x10000;\r\n out.push(String.fromCharCode((ucs4 >>> 10) | 0xD800));\r\n out.push(String.fromCharCode((ucs4 & 0x3FF) | 0xDC00));\r\n i += 4;\r\n continue;\r\n }\r\n }\r\n }\r\n out.push(String.fromCharCode(0xFFFD));\r\n ++i;\r\n }\r\n\r\n return out.join('');\r\n}\r\n\r\nexport default decodeUTF8;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport decodeUTF8 from '../utils/utf8-conv.js';\r\nimport {IllegalStateException} from '../utils/exception.js';\r\n\r\nlet le = (function () {\r\n let buf = new ArrayBuffer(2);\r\n (new DataView(buf)).setInt16(0, 256, true); // little-endian write\r\n return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE\r\n})();\r\n\r\nclass AMF {\r\n\r\n static parseScriptData(arrayBuffer, dataOffset, dataSize) {\r\n let data = {};\r\n\r\n try {\r\n let name = AMF.parseValue(arrayBuffer, dataOffset, dataSize);\r\n let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);\r\n\r\n data[name.data] = value.data;\r\n } catch (e) {\r\n Log.e('AMF', e.toString());\r\n }\r\n\r\n return data;\r\n }\r\n\r\n static parseObject(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 3) {\r\n throw new IllegalStateException('Data not enough when parse ScriptDataObject');\r\n }\r\n let name = AMF.parseString(arrayBuffer, dataOffset, dataSize);\r\n let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);\r\n let isObjectEnd = value.objectEnd;\r\n\r\n return {\r\n data: {\r\n name: name.data,\r\n value: value.data\r\n },\r\n size: name.size + value.size,\r\n objectEnd: isObjectEnd\r\n };\r\n }\r\n\r\n static parseVariable(arrayBuffer, dataOffset, dataSize) {\r\n return AMF.parseObject(arrayBuffer, dataOffset, dataSize);\r\n }\r\n\r\n static parseString(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 2) {\r\n throw new IllegalStateException('Data not enough when parse String');\r\n }\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n let length = v.getUint16(0, !le);\r\n\r\n let str;\r\n if (length > 0) {\r\n str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 2, length));\r\n } else {\r\n str = '';\r\n }\r\n\r\n return {\r\n data: str,\r\n size: 2 + length\r\n };\r\n }\r\n\r\n static parseLongString(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 4) {\r\n throw new IllegalStateException('Data not enough when parse LongString');\r\n }\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n let length = v.getUint32(0, !le);\r\n\r\n let str;\r\n if (length > 0) {\r\n str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 4, length));\r\n } else {\r\n str = '';\r\n }\r\n\r\n return {\r\n data: str,\r\n size: 4 + length\r\n };\r\n }\r\n\r\n static parseDate(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 10) {\r\n throw new IllegalStateException('Data size invalid when parse Date');\r\n }\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n let timestamp = v.getFloat64(0, !le);\r\n let localTimeOffset = v.getInt16(8, !le);\r\n timestamp += localTimeOffset * 60 * 1000; // get UTC time\r\n\r\n return {\r\n data: new Date(timestamp),\r\n size: 8 + 2\r\n };\r\n }\r\n\r\n static parseValue(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 1) {\r\n throw new IllegalStateException('Data not enough when parse Value');\r\n }\r\n\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n\r\n let offset = 1;\r\n let type = v.getUint8(0);\r\n let value;\r\n let objectEnd = false;\r\n\r\n try {\r\n switch (type) {\r\n case 0: // Number(Double) type\r\n value = v.getFloat64(1, !le);\r\n offset += 8;\r\n break;\r\n case 1: { // Boolean type\r\n let b = v.getUint8(1);\r\n value = b ? true : false;\r\n offset += 1;\r\n break;\r\n }\r\n case 2: { // String type\r\n let amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);\r\n value = amfstr.data;\r\n offset += amfstr.size;\r\n break;\r\n }\r\n case 3: { // Object(s) type\r\n value = {};\r\n let terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd\r\n if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {\r\n terminal = 3;\r\n }\r\n while (offset < dataSize - 4) { // 4 === type(UI8) + ScriptDataObjectEnd(UI24)\r\n let amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);\r\n if (amfobj.objectEnd)\r\n break;\r\n value[amfobj.data.name] = amfobj.data.value;\r\n offset += amfobj.size;\r\n }\r\n if (offset <= dataSize - 3) {\r\n let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;\r\n if (marker === 9) {\r\n offset += 3;\r\n }\r\n }\r\n break;\r\n }\r\n case 8: { // ECMA array type (Mixed array)\r\n value = {};\r\n offset += 4; // ECMAArrayLength(UI32)\r\n let terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd\r\n if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {\r\n terminal = 3;\r\n }\r\n while (offset < dataSize - 8) { // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24)\r\n let amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);\r\n if (amfvar.objectEnd)\r\n break;\r\n value[amfvar.data.name] = amfvar.data.value;\r\n offset += amfvar.size;\r\n }\r\n if (offset <= dataSize - 3) {\r\n let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;\r\n if (marker === 9) {\r\n offset += 3;\r\n }\r\n }\r\n break;\r\n }\r\n case 9: // ScriptDataObjectEnd\r\n value = undefined;\r\n offset = 1;\r\n objectEnd = true;\r\n break;\r\n case 10: { // Strict array type\r\n // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf\r\n value = [];\r\n let strictArrayLength = v.getUint32(1, !le);\r\n offset += 4;\r\n for (let i = 0; i < strictArrayLength; i++) {\r\n let val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset);\r\n value.push(val.data);\r\n offset += val.size;\r\n }\r\n break;\r\n }\r\n case 11: { // Date type\r\n let date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1);\r\n value = date.data;\r\n offset += date.size;\r\n break;\r\n }\r\n case 12: { // Long string type\r\n let amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);\r\n value = amfLongStr.data;\r\n offset += amfLongStr.size;\r\n break;\r\n }\r\n default:\r\n // ignore and skip\r\n offset = dataSize;\r\n Log.w('AMF', 'Unsupported AMF value type ' + type);\r\n }\r\n } catch (e) {\r\n Log.e('AMF', e.toString());\r\n }\r\n\r\n return {\r\n data: value,\r\n size: offset,\r\n objectEnd: objectEnd\r\n };\r\n }\r\n\r\n}\r\n\r\nexport default AMF;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport {IllegalStateException, InvalidArgumentException} from '../utils/exception.js';\r\n\r\n// Exponential-Golomb buffer decoder\r\nclass ExpGolomb {\r\n\r\n constructor(uint8array) {\r\n this.TAG = 'ExpGolomb';\r\n\r\n this._buffer = uint8array;\r\n this._buffer_index = 0;\r\n this._total_bytes = uint8array.byteLength;\r\n this._total_bits = uint8array.byteLength * 8;\r\n this._current_word = 0;\r\n this._current_word_bits_left = 0;\r\n }\r\n\r\n destroy() {\r\n this._buffer = null;\r\n }\r\n\r\n _fillCurrentWord() {\r\n let buffer_bytes_left = this._total_bytes - this._buffer_index;\r\n if (buffer_bytes_left <= 0)\r\n throw new IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available');\r\n\r\n let bytes_read = Math.min(4, buffer_bytes_left);\r\n let word = new Uint8Array(4);\r\n word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read));\r\n this._current_word = new DataView(word.buffer).getUint32(0, false);\r\n\r\n this._buffer_index += bytes_read;\r\n this._current_word_bits_left = bytes_read * 8;\r\n }\r\n\r\n readBits(bits) {\r\n if (bits > 32)\r\n throw new InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!');\r\n\r\n if (bits <= this._current_word_bits_left) {\r\n let result = this._current_word >>> (32 - bits);\r\n this._current_word <<= bits;\r\n this._current_word_bits_left -= bits;\r\n return result;\r\n }\r\n\r\n let result = this._current_word_bits_left ? this._current_word : 0;\r\n result = result >>> (32 - this._current_word_bits_left);\r\n let bits_need_left = bits - this._current_word_bits_left;\r\n\r\n this._fillCurrentWord();\r\n let bits_read_next = Math.min(bits_need_left, this._current_word_bits_left);\r\n\r\n let result2 = this._current_word >>> (32 - bits_read_next);\r\n this._current_word <<= bits_read_next;\r\n this._current_word_bits_left -= bits_read_next;\r\n\r\n result = (result << bits_read_next) | result2;\r\n return result;\r\n }\r\n\r\n readBool() {\r\n return this.readBits(1) === 1;\r\n }\r\n\r\n readByte() {\r\n return this.readBits(8);\r\n }\r\n\r\n _skipLeadingZero() {\r\n let zero_count;\r\n for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) {\r\n if (0 !== (this._current_word & (0x80000000 >>> zero_count))) {\r\n this._current_word <<= zero_count;\r\n this._current_word_bits_left -= zero_count;\r\n return zero_count;\r\n }\r\n }\r\n this._fillCurrentWord();\r\n return zero_count + this._skipLeadingZero();\r\n }\r\n\r\n readUEG() { // unsigned exponential golomb\r\n let leading_zeros = this._skipLeadingZero();\r\n return this.readBits(leading_zeros + 1) - 1;\r\n }\r\n\r\n readSEG() { // signed exponential golomb\r\n let value = this.readUEG();\r\n if (value & 0x01) {\r\n return (value + 1) >>> 1;\r\n } else {\r\n return -1 * (value >>> 1);\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default ExpGolomb;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport ExpGolomb from './exp-golomb.js';\r\n\r\nclass SPSParser {\r\n\r\n static _ebsp2rbsp(uint8array) {\r\n let src = uint8array;\r\n let src_length = src.byteLength;\r\n let dst = new Uint8Array(src_length);\r\n let dst_idx = 0;\r\n\r\n for (let i = 0; i < src_length; i++) {\r\n if (i >= 2) {\r\n // Unescape: Skip 0x03 after 00 00\r\n if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {\r\n continue;\r\n }\r\n }\r\n dst[dst_idx] = src[i];\r\n dst_idx++;\r\n }\r\n\r\n return new Uint8Array(dst.buffer, 0, dst_idx);\r\n }\r\n\r\n static parseSPS(uint8array) {\r\n let rbsp = SPSParser._ebsp2rbsp(uint8array);\r\n let gb = new ExpGolomb(rbsp);\r\n\r\n gb.readByte();\r\n let profile_idc = gb.readByte(); // profile_idc\r\n gb.readByte(); // constraint_set_flags[5] + reserved_zero[3]\r\n let level_idc = gb.readByte(); // level_idc\r\n gb.readUEG(); // seq_parameter_set_id\r\n\r\n let profile_string = SPSParser.getProfileString(profile_idc);\r\n let level_string = SPSParser.getLevelString(level_idc);\r\n let chroma_format_idc = 1;\r\n let chroma_format = 420;\r\n let chroma_format_table = [0, 420, 422, 444];\r\n let bit_depth = 8;\r\n\r\n if (profile_idc === 100 || profile_idc === 110 || profile_idc === 122 ||\r\n profile_idc === 244 || profile_idc === 44 || profile_idc === 83 ||\r\n profile_idc === 86 || profile_idc === 118 || profile_idc === 128 ||\r\n profile_idc === 138 || profile_idc === 144) {\r\n\r\n chroma_format_idc = gb.readUEG();\r\n if (chroma_format_idc === 3) {\r\n gb.readBits(1); // separate_colour_plane_flag\r\n }\r\n if (chroma_format_idc <= 3) {\r\n chroma_format = chroma_format_table[chroma_format_idc];\r\n }\r\n\r\n bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8\r\n gb.readUEG(); // bit_depth_chroma_minus8\r\n gb.readBits(1); // qpprime_y_zero_transform_bypass_flag\r\n if (gb.readBool()) { // seq_scaling_matrix_present_flag\r\n let scaling_list_count = (chroma_format_idc !== 3) ? 8 : 12;\r\n for (let i = 0; i < scaling_list_count; i++) {\r\n if (gb.readBool()) { // seq_scaling_list_present_flag\r\n if (i < 6) {\r\n SPSParser._skipScalingList(gb, 16);\r\n } else {\r\n SPSParser._skipScalingList(gb, 64);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n gb.readUEG(); // log2_max_frame_num_minus4\r\n let pic_order_cnt_type = gb.readUEG();\r\n if (pic_order_cnt_type === 0) {\r\n gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4\r\n } else if (pic_order_cnt_type === 1) {\r\n gb.readBits(1); // delta_pic_order_always_zero_flag\r\n gb.readSEG(); // offset_for_non_ref_pic\r\n gb.readSEG(); // offset_for_top_to_bottom_field\r\n let num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG();\r\n for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {\r\n gb.readSEG(); // offset_for_ref_frame\r\n }\r\n }\r\n let ref_frames = gb.readUEG(); // max_num_ref_frames\r\n gb.readBits(1); // gaps_in_frame_num_value_allowed_flag\r\n\r\n let pic_width_in_mbs_minus1 = gb.readUEG();\r\n let pic_height_in_map_units_minus1 = gb.readUEG();\r\n\r\n let frame_mbs_only_flag = gb.readBits(1);\r\n if (frame_mbs_only_flag === 0) {\r\n gb.readBits(1); // mb_adaptive_frame_field_flag\r\n }\r\n gb.readBits(1); // direct_8x8_inference_flag\r\n\r\n let frame_crop_left_offset = 0;\r\n let frame_crop_right_offset = 0;\r\n let frame_crop_top_offset = 0;\r\n let frame_crop_bottom_offset = 0;\r\n\r\n let frame_cropping_flag = gb.readBool();\r\n if (frame_cropping_flag) {\r\n frame_crop_left_offset = gb.readUEG();\r\n frame_crop_right_offset = gb.readUEG();\r\n frame_crop_top_offset = gb.readUEG();\r\n frame_crop_bottom_offset = gb.readUEG();\r\n }\r\n\r\n let sar_width = 1, sar_height = 1;\r\n let fps = 0, fps_fixed = true, fps_num = 0, fps_den = 0;\r\n\r\n let vui_parameters_present_flag = gb.readBool();\r\n if (vui_parameters_present_flag) {\r\n if (gb.readBool()) { // aspect_ratio_info_present_flag\r\n let aspect_ratio_idc = gb.readByte();\r\n let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];\r\n let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];\r\n\r\n if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {\r\n sar_width = sar_w_table[aspect_ratio_idc - 1];\r\n sar_height = sar_h_table[aspect_ratio_idc - 1];\r\n } else if (aspect_ratio_idc === 255) {\r\n sar_width = gb.readByte() << 8 | gb.readByte();\r\n sar_height = gb.readByte() << 8 | gb.readByte();\r\n }\r\n }\r\n\r\n if (gb.readBool()) { // overscan_info_present_flag\r\n gb.readBool(); // overscan_appropriate_flag\r\n }\r\n if (gb.readBool()) { // video_signal_type_present_flag\r\n gb.readBits(4); // video_format & video_full_range_flag\r\n if (gb.readBool()) { // colour_description_present_flag\r\n gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients\r\n }\r\n }\r\n if (gb.readBool()) { // chroma_loc_info_present_flag\r\n gb.readUEG(); // chroma_sample_loc_type_top_field\r\n gb.readUEG(); // chroma_sample_loc_type_bottom_field\r\n }\r\n if (gb.readBool()) { // timing_info_present_flag\r\n let num_units_in_tick = gb.readBits(32);\r\n let time_scale = gb.readBits(32);\r\n fps_fixed = gb.readBool(); // fixed_frame_rate_flag\r\n\r\n fps_num = time_scale;\r\n fps_den = num_units_in_tick * 2;\r\n fps = fps_num / fps_den;\r\n }\r\n }\r\n\r\n let sarScale = 1;\r\n if (sar_width !== 1 || sar_height !== 1) {\r\n sarScale = sar_width / sar_height;\r\n }\r\n\r\n let crop_unit_x = 0, crop_unit_y = 0;\r\n if (chroma_format_idc === 0) {\r\n crop_unit_x = 1;\r\n crop_unit_y = 2 - frame_mbs_only_flag;\r\n } else {\r\n let sub_wc = (chroma_format_idc === 3) ? 1 : 2;\r\n let sub_hc = (chroma_format_idc === 1) ? 2 : 1;\r\n crop_unit_x = sub_wc;\r\n crop_unit_y = sub_hc * (2 - frame_mbs_only_flag);\r\n }\r\n\r\n let codec_width = (pic_width_in_mbs_minus1 + 1) * 16;\r\n let codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16);\r\n\r\n codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x;\r\n codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y;\r\n\r\n let present_width = Math.ceil(codec_width * sarScale);\r\n\r\n gb.destroy();\r\n gb = null;\r\n\r\n return {\r\n profile_string: profile_string, // baseline, high, high10, ...\r\n level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ...\r\n bit_depth: bit_depth, // 8bit, 10bit, ...\r\n ref_frames: ref_frames,\r\n chroma_format: chroma_format, // 4:2:0, 4:2:2, ...\r\n chroma_format_string: SPSParser.getChromaFormatString(chroma_format),\r\n\r\n frame_rate: {\r\n fixed: fps_fixed,\r\n fps: fps,\r\n fps_den: fps_den,\r\n fps_num: fps_num\r\n },\r\n\r\n sar_ratio: {\r\n width: sar_width,\r\n height: sar_height\r\n },\r\n\r\n codec_size: {\r\n width: codec_width,\r\n height: codec_height\r\n },\r\n\r\n present_size: {\r\n width: present_width,\r\n height: codec_height\r\n }\r\n };\r\n }\r\n\r\n static _skipScalingList(gb, count) {\r\n let last_scale = 8, next_scale = 8;\r\n let delta_scale = 0;\r\n for (let i = 0; i < count; i++) {\r\n if (next_scale !== 0) {\r\n delta_scale = gb.readSEG();\r\n next_scale = (last_scale + delta_scale + 256) % 256;\r\n }\r\n last_scale = (next_scale === 0) ? last_scale : next_scale;\r\n }\r\n }\r\n\r\n static getProfileString(profile_idc) {\r\n switch (profile_idc) {\r\n case 66:\r\n return 'Baseline';\r\n case 77:\r\n return 'Main';\r\n case 88:\r\n return 'Extended';\r\n case 100:\r\n return 'High';\r\n case 110:\r\n return 'High10';\r\n case 122:\r\n return 'High422';\r\n case 244:\r\n return 'High444';\r\n default:\r\n return 'Unknown';\r\n }\r\n }\r\n\r\n static getLevelString(level_idc) {\r\n return (level_idc / 10).toFixed(1);\r\n }\r\n\r\n static getChromaFormatString(chroma) {\r\n switch (chroma) {\r\n case 420:\r\n return '4:2:0';\r\n case 422:\r\n return '4:2:2';\r\n case 444:\r\n return '4:4:4';\r\n default:\r\n return 'Unknown';\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default SPSParser;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport AMF from './amf-parser.js';\r\nimport SPSParser from './sps-parser.js';\r\nimport DemuxErrors from './demux-errors.js';\r\nimport MediaInfo from '../core/media-info.js';\r\nimport {IllegalStateException} from '../utils/exception.js';\r\n\r\nfunction Swap16(src) {\r\n return (((src >>> 8) & 0xFF) |\r\n ((src & 0xFF) << 8));\r\n}\r\n\r\nfunction Swap32(src) {\r\n return (((src & 0xFF000000) >>> 24) |\r\n ((src & 0x00FF0000) >>> 8) |\r\n ((src & 0x0000FF00) << 8) |\r\n ((src & 0x000000FF) << 24));\r\n}\r\n\r\nfunction ReadBig32(array, index) {\r\n return ((array[index] << 24) |\r\n (array[index + 1] << 16) |\r\n (array[index + 2] << 8) |\r\n (array[index + 3]));\r\n}\r\n\r\n\r\nclass FLVDemuxer {\r\n\r\n constructor(probeData, config) {\r\n this.TAG = 'FLVDemuxer';\r\n\r\n this._config = config;\r\n\r\n this._onError = null;\r\n this._onMediaInfo = null;\r\n this._onMetaDataArrived = null;\r\n this._onScriptDataArrived = null;\r\n this._onTrackMetadata = null;\r\n this._onDataAvailable = null;\r\n\r\n this._dataOffset = probeData.dataOffset;\r\n this._firstParse = true;\r\n this._dispatch = false;\r\n\r\n this._hasAudio = probeData.hasAudioTrack;\r\n this._hasVideo = probeData.hasVideoTrack;\r\n\r\n this._hasAudioFlagOverrided = false;\r\n this._hasVideoFlagOverrided = false;\r\n\r\n this._audioInitialMetadataDispatched = false;\r\n this._videoInitialMetadataDispatched = false;\r\n\r\n this._mediaInfo = new MediaInfo();\r\n this._mediaInfo.hasAudio = this._hasAudio;\r\n this._mediaInfo.hasVideo = this._hasVideo;\r\n this._metadata = null;\r\n this._audioMetadata = null;\r\n this._videoMetadata = null;\r\n\r\n this._naluLengthSize = 4;\r\n this._timestampBase = 0; // int32, in milliseconds\r\n this._timescale = 1000;\r\n this._duration = 0; // int32, in milliseconds\r\n this._durationOverrided = false;\r\n this._referenceFrameRate = {\r\n fixed: true,\r\n fps: 23.976,\r\n fps_num: 23976,\r\n fps_den: 1000\r\n };\r\n\r\n this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48000];\r\n\r\n this._mpegSamplingRates = [\r\n 96000, 88200, 64000, 48000, 44100, 32000,\r\n 24000, 22050, 16000, 12000, 11025, 8000, 7350\r\n ];\r\n\r\n this._mpegAudioV10SampleRateTable = [44100, 48000, 32000, 0];\r\n this._mpegAudioV20SampleRateTable = [22050, 24000, 16000, 0];\r\n this._mpegAudioV25SampleRateTable = [11025, 12000, 8000, 0];\r\n\r\n this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1];\r\n this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1];\r\n this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1];\r\n\r\n this._videoTrack = {type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0};\r\n this._audioTrack = {type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0};\r\n\r\n this._littleEndian = (function () {\r\n let buf = new ArrayBuffer(2);\r\n (new DataView(buf)).setInt16(0, 256, true); // little-endian write\r\n return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE\r\n })();\r\n }\r\n\r\n destroy() {\r\n this._mediaInfo = null;\r\n this._metadata = null;\r\n this._audioMetadata = null;\r\n this._videoMetadata = null;\r\n this._videoTrack = null;\r\n this._audioTrack = null;\r\n\r\n this._onError = null;\r\n this._onMediaInfo = null;\r\n this._onMetaDataArrived = null;\r\n this._onScriptDataArrived = null;\r\n this._onTrackMetadata = null;\r\n this._onDataAvailable = null;\r\n }\r\n\r\n static probe(buffer) {\r\n let data = new Uint8Array(buffer);\r\n let mismatch = {match: false};\r\n\r\n if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) {\r\n return mismatch;\r\n }\r\n\r\n let hasAudio = ((data[4] & 4) >>> 2) !== 0;\r\n let hasVideo = (data[4] & 1) !== 0;\r\n\r\n let offset = ReadBig32(data, 5);\r\n\r\n if (offset < 9) {\r\n return mismatch;\r\n }\r\n\r\n return {\r\n match: true,\r\n consumed: offset,\r\n dataOffset: offset,\r\n hasAudioTrack: hasAudio,\r\n hasVideoTrack: hasVideo\r\n };\r\n }\r\n\r\n bindDataSource(loader) {\r\n loader.onDataArrival = this.parseChunks.bind(this);\r\n return this;\r\n }\r\n\r\n // prototype: function(type: string, metadata: any): void\r\n get onTrackMetadata() {\r\n return this._onTrackMetadata;\r\n }\r\n\r\n set onTrackMetadata(callback) {\r\n this._onTrackMetadata = callback;\r\n }\r\n\r\n // prototype: function(mediaInfo: MediaInfo): void\r\n get onMediaInfo() {\r\n return this._onMediaInfo;\r\n }\r\n\r\n set onMediaInfo(callback) {\r\n this._onMediaInfo = callback;\r\n }\r\n\r\n get onMetaDataArrived() {\r\n return this._onMetaDataArrived;\r\n }\r\n\r\n set onMetaDataArrived(callback) {\r\n this._onMetaDataArrived = callback;\r\n }\r\n\r\n get onScriptDataArrived() {\r\n return this._onScriptDataArrived;\r\n }\r\n\r\n set onScriptDataArrived(callback) {\r\n this._onScriptDataArrived = callback;\r\n }\r\n\r\n // prototype: function(type: number, info: string): void\r\n get onError() {\r\n return this._onError;\r\n }\r\n\r\n set onError(callback) {\r\n this._onError = callback;\r\n }\r\n\r\n // prototype: function(videoTrack: any, audioTrack: any): void\r\n get onDataAvailable() {\r\n return this._onDataAvailable;\r\n }\r\n\r\n set onDataAvailable(callback) {\r\n this._onDataAvailable = callback;\r\n }\r\n\r\n // timestamp base for output samples, must be in milliseconds\r\n get timestampBase() {\r\n return this._timestampBase;\r\n }\r\n\r\n set timestampBase(base) {\r\n this._timestampBase = base;\r\n }\r\n\r\n get overridedDuration() {\r\n return this._duration;\r\n }\r\n\r\n // Force-override media duration. Must be in milliseconds, int32\r\n set overridedDuration(duration) {\r\n this._durationOverrided = true;\r\n this._duration = duration;\r\n this._mediaInfo.duration = duration;\r\n }\r\n\r\n // Force-override audio track present flag, boolean\r\n set overridedHasAudio(hasAudio) {\r\n this._hasAudioFlagOverrided = true;\r\n this._hasAudio = hasAudio;\r\n this._mediaInfo.hasAudio = hasAudio;\r\n }\r\n\r\n // Force-override video track present flag, boolean\r\n set overridedHasVideo(hasVideo) {\r\n this._hasVideoFlagOverrided = true;\r\n this._hasVideo = hasVideo;\r\n this._mediaInfo.hasVideo = hasVideo;\r\n }\r\n\r\n resetMediaInfo() {\r\n this._mediaInfo = new MediaInfo();\r\n }\r\n\r\n _isInitialMetadataDispatched() {\r\n if (this._hasAudio && this._hasVideo) { // both audio & video\r\n return this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched;\r\n }\r\n if (this._hasAudio && !this._hasVideo) { // audio only\r\n return this._audioInitialMetadataDispatched;\r\n }\r\n if (!this._hasAudio && this._hasVideo) { // video only\r\n return this._videoInitialMetadataDispatched;\r\n }\r\n return false;\r\n }\r\n\r\n // function parseChunks(chunk: ArrayBuffer, byteStart: number): number;\r\n parseChunks(chunk, byteStart) {\r\n if (!this._onError || !this._onMediaInfo || !this._onTrackMetadata || !this._onDataAvailable) {\r\n throw new IllegalStateException('Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified');\r\n }\r\n\r\n let offset = 0;\r\n let le = this._littleEndian;\r\n\r\n if (byteStart === 0) { // buffer with FLV header\r\n if (chunk.byteLength > 13) {\r\n let probeData = FLVDemuxer.probe(chunk);\r\n offset = probeData.dataOffset;\r\n } else {\r\n return 0;\r\n }\r\n }\r\n\r\n if (this._firstParse) { // handle PreviousTagSize0 before Tag1\r\n this._firstParse = false;\r\n if (byteStart + offset !== this._dataOffset) {\r\n Log.w(this.TAG, 'First time parsing but chunk byteStart invalid!');\r\n }\r\n\r\n let v = new DataView(chunk, offset);\r\n let prevTagSize0 = v.getUint32(0, !le);\r\n if (prevTagSize0 !== 0) {\r\n Log.w(this.TAG, 'PrevTagSize0 !== 0 !!!');\r\n }\r\n offset += 4;\r\n }\r\n\r\n while (offset < chunk.byteLength) {\r\n this._dispatch = true;\r\n\r\n let v = new DataView(chunk, offset);\r\n\r\n if (offset + 11 + 4 > chunk.byteLength) {\r\n // data not enough for parsing an flv tag\r\n break;\r\n }\r\n\r\n let tagType = v.getUint8(0);\r\n let dataSize = v.getUint32(0, !le) & 0x00FFFFFF;\r\n\r\n if (offset + 11 + dataSize + 4 > chunk.byteLength) {\r\n // data not enough for parsing actual data body\r\n break;\r\n }\r\n\r\n if (tagType !== 8 && tagType !== 9 && tagType !== 18) {\r\n Log.w(this.TAG, `Unsupported tag type ${tagType}, skipped`);\r\n // consume the whole tag (skip it)\r\n offset += 11 + dataSize + 4;\r\n continue;\r\n }\r\n\r\n let ts2 = v.getUint8(4);\r\n let ts1 = v.getUint8(5);\r\n let ts0 = v.getUint8(6);\r\n let ts3 = v.getUint8(7);\r\n\r\n let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24);\r\n\r\n let streamId = v.getUint32(7, !le) & 0x00FFFFFF;\r\n if (streamId !== 0) {\r\n Log.w(this.TAG, 'Meet tag which has StreamID != 0!');\r\n }\r\n\r\n let dataOffset = offset + 11;\r\n\r\n switch (tagType) {\r\n case 8: // Audio\r\n this._parseAudioData(chunk, dataOffset, dataSize, timestamp);\r\n break;\r\n case 9: // Video\r\n this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset);\r\n break;\r\n case 18: // ScriptDataObject\r\n this._parseScriptData(chunk, dataOffset, dataSize);\r\n break;\r\n }\r\n\r\n let prevTagSize = v.getUint32(11 + dataSize, !le);\r\n if (prevTagSize !== 11 + dataSize) {\r\n Log.w(this.TAG, `Invalid PrevTagSize ${prevTagSize}`);\r\n }\r\n\r\n offset += 11 + dataSize + 4; // tagBody + dataSize + prevTagSize\r\n }\r\n\r\n // dispatch parsed frames to consumer (typically, the remuxer)\r\n if (this._isInitialMetadataDispatched()) {\r\n if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {\r\n this._onDataAvailable(this._audioTrack, this._videoTrack);\r\n }\r\n }\r\n\r\n return offset; // consumed bytes, just equals latest offset index\r\n }\r\n\r\n _parseScriptData(arrayBuffer, dataOffset, dataSize) {\r\n let scriptData = AMF.parseScriptData(arrayBuffer, dataOffset, dataSize);\r\n\r\n if (scriptData.hasOwnProperty('onMetaData')) {\r\n if (scriptData.onMetaData == null || typeof scriptData.onMetaData !== 'object') {\r\n Log.w(this.TAG, 'Invalid onMetaData structure!');\r\n return;\r\n }\r\n if (this._metadata) {\r\n Log.w(this.TAG, 'Found another onMetaData tag!');\r\n }\r\n this._metadata = scriptData;\r\n let onMetaData = this._metadata.onMetaData;\r\n\r\n if (this._onMetaDataArrived) {\r\n this._onMetaDataArrived(Object.assign({}, onMetaData));\r\n }\r\n\r\n if (typeof onMetaData.hasAudio === 'boolean') { // hasAudio\r\n if (this._hasAudioFlagOverrided === false) {\r\n this._hasAudio = onMetaData.hasAudio;\r\n this._mediaInfo.hasAudio = this._hasAudio;\r\n }\r\n }\r\n if (typeof onMetaData.hasVideo === 'boolean') { // hasVideo\r\n if (this._hasVideoFlagOverrided === false) {\r\n this._hasVideo = onMetaData.hasVideo;\r\n this._mediaInfo.hasVideo = this._hasVideo;\r\n }\r\n }\r\n if (typeof onMetaData.audiodatarate === 'number') { // audiodatarate\r\n this._mediaInfo.audioDataRate = onMetaData.audiodatarate;\r\n }\r\n if (typeof onMetaData.videodatarate === 'number') { // videodatarate\r\n this._mediaInfo.videoDataRate = onMetaData.videodatarate;\r\n }\r\n if (typeof onMetaData.width === 'number') { // width\r\n this._mediaInfo.width = onMetaData.width;\r\n }\r\n if (typeof onMetaData.height === 'number') { // height\r\n this._mediaInfo.height = onMetaData.height;\r\n }\r\n if (typeof onMetaData.duration === 'number') { // duration\r\n if (!this._durationOverrided) {\r\n let duration = Math.floor(onMetaData.duration * this._timescale);\r\n this._duration = duration;\r\n this._mediaInfo.duration = duration;\r\n }\r\n } else {\r\n this._mediaInfo.duration = 0;\r\n }\r\n if (typeof onMetaData.framerate === 'number') { // framerate\r\n let fps_num = Math.floor(onMetaData.framerate * 1000);\r\n if (fps_num > 0) {\r\n let fps = fps_num / 1000;\r\n this._referenceFrameRate.fixed = true;\r\n this._referenceFrameRate.fps = fps;\r\n this._referenceFrameRate.fps_num = fps_num;\r\n this._referenceFrameRate.fps_den = 1000;\r\n this._mediaInfo.fps = fps;\r\n }\r\n }\r\n if (typeof onMetaData.keyframes === 'object') { // keyframes\r\n this._mediaInfo.hasKeyframesIndex = true;\r\n let keyframes = onMetaData.keyframes;\r\n this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(keyframes);\r\n onMetaData.keyframes = null; // keyframes has been extracted, remove it\r\n } else {\r\n this._mediaInfo.hasKeyframesIndex = false;\r\n }\r\n this._dispatch = false;\r\n this._mediaInfo.metadata = onMetaData;\r\n Log.v(this.TAG, 'Parsed onMetaData');\r\n if (this._mediaInfo.isComplete()) {\r\n this._onMediaInfo(this._mediaInfo);\r\n }\r\n }\r\n\r\n if (Object.keys(scriptData).length > 0) {\r\n if (this._onScriptDataArrived) {\r\n this._onScriptDataArrived(Object.assign({}, scriptData));\r\n }\r\n }\r\n }\r\n\r\n _parseKeyframesIndex(keyframes) {\r\n let times = [];\r\n let filepositions = [];\r\n\r\n // ignore first keyframe which is actually AVC Sequence Header (AVCDecoderConfigurationRecord)\r\n for (let i = 1; i < keyframes.times.length; i++) {\r\n let time = this._timestampBase + Math.floor(keyframes.times[i] * 1000);\r\n times.push(time);\r\n filepositions.push(keyframes.filepositions[i]);\r\n }\r\n\r\n return {\r\n times: times,\r\n filepositions: filepositions\r\n };\r\n }\r\n\r\n _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) {\r\n if (dataSize <= 1) {\r\n Log.w(this.TAG, 'Flv: Invalid audio packet, missing SoundData payload!');\r\n return;\r\n }\r\n\r\n if (this._hasAudioFlagOverrided === true && this._hasAudio === false) {\r\n // If hasAudio: false indicated explicitly in MediaDataSource,\r\n // Ignore all the audio packets\r\n return;\r\n }\r\n\r\n let le = this._littleEndian;\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n\r\n let soundSpec = v.getUint8(0);\r\n\r\n let soundFormat = soundSpec >>> 4;\r\n if (soundFormat !== 2 && soundFormat !== 10) { // MP3 or AAC\r\n this._onError(DemuxErrors.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec idx: ' + soundFormat);\r\n return;\r\n }\r\n\r\n let soundRate = 0;\r\n let soundRateIndex = (soundSpec & 12) >>> 2;\r\n if (soundRateIndex >= 0 && soundRateIndex <= 4) {\r\n soundRate = this._flvSoundRateTable[soundRateIndex];\r\n } else {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid audio sample rate idx: ' + soundRateIndex);\r\n return;\r\n }\r\n\r\n let soundSize = (soundSpec & 2) >>> 1; // unused\r\n let soundType = (soundSpec & 1);\r\n\r\n\r\n let meta = this._audioMetadata;\r\n let track = this._audioTrack;\r\n\r\n if (!meta) {\r\n if (this._hasAudio === false && this._hasAudioFlagOverrided === false) {\r\n this._hasAudio = true;\r\n this._mediaInfo.hasAudio = true;\r\n }\r\n\r\n // initial metadata\r\n meta = this._audioMetadata = {};\r\n meta.type = 'audio';\r\n meta.id = track.id;\r\n meta.timescale = this._timescale;\r\n meta.duration = this._duration;\r\n meta.audioSampleRate = soundRate;\r\n meta.channelCount = (soundType === 0 ? 1 : 2);\r\n }\r\n\r\n if (soundFormat === 10) { // AAC\r\n let aacData = this._parseAACAudioData(arrayBuffer, dataOffset + 1, dataSize - 1);\r\n if (aacData == undefined) {\r\n return;\r\n }\r\n\r\n if (aacData.packetType === 0) { // AAC sequence header (AudioSpecificConfig)\r\n if (meta.config) {\r\n Log.w(this.TAG, 'Found another AudioSpecificConfig!');\r\n }\r\n let misc = aacData.data;\r\n meta.audioSampleRate = misc.samplingRate;\r\n meta.channelCount = misc.channelCount;\r\n meta.codec = misc.codec;\r\n meta.originalCodec = misc.originalCodec;\r\n meta.config = misc.config;\r\n // The decode result of an aac sample is 1024 PCM samples\r\n meta.refSampleDuration = 1024 / meta.audioSampleRate * meta.timescale;\r\n Log.v(this.TAG, 'Parsed AudioSpecificConfig');\r\n\r\n if (this._isInitialMetadataDispatched()) {\r\n // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer\r\n if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {\r\n this._onDataAvailable(this._audioTrack, this._videoTrack);\r\n }\r\n } else {\r\n this._audioInitialMetadataDispatched = true;\r\n }\r\n // then notify new metadata\r\n this._dispatch = false;\r\n this._onTrackMetadata('audio', meta);\r\n\r\n let mi = this._mediaInfo;\r\n mi.audioCodec = meta.originalCodec;\r\n mi.audioSampleRate = meta.audioSampleRate;\r\n mi.audioChannelCount = meta.channelCount;\r\n if (mi.hasVideo) {\r\n if (mi.videoCodec != null) {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.videoCodec + ',' + mi.audioCodec + '\"';\r\n }\r\n } else {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.audioCodec + '\"';\r\n }\r\n if (mi.isComplete()) {\r\n this._onMediaInfo(mi);\r\n }\r\n } else if (aacData.packetType === 1) { // AAC raw frame data\r\n let dts = this._timestampBase + tagTimestamp;\r\n let aacSample = {unit: aacData.data, length: aacData.data.byteLength, dts: dts, pts: dts};\r\n track.samples.push(aacSample);\r\n track.length += aacData.data.length;\r\n } else {\r\n Log.e(this.TAG, `Flv: Unsupported AAC data type ${aacData.packetType}`);\r\n }\r\n } else if (soundFormat === 2) { // MP3\r\n if (!meta.codec) {\r\n // We need metadata for mp3 audio track, extract info from frame header\r\n let misc = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, true);\r\n if (misc == undefined) {\r\n return;\r\n }\r\n meta.audioSampleRate = misc.samplingRate;\r\n meta.channelCount = misc.channelCount;\r\n meta.codec = misc.codec;\r\n meta.originalCodec = misc.originalCodec;\r\n // The decode result of an mp3 sample is 1152 PCM samples\r\n meta.refSampleDuration = 1152 / meta.audioSampleRate * meta.timescale;\r\n Log.v(this.TAG, 'Parsed MPEG Audio Frame Header');\r\n\r\n this._audioInitialMetadataDispatched = true;\r\n this._onTrackMetadata('audio', meta);\r\n\r\n let mi = this._mediaInfo;\r\n mi.audioCodec = meta.codec;\r\n mi.audioSampleRate = meta.audioSampleRate;\r\n mi.audioChannelCount = meta.channelCount;\r\n mi.audioDataRate = misc.bitRate;\r\n if (mi.hasVideo) {\r\n if (mi.videoCodec != null) {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.videoCodec + ',' + mi.audioCodec + '\"';\r\n }\r\n } else {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.audioCodec + '\"';\r\n }\r\n if (mi.isComplete()) {\r\n this._onMediaInfo(mi);\r\n }\r\n }\r\n\r\n // This packet is always a valid audio packet, extract it\r\n let data = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, false);\r\n if (data == undefined) {\r\n return;\r\n }\r\n let dts = this._timestampBase + tagTimestamp;\r\n let mp3Sample = {unit: data, length: data.byteLength, dts: dts, pts: dts};\r\n track.samples.push(mp3Sample);\r\n track.length += data.length;\r\n }\r\n }\r\n\r\n _parseAACAudioData(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize <= 1) {\r\n Log.w(this.TAG, 'Flv: Invalid AAC packet, missing AACPacketType or/and Data!');\r\n return;\r\n }\r\n\r\n let result = {};\r\n let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);\r\n\r\n result.packetType = array[0];\r\n\r\n if (array[0] === 0) {\r\n result.data = this._parseAACAudioSpecificConfig(arrayBuffer, dataOffset + 1, dataSize - 1);\r\n } else {\r\n result.data = array.subarray(1);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) {\r\n let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);\r\n let config = null;\r\n\r\n /* Audio Object Type:\r\n 0: Null\r\n 1: AAC Main\r\n 2: AAC LC\r\n 3: AAC SSR (Scalable Sample Rate)\r\n 4: AAC LTP (Long Term Prediction)\r\n 5: HE-AAC / SBR (Spectral Band Replication)\r\n 6: AAC Scalable\r\n */\r\n\r\n let audioObjectType = 0;\r\n let originalAudioObjectType = 0;\r\n let audioExtensionObjectType = null;\r\n let samplingIndex = 0;\r\n let extensionSamplingIndex = null;\r\n\r\n // 5 bits\r\n audioObjectType = originalAudioObjectType = array[0] >>> 3;\r\n // 4 bits\r\n samplingIndex = ((array[0] & 0x07) << 1) | (array[1] >>> 7);\r\n if (samplingIndex < 0 || samplingIndex >= this._mpegSamplingRates.length) {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid sampling frequency index!');\r\n return;\r\n }\r\n\r\n let samplingFrequence = this._mpegSamplingRates[samplingIndex];\r\n\r\n // 4 bits\r\n let channelConfig = (array[1] & 0x78) >>> 3;\r\n if (channelConfig < 0 || channelConfig >= 8) {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: AAC invalid channel configuration');\r\n return;\r\n }\r\n\r\n if (audioObjectType === 5) { // HE-AAC?\r\n // 4 bits\r\n extensionSamplingIndex = ((array[1] & 0x07) << 1) | (array[2] >>> 7);\r\n // 5 bits\r\n audioExtensionObjectType = (array[2] & 0x7C) >>> 2;\r\n }\r\n\r\n // workarounds for various browsers\r\n let userAgent = self.navigator.userAgent.toLowerCase();\r\n\r\n if (userAgent.indexOf('firefox') !== -1) {\r\n // firefox: use SBR (HE-AAC) if freq less than 24kHz\r\n if (samplingIndex >= 6) {\r\n audioObjectType = 5;\r\n config = new Array(4);\r\n extensionSamplingIndex = samplingIndex - 3;\r\n } else { // use LC-AAC\r\n audioObjectType = 2;\r\n config = new Array(2);\r\n extensionSamplingIndex = samplingIndex;\r\n }\r\n } else if (userAgent.indexOf('android') !== -1) {\r\n // android: always use LC-AAC\r\n audioObjectType = 2;\r\n config = new Array(2);\r\n extensionSamplingIndex = samplingIndex;\r\n } else {\r\n // for other browsers, e.g. chrome...\r\n // Always use HE-AAC to make it easier to switch aac codec profile\r\n audioObjectType = 5;\r\n extensionSamplingIndex = samplingIndex;\r\n config = new Array(4);\r\n\r\n if (samplingIndex >= 6) {\r\n extensionSamplingIndex = samplingIndex - 3;\r\n } else if (channelConfig === 1) { // Mono channel\r\n audioObjectType = 2;\r\n config = new Array(2);\r\n extensionSamplingIndex = samplingIndex;\r\n }\r\n }\r\n\r\n config[0] = audioObjectType << 3;\r\n config[0] |= (samplingIndex & 0x0F) >>> 1;\r\n config[1] = (samplingIndex & 0x0F) << 7;\r\n config[1] |= (channelConfig & 0x0F) << 3;\r\n if (audioObjectType === 5) {\r\n config[1] |= ((extensionSamplingIndex & 0x0F) >>> 1);\r\n config[2] = (extensionSamplingIndex & 0x01) << 7;\r\n // extended audio object type: force to 2 (LC-AAC)\r\n config[2] |= (2 << 2);\r\n config[3] = 0;\r\n }\r\n\r\n return {\r\n config: config,\r\n samplingRate: samplingFrequence,\r\n channelCount: channelConfig,\r\n codec: 'mp4a.40.' + audioObjectType,\r\n originalCodec: 'mp4a.40.' + originalAudioObjectType\r\n };\r\n }\r\n\r\n _parseMP3AudioData(arrayBuffer, dataOffset, dataSize, requestHeader) {\r\n if (dataSize < 4) {\r\n Log.w(this.TAG, 'Flv: Invalid MP3 packet, header missing!');\r\n return;\r\n }\r\n\r\n let le = this._littleEndian;\r\n let array = new Uint8Array(arrayBuffer, dataOffset, dataSize);\r\n let result = null;\r\n\r\n if (requestHeader) {\r\n if (array[0] !== 0xFF) {\r\n return;\r\n }\r\n let ver = (array[1] >>> 3) & 0x03;\r\n let layer = (array[1] & 0x06) >> 1;\r\n\r\n let bitrate_index = (array[2] & 0xF0) >>> 4;\r\n let sampling_freq_index = (array[2] & 0x0C) >>> 2;\r\n\r\n let channel_mode = (array[3] >>> 6) & 0x03;\r\n let channel_count = channel_mode !== 3 ? 2 : 1;\r\n\r\n let sample_rate = 0;\r\n let bit_rate = 0;\r\n let object_type = 34; // Layer-3, listed in MPEG-4 Audio Object Types\r\n\r\n let codec = 'mp3';\r\n\r\n switch (ver) {\r\n case 0: // MPEG 2.5\r\n sample_rate = this._mpegAudioV25SampleRateTable[sampling_freq_index];\r\n break;\r\n case 2: // MPEG 2\r\n sample_rate = this._mpegAudioV20SampleRateTable[sampling_freq_index];\r\n break;\r\n case 3: // MPEG 1\r\n sample_rate = this._mpegAudioV10SampleRateTable[sampling_freq_index];\r\n break;\r\n }\r\n\r\n switch (layer) {\r\n case 1: // Layer 3\r\n object_type = 34;\r\n if (bitrate_index < this._mpegAudioL3BitRateTable.length) {\r\n bit_rate = this._mpegAudioL3BitRateTable[bitrate_index];\r\n }\r\n break;\r\n case 2: // Layer 2\r\n object_type = 33;\r\n if (bitrate_index < this._mpegAudioL2BitRateTable.length) {\r\n bit_rate = this._mpegAudioL2BitRateTable[bitrate_index];\r\n }\r\n break;\r\n case 3: // Layer 1\r\n object_type = 32;\r\n if (bitrate_index < this._mpegAudioL1BitRateTable.length) {\r\n bit_rate = this._mpegAudioL1BitRateTable[bitrate_index];\r\n }\r\n break;\r\n }\r\n\r\n result = {\r\n bitRate: bit_rate,\r\n samplingRate: sample_rate,\r\n channelCount: channel_count,\r\n codec: codec,\r\n originalCodec: codec\r\n };\r\n } else {\r\n result = array;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) {\r\n if (dataSize <= 1) {\r\n Log.w(this.TAG, 'Flv: Invalid video packet, missing VideoData payload!');\r\n return;\r\n }\r\n\r\n if (this._hasVideoFlagOverrided === true && this._hasVideo === false) {\r\n // If hasVideo: false indicated explicitly in MediaDataSource,\r\n // Ignore all the video packets\r\n return;\r\n }\r\n\r\n let spec = (new Uint8Array(arrayBuffer, dataOffset, dataSize))[0];\r\n\r\n let frameType = (spec & 240) >>> 4;\r\n let codecId = spec & 15;\r\n\r\n if (codecId !== 7) {\r\n this._onError(DemuxErrors.CODEC_UNSUPPORTED, `Flv: Unsupported codec in video frame: ${codecId}`);\r\n return;\r\n }\r\n\r\n this._parseAVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType);\r\n }\r\n\r\n _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) {\r\n if (dataSize < 4) {\r\n Log.w(this.TAG, 'Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime');\r\n return;\r\n }\r\n\r\n let le = this._littleEndian;\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n\r\n let packetType = v.getUint8(0);\r\n let cts_unsigned = v.getUint32(0, !le) & 0x00FFFFFF;\r\n let cts = (cts_unsigned << 8) >> 8; // convert to 24-bit signed int\r\n\r\n if (packetType === 0) { // AVCDecoderConfigurationRecord\r\n this._parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4);\r\n } else if (packetType === 1) { // One or more Nalus\r\n this._parseAVCVideoData(arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts);\r\n } else if (packetType === 2) {\r\n // empty, AVC end of sequence\r\n } else {\r\n this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Invalid video packet type ${packetType}`);\r\n return;\r\n }\r\n }\r\n\r\n _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) {\r\n if (dataSize < 7) {\r\n Log.w(this.TAG, 'Flv: Invalid AVCDecoderConfigurationRecord, lack of data!');\r\n return;\r\n }\r\n\r\n let meta = this._videoMetadata;\r\n let track = this._videoTrack;\r\n let le = this._littleEndian;\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n\r\n if (!meta) {\r\n if (this._hasVideo === false && this._hasVideoFlagOverrided === false) {\r\n this._hasVideo = true;\r\n this._mediaInfo.hasVideo = true;\r\n }\r\n\r\n meta = this._videoMetadata = {};\r\n meta.type = 'video';\r\n meta.id = track.id;\r\n meta.timescale = this._timescale;\r\n meta.duration = this._duration;\r\n } else {\r\n if (typeof meta.avcc !== 'undefined') {\r\n Log.w(this.TAG, 'Found another AVCDecoderConfigurationRecord!');\r\n }\r\n }\r\n\r\n let version = v.getUint8(0); // configurationVersion\r\n let avcProfile = v.getUint8(1); // avcProfileIndication\r\n let profileCompatibility = v.getUint8(2); // profile_compatibility\r\n let avcLevel = v.getUint8(3); // AVCLevelIndication\r\n\r\n if (version !== 1 || avcProfile === 0) {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord');\r\n return;\r\n }\r\n\r\n this._naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne\r\n if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) { // holy shit!!!\r\n this._onError(DemuxErrors.FORMAT_ERROR, `Flv: Strange NaluLengthSizeMinusOne: ${this._naluLengthSize - 1}`);\r\n return;\r\n }\r\n\r\n let spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets\r\n if (spsCount === 0) {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS');\r\n return;\r\n } else if (spsCount > 1) {\r\n Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: SPS Count = ${spsCount}`);\r\n }\r\n\r\n let offset = 6;\r\n\r\n for (let i = 0; i < spsCount; i++) {\r\n let len = v.getUint16(offset, !le); // sequenceParameterSetLength\r\n offset += 2;\r\n\r\n if (len === 0) {\r\n continue;\r\n }\r\n\r\n // Notice: Nalu without startcode header (00 00 00 01)\r\n let sps = new Uint8Array(arrayBuffer, dataOffset + offset, len);\r\n offset += len;\r\n\r\n let config = SPSParser.parseSPS(sps);\r\n if (i !== 0) {\r\n // ignore other sps's config\r\n continue;\r\n }\r\n\r\n meta.codecWidth = config.codec_size.width;\r\n meta.codecHeight = config.codec_size.height;\r\n meta.presentWidth = config.present_size.width;\r\n meta.presentHeight = config.present_size.height;\r\n\r\n meta.profile = config.profile_string;\r\n meta.level = config.level_string;\r\n meta.bitDepth = config.bit_depth;\r\n meta.chromaFormat = config.chroma_format;\r\n meta.sarRatio = config.sar_ratio;\r\n meta.frameRate = config.frame_rate;\r\n\r\n if (config.frame_rate.fixed === false ||\r\n config.frame_rate.fps_num === 0 ||\r\n config.frame_rate.fps_den === 0) {\r\n meta.frameRate = this._referenceFrameRate;\r\n }\r\n\r\n let fps_den = meta.frameRate.fps_den;\r\n let fps_num = meta.frameRate.fps_num;\r\n meta.refSampleDuration = meta.timescale * (fps_den / fps_num);\r\n\r\n let codecArray = sps.subarray(1, 4);\r\n let codecString = 'avc1.';\r\n for (let j = 0; j < 3; j++) {\r\n let h = codecArray[j].toString(16);\r\n if (h.length < 2) {\r\n h = '0' + h;\r\n }\r\n codecString += h;\r\n }\r\n meta.codec = codecString;\r\n\r\n let mi = this._mediaInfo;\r\n mi.width = meta.codecWidth;\r\n mi.height = meta.codecHeight;\r\n mi.fps = meta.frameRate.fps;\r\n mi.profile = meta.profile;\r\n mi.level = meta.level;\r\n mi.refFrames = config.ref_frames;\r\n mi.chromaFormat = config.chroma_format_string;\r\n mi.sarNum = meta.sarRatio.width;\r\n mi.sarDen = meta.sarRatio.height;\r\n mi.videoCodec = codecString;\r\n\r\n if (mi.hasAudio) {\r\n if (mi.audioCodec != null) {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.videoCodec + ',' + mi.audioCodec + '\"';\r\n }\r\n } else {\r\n mi.mimeType = 'video/x-flv; codecs=\"' + mi.videoCodec + '\"';\r\n }\r\n if (mi.isComplete()) {\r\n this._onMediaInfo(mi);\r\n }\r\n }\r\n\r\n let ppsCount = v.getUint8(offset); // numOfPictureParameterSets\r\n if (ppsCount === 0) {\r\n this._onError(DemuxErrors.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS');\r\n return;\r\n } else if (ppsCount > 1) {\r\n Log.w(this.TAG, `Flv: Strange AVCDecoderConfigurationRecord: PPS Count = ${ppsCount}`);\r\n }\r\n\r\n offset++;\r\n\r\n for (let i = 0; i < ppsCount; i++) {\r\n let len = v.getUint16(offset, !le); // pictureParameterSetLength\r\n offset += 2;\r\n\r\n if (len === 0) {\r\n continue;\r\n }\r\n\r\n // pps is useless for extracting video information\r\n offset += len;\r\n }\r\n\r\n meta.avcc = new Uint8Array(dataSize);\r\n meta.avcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0);\r\n Log.v(this.TAG, 'Parsed AVCDecoderConfigurationRecord');\r\n\r\n if (this._isInitialMetadataDispatched()) {\r\n // flush parsed frames\r\n if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {\r\n this._onDataAvailable(this._audioTrack, this._videoTrack);\r\n }\r\n } else {\r\n this._videoInitialMetadataDispatched = true;\r\n }\r\n // notify new metadata\r\n this._dispatch = false;\r\n this._onTrackMetadata('video', meta);\r\n }\r\n\r\n _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) {\r\n let le = this._littleEndian;\r\n let v = new DataView(arrayBuffer, dataOffset, dataSize);\r\n\r\n let units = [], length = 0;\r\n\r\n let offset = 0;\r\n const lengthSize = this._naluLengthSize;\r\n let dts = this._timestampBase + tagTimestamp;\r\n let keyframe = (frameType === 1); // from FLV Frame Type constants\r\n\r\n while (offset < dataSize) {\r\n if (offset + 4 >= dataSize) {\r\n Log.w(this.TAG, `Malformed Nalu near timestamp ${dts}, offset = ${offset}, dataSize = ${dataSize}`);\r\n break; // data not enough for next Nalu\r\n }\r\n // Nalu with length-header (AVC1)\r\n let naluSize = v.getUint32(offset, !le); // Big-Endian read\r\n if (lengthSize === 3) {\r\n naluSize >>>= 8;\r\n }\r\n if (naluSize > dataSize - lengthSize) {\r\n Log.w(this.TAG, `Malformed Nalus near timestamp ${dts}, NaluSize > DataSize!`);\r\n return;\r\n }\r\n\r\n let unitType = v.getUint8(offset + lengthSize) & 0x1F;\r\n\r\n if (unitType === 5) { // IDR\r\n keyframe = true;\r\n }\r\n\r\n let data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize);\r\n let unit = {type: unitType, data: data};\r\n units.push(unit);\r\n length += data.byteLength;\r\n\r\n offset += lengthSize + naluSize;\r\n }\r\n\r\n if (units.length) {\r\n let track = this._videoTrack;\r\n let avcSample = {\r\n units: units,\r\n length: length,\r\n isKeyframe: keyframe,\r\n dts: dts,\r\n cts: cts,\r\n pts: (dts + cts)\r\n };\r\n if (keyframe) {\r\n avcSample.fileposition = tagPosition;\r\n }\r\n track.samples.push(avcSample);\r\n track.length += length;\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default FLVDemuxer;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js)\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n// MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12)\r\nclass MP4 {\r\n\r\n static init() {\r\n MP4.types = {\r\n avc1: [], avcC: [], btrt: [], dinf: [],\r\n dref: [], esds: [], ftyp: [], hdlr: [],\r\n mdat: [], mdhd: [], mdia: [], mfhd: [],\r\n minf: [], moof: [], moov: [], mp4a: [],\r\n mvex: [], mvhd: [], sdtp: [], stbl: [],\r\n stco: [], stsc: [], stsd: [], stsz: [],\r\n stts: [], tfdt: [], tfhd: [], traf: [],\r\n trak: [], trun: [], trex: [], tkhd: [],\r\n vmhd: [], smhd: [], '.mp3': []\r\n };\r\n\r\n for (let name in MP4.types) {\r\n if (MP4.types.hasOwnProperty(name)) {\r\n MP4.types[name] = [\r\n name.charCodeAt(0),\r\n name.charCodeAt(1),\r\n name.charCodeAt(2),\r\n name.charCodeAt(3)\r\n ];\r\n }\r\n }\r\n\r\n let constants = MP4.constants = {};\r\n\r\n constants.FTYP = new Uint8Array([\r\n 0x69, 0x73, 0x6F, 0x6D, // major_brand: isom\r\n 0x0, 0x0, 0x0, 0x1, // minor_version: 0x01\r\n 0x69, 0x73, 0x6F, 0x6D, // isom\r\n 0x61, 0x76, 0x63, 0x31 // avc1\r\n ]);\r\n\r\n constants.STSD_PREFIX = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x01 // entry_count\r\n ]);\r\n\r\n constants.STTS = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00 // entry_count\r\n ]);\r\n\r\n constants.STSC = constants.STCO = constants.STTS;\r\n\r\n constants.STSZ = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // sample_size\r\n 0x00, 0x00, 0x00, 0x00 // sample_count\r\n ]);\r\n\r\n constants.HDLR_VIDEO = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // pre_defined\r\n 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x56, 0x69, 0x64, 0x65,\r\n 0x6F, 0x48, 0x61, 0x6E,\r\n 0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler\r\n ]);\r\n\r\n constants.HDLR_AUDIO = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // pre_defined\r\n 0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun'\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x53, 0x6F, 0x75, 0x6E,\r\n 0x64, 0x48, 0x61, 0x6E,\r\n 0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler\r\n ]);\r\n\r\n constants.DREF = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x01, // entry_count\r\n 0x00, 0x00, 0x00, 0x0C, // entry_size\r\n 0x75, 0x72, 0x6C, 0x20, // type 'url '\r\n 0x00, 0x00, 0x00, 0x01 // version(0) + flags\r\n ]);\r\n\r\n // Sound media header\r\n constants.SMHD = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2)\r\n ]);\r\n\r\n // video media header\r\n constants.VMHD = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x01, // version(0) + flags\r\n 0x00, 0x00, // graphicsmode: 2 bytes\r\n 0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes\r\n 0x00, 0x00\r\n ]);\r\n }\r\n\r\n // Generate a box\r\n static box(type) {\r\n let size = 8;\r\n let result = null;\r\n let datas = Array.prototype.slice.call(arguments, 1);\r\n let arrayCount = datas.length;\r\n\r\n for (let i = 0; i < arrayCount; i++) {\r\n size += datas[i].byteLength;\r\n }\r\n\r\n result = new Uint8Array(size);\r\n result[0] = (size >>> 24) & 0xFF; // size\r\n result[1] = (size >>> 16) & 0xFF;\r\n result[2] = (size >>> 8) & 0xFF;\r\n result[3] = (size) & 0xFF;\r\n\r\n result.set(type, 4); // type\r\n\r\n let offset = 8;\r\n for (let i = 0; i < arrayCount; i++) { // data body\r\n result.set(datas[i], offset);\r\n offset += datas[i].byteLength;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n // emit ftyp & moov\r\n static generateInitSegment(meta) {\r\n let ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP);\r\n let moov = MP4.moov(meta);\r\n\r\n let result = new Uint8Array(ftyp.byteLength + moov.byteLength);\r\n result.set(ftyp, 0);\r\n result.set(moov, ftyp.byteLength);\r\n return result;\r\n }\r\n\r\n // Movie metadata box\r\n static moov(meta) {\r\n let mvhd = MP4.mvhd(meta.timescale, meta.duration);\r\n let trak = MP4.trak(meta);\r\n let mvex = MP4.mvex(meta);\r\n return MP4.box(MP4.types.moov, mvhd, trak, mvex);\r\n }\r\n\r\n // Movie header box\r\n static mvhd(timescale, duration) {\r\n return MP4.box(MP4.types.mvhd, new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // creation_time\r\n 0x00, 0x00, 0x00, 0x00, // modification_time\r\n (timescale >>> 24) & 0xFF, // timescale: 4 bytes\r\n (timescale >>> 16) & 0xFF,\r\n (timescale >>> 8) & 0xFF,\r\n (timescale) & 0xFF,\r\n (duration >>> 24) & 0xFF, // duration: 4 bytes\r\n (duration >>> 16) & 0xFF,\r\n (duration >>> 8) & 0xFF,\r\n (duration) & 0xFF,\r\n 0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0\r\n 0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes)\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x01, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x40, 0x00, 0x00, 0x00, // ----end composition matrix----\r\n 0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes----\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00, // ----end pre_defined 6 * 4 bytes----\r\n 0xFF, 0xFF, 0xFF, 0xFF // next_track_ID\r\n ]));\r\n }\r\n\r\n // Track box\r\n static trak(meta) {\r\n return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta));\r\n }\r\n\r\n // Track header box\r\n static tkhd(meta) {\r\n let trackId = meta.id, duration = meta.duration;\r\n let width = meta.presentWidth, height = meta.presentHeight;\r\n\r\n return MP4.box(MP4.types.tkhd, new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x07, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // creation_time\r\n 0x00, 0x00, 0x00, 0x00, // modification_time\r\n (trackId >>> 24) & 0xFF, // track_ID: 4 bytes\r\n (trackId >>> 16) & 0xFF,\r\n (trackId >>> 8) & 0xFF,\r\n (trackId) & 0xFF,\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes\r\n (duration >>> 24) & 0xFF, // duration: 4 bytes\r\n (duration >>> 16) & 0xFF,\r\n (duration >>> 8) & 0xFF,\r\n (duration) & 0xFF,\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes)\r\n 0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes)\r\n 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x01, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x40, 0x00, 0x00, 0x00, // ----end composition matrix----\r\n (width >>> 8) & 0xFF, // width and height\r\n (width) & 0xFF,\r\n 0x00, 0x00,\r\n (height >>> 8) & 0xFF,\r\n (height) & 0xFF,\r\n 0x00, 0x00\r\n ]));\r\n }\r\n\r\n // Media Box\r\n static mdia(meta) {\r\n return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta));\r\n }\r\n\r\n // Media header box\r\n static mdhd(meta) {\r\n let timescale = meta.timescale;\r\n let duration = meta.duration;\r\n return MP4.box(MP4.types.mdhd, new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n 0x00, 0x00, 0x00, 0x00, // creation_time\r\n 0x00, 0x00, 0x00, 0x00, // modification_time\r\n (timescale >>> 24) & 0xFF, // timescale: 4 bytes\r\n (timescale >>> 16) & 0xFF,\r\n (timescale >>> 8) & 0xFF,\r\n (timescale) & 0xFF,\r\n (duration >>> 24) & 0xFF, // duration: 4 bytes\r\n (duration >>> 16) & 0xFF,\r\n (duration >>> 8) & 0xFF,\r\n (duration) & 0xFF,\r\n 0x55, 0xC4, // language: und (undetermined)\r\n 0x00, 0x00 // pre_defined = 0\r\n ]));\r\n }\r\n\r\n // Media handler reference box\r\n static hdlr(meta) {\r\n let data = null;\r\n if (meta.type === 'audio') {\r\n data = MP4.constants.HDLR_AUDIO;\r\n } else {\r\n data = MP4.constants.HDLR_VIDEO;\r\n }\r\n return MP4.box(MP4.types.hdlr, data);\r\n }\r\n\r\n // Media infomation box\r\n static minf(meta) {\r\n let xmhd = null;\r\n if (meta.type === 'audio') {\r\n xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD);\r\n } else {\r\n xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD);\r\n }\r\n return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta));\r\n }\r\n\r\n // Data infomation box\r\n static dinf() {\r\n let result = MP4.box(MP4.types.dinf,\r\n MP4.box(MP4.types.dref, MP4.constants.DREF)\r\n );\r\n return result;\r\n }\r\n\r\n // Sample table box\r\n static stbl(meta) {\r\n let result = MP4.box(MP4.types.stbl, // type: stbl\r\n MP4.stsd(meta), // Sample Description Table\r\n MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample\r\n MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk\r\n MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size\r\n MP4.box(MP4.types.stco, MP4.constants.STCO) // Chunk offset\r\n ); \r\n return result; \r\n }\r\n\r\n // Sample description box\r\n static stsd(meta) {\r\n if (meta.type === 'audio') {\r\n if (meta.codec === 'mp3') {\r\n return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta));\r\n }\r\n // else: aac -> mp4a\r\n return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta));\r\n } else {\r\n return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta));\r\n }\r\n }\r\n\r\n static mp3(meta) {\r\n let channelCount = meta.channelCount;\r\n let sampleRate = meta.audioSampleRate;\r\n\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // reserved(4)\r\n 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, channelCount, // channelCount(2)\r\n 0x00, 0x10, // sampleSize(2)\r\n 0x00, 0x00, 0x00, 0x00, // reserved(4)\r\n (sampleRate >>> 8) & 0xFF, // Audio sample rate\r\n (sampleRate) & 0xFF,\r\n 0x00, 0x00\r\n ]);\r\n\r\n return MP4.box(MP4.types['.mp3'], data);\r\n }\r\n\r\n static mp4a(meta) {\r\n let channelCount = meta.channelCount;\r\n let sampleRate = meta.audioSampleRate;\r\n\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // reserved(4)\r\n 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, channelCount, // channelCount(2)\r\n 0x00, 0x10, // sampleSize(2)\r\n 0x00, 0x00, 0x00, 0x00, // reserved(4)\r\n (sampleRate >>> 8) & 0xFF, // Audio sample rate\r\n (sampleRate) & 0xFF,\r\n 0x00, 0x00\r\n ]);\r\n\r\n return MP4.box(MP4.types.mp4a, data, MP4.esds(meta));\r\n }\r\n\r\n static esds(meta) {\r\n let config = meta.config || [];\r\n let configSize = config.length;\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version 0 + flags\r\n\r\n 0x03, // descriptor_type\r\n 0x17 + configSize, // length3\r\n 0x00, 0x01, // es_id\r\n 0x00, // stream_priority\r\n\r\n 0x04, // descriptor_type\r\n 0x0F + configSize, // length\r\n 0x40, // codec: mpeg4_audio\r\n 0x15, // stream_type: Audio\r\n 0x00, 0x00, 0x00, // buffer_size\r\n 0x00, 0x00, 0x00, 0x00, // maxBitrate\r\n 0x00, 0x00, 0x00, 0x00, // avgBitrate\r\n\r\n 0x05 // descriptor_type\r\n ].concat([\r\n configSize\r\n ]).concat(\r\n config\r\n ).concat([\r\n 0x06, 0x01, 0x02 // GASpecificConfig\r\n ]));\r\n return MP4.box(MP4.types.esds, data);\r\n }\r\n\r\n static avc1(meta) {\r\n let avcc = meta.avcc;\r\n let width = meta.codecWidth, height = meta.codecHeight;\r\n\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // reserved(4)\r\n 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)\r\n 0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)\r\n 0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n (width >>> 8) & 0xFF, // width: 2 bytes\r\n (width) & 0xFF,\r\n (height >>> 8) & 0xFF, // height: 2 bytes\r\n (height) & 0xFF,\r\n 0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes\r\n 0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes\r\n 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes\r\n 0x00, 0x01, // frame_count\r\n 0x0A, // strlen\r\n 0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes\r\n 0x66, 0x6C, 0x76, 0x2E,\r\n 0x6A, 0x73, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00, 0x00,\r\n 0x00, 0x00, 0x00,\r\n 0x00, 0x18, // depth\r\n 0xFF, 0xFF // pre_defined = -1\r\n ]);\r\n return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc));\r\n }\r\n\r\n // Movie Extends box\r\n static mvex(meta) {\r\n return MP4.box(MP4.types.mvex, MP4.trex(meta));\r\n }\r\n\r\n // Track Extends box\r\n static trex(meta) {\r\n let trackId = meta.id;\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) + flags\r\n (trackId >>> 24) & 0xFF, // track_ID\r\n (trackId >>> 16) & 0xFF,\r\n (trackId >>> 8) & 0xFF,\r\n (trackId) & 0xFF,\r\n 0x00, 0x00, 0x00, 0x01, // default_sample_description_index\r\n 0x00, 0x00, 0x00, 0x00, // default_sample_duration\r\n 0x00, 0x00, 0x00, 0x00, // default_sample_size\r\n 0x00, 0x01, 0x00, 0x01 // default_sample_flags\r\n ]);\r\n return MP4.box(MP4.types.trex, data);\r\n }\r\n\r\n // Movie fragment box\r\n static moof(track, baseMediaDecodeTime) {\r\n return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime));\r\n }\r\n\r\n static mfhd(sequenceNumber) {\r\n let data = new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00,\r\n (sequenceNumber >>> 24) & 0xFF, // sequence_number: int32\r\n (sequenceNumber >>> 16) & 0xFF,\r\n (sequenceNumber >>> 8) & 0xFF,\r\n (sequenceNumber) & 0xFF\r\n ]);\r\n return MP4.box(MP4.types.mfhd, data);\r\n }\r\n\r\n // Track fragment box\r\n static traf(track, baseMediaDecodeTime) {\r\n let trackId = track.id;\r\n\r\n // Track fragment header box\r\n let tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) & flags\r\n (trackId >>> 24) & 0xFF, // track_ID\r\n (trackId >>> 16) & 0xFF,\r\n (trackId >>> 8) & 0xFF,\r\n (trackId) & 0xFF\r\n ]));\r\n // Track Fragment Decode Time\r\n let tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([\r\n 0x00, 0x00, 0x00, 0x00, // version(0) & flags\r\n (baseMediaDecodeTime >>> 24) & 0xFF, // baseMediaDecodeTime: int32\r\n (baseMediaDecodeTime >>> 16) & 0xFF,\r\n (baseMediaDecodeTime >>> 8) & 0xFF,\r\n (baseMediaDecodeTime) & 0xFF\r\n ]));\r\n let sdtp = MP4.sdtp(track);\r\n let trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8);\r\n\r\n return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp);\r\n }\r\n\r\n // Sample Dependency Type box\r\n static sdtp(track) {\r\n let samples = track.samples || [];\r\n let sampleCount = samples.length;\r\n let data = new Uint8Array(4 + sampleCount);\r\n // 0~4 bytes: version(0) & flags\r\n for (let i = 0; i < sampleCount; i++) {\r\n let flags = samples[i].flags;\r\n data[i + 4] = (flags.isLeading << 6) // is_leading: 2 (bit)\r\n | (flags.dependsOn << 4) // sample_depends_on\r\n | (flags.isDependedOn << 2) // sample_is_depended_on\r\n | (flags.hasRedundancy); // sample_has_redundancy\r\n }\r\n return MP4.box(MP4.types.sdtp, data);\r\n }\r\n\r\n // Track fragment run box\r\n static trun(track, offset) {\r\n let samples = track.samples || [];\r\n let sampleCount = samples.length;\r\n let dataSize = 12 + 16 * sampleCount;\r\n let data = new Uint8Array(dataSize);\r\n offset += 8 + dataSize;\r\n\r\n data.set([\r\n 0x00, 0x00, 0x0F, 0x01, // version(0) & flags\r\n (sampleCount >>> 24) & 0xFF, // sample_count\r\n (sampleCount >>> 16) & 0xFF,\r\n (sampleCount >>> 8) & 0xFF,\r\n (sampleCount) & 0xFF,\r\n (offset >>> 24) & 0xFF, // data_offset\r\n (offset >>> 16) & 0xFF,\r\n (offset >>> 8) & 0xFF,\r\n (offset) & 0xFF\r\n ], 0);\r\n\r\n for (let i = 0; i < sampleCount; i++) {\r\n let duration = samples[i].duration;\r\n let size = samples[i].size;\r\n let flags = samples[i].flags;\r\n let cts = samples[i].cts;\r\n data.set([\r\n (duration >>> 24) & 0xFF, // sample_duration\r\n (duration >>> 16) & 0xFF,\r\n (duration >>> 8) & 0xFF,\r\n (duration) & 0xFF,\r\n (size >>> 24) & 0xFF, // sample_size\r\n (size >>> 16) & 0xFF,\r\n (size >>> 8) & 0xFF,\r\n (size) & 0xFF,\r\n (flags.isLeading << 2) | flags.dependsOn, // sample_flags\r\n (flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | flags.isNonSync,\r\n 0x00, 0x00, // sample_degradation_priority\r\n (cts >>> 24) & 0xFF, // sample_composition_time_offset\r\n (cts >>> 16) & 0xFF,\r\n (cts >>> 8) & 0xFF,\r\n (cts) & 0xFF\r\n ], 12 + 16 * i);\r\n }\r\n return MP4.box(MP4.types.trun, data);\r\n }\r\n\r\n static mdat(data) {\r\n return MP4.box(MP4.types.mdat, data);\r\n }\r\n\r\n}\r\n\r\nMP4.init();\r\n\r\nexport default MP4;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * This file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js)\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nclass AAC {\r\n\r\n static getSilentFrame(codec, channelCount) {\r\n if (codec === 'mp4a.40.2') {\r\n // handle LC-AAC\r\n if (channelCount === 1) {\r\n return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);\r\n } else if (channelCount === 2) {\r\n return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);\r\n } else if (channelCount === 3) {\r\n return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);\r\n } else if (channelCount === 4) {\r\n return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);\r\n } else if (channelCount === 5) {\r\n return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);\r\n } else if (channelCount === 6) {\r\n 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]);\r\n }\r\n } else {\r\n // handle HE-AAC (mp4a.40.5 / mp4a.40.29)\r\n if (channelCount === 1) {\r\n // 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\r\n 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]);\r\n } else if (channelCount === 2) {\r\n // 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\r\n 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]);\r\n } else if (channelCount === 3) {\r\n // 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\r\n 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]);\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n}\r\n\r\nexport default AAC;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport MP4 from './mp4-generator.js';\r\nimport AAC from './aac-silent.js';\r\nimport Browser from '../utils/browser.js';\r\nimport { SampleInfo, MediaSegmentInfo, MediaSegmentInfoList } from '../core/media-segment-info.js';\r\nimport { IllegalStateException } from '../utils/exception.js';\r\n\r\n\r\n// Fragmented mp4 remuxer\r\nclass MP4Remuxer {\r\n\r\n constructor(config) {\r\n this.TAG = 'MP4Remuxer';\r\n\r\n this._config = config;\r\n this._isLive = (config.isLive === true) ? true : false;\r\n\r\n this._dtsBase = -1;\r\n this._dtsBaseInited = false;\r\n this._audioDtsBase = Infinity;\r\n this._videoDtsBase = Infinity;\r\n this._audioNextDts = undefined;\r\n this._videoNextDts = undefined;\r\n this._audioStashedLastSample = null;\r\n this._videoStashedLastSample = null;\r\n\r\n this._audioMeta = null;\r\n this._videoMeta = null;\r\n\r\n this._audioSegmentInfoList = new MediaSegmentInfoList('audio');\r\n this._videoSegmentInfoList = new MediaSegmentInfoList('video');\r\n\r\n this._onInitSegment = null;\r\n this._onMediaSegment = null;\r\n\r\n // Workaround for chrome < 50: Always force first sample as a Random Access Point in media segment\r\n // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412\r\n this._forceFirstIDR = (Browser.chrome &&\r\n (Browser.version.major < 50 ||\r\n (Browser.version.major === 50 && Browser.version.build < 2661))) ? true : false;\r\n\r\n // Workaround for IE11/Edge: Fill silent aac frame after keyframe-seeking\r\n // Make audio beginDts equals with video beginDts, in order to fix seek freeze\r\n this._fillSilentAfterSeek = (Browser.msedge || Browser.msie);\r\n\r\n // While only FireFox supports 'audio/mp4, codecs=\"mp3\"', use 'audio/mpeg' for chrome, safari, ...\r\n this._mp3UseMpegAudio = !Browser.firefox;\r\n\r\n this._fillAudioTimestampGap = this._config.fixAudioTimestampGap;\r\n }\r\n\r\n destroy() {\r\n this._dtsBase = -1;\r\n this._dtsBaseInited = false;\r\n this._audioMeta = null;\r\n this._videoMeta = null;\r\n this._audioSegmentInfoList.clear();\r\n this._audioSegmentInfoList = null;\r\n this._videoSegmentInfoList.clear();\r\n this._videoSegmentInfoList = null;\r\n this._onInitSegment = null;\r\n this._onMediaSegment = null;\r\n }\r\n\r\n bindDataSource(producer) {\r\n producer.onDataAvailable = this.remux.bind(this);\r\n producer.onTrackMetadata = this._onTrackMetadataReceived.bind(this);\r\n return this;\r\n }\r\n\r\n /* prototype: function onInitSegment(type: string, initSegment: ArrayBuffer): void\r\n InitSegment: {\r\n type: string,\r\n data: ArrayBuffer,\r\n codec: string,\r\n container: string\r\n }\r\n */\r\n get onInitSegment() {\r\n return this._onInitSegment;\r\n }\r\n\r\n set onInitSegment(callback) {\r\n this._onInitSegment = callback;\r\n }\r\n\r\n /* prototype: function onMediaSegment(type: string, mediaSegment: MediaSegment): void\r\n MediaSegment: {\r\n type: string,\r\n data: ArrayBuffer,\r\n sampleCount: int32\r\n info: MediaSegmentInfo\r\n }\r\n */\r\n get onMediaSegment() {\r\n return this._onMediaSegment;\r\n }\r\n\r\n set onMediaSegment(callback) {\r\n this._onMediaSegment = callback;\r\n }\r\n\r\n insertDiscontinuity() {\r\n this._audioNextDts = this._videoNextDts = undefined;\r\n }\r\n\r\n seek(originalDts) {\r\n this._audioStashedLastSample = null;\r\n this._videoStashedLastSample = null;\r\n this._videoSegmentInfoList.clear();\r\n this._audioSegmentInfoList.clear();\r\n }\r\n\r\n remux(audioTrack, videoTrack) {\r\n if (!this._onMediaSegment) {\r\n throw new IllegalStateException('MP4Remuxer: onMediaSegment callback must be specificed!');\r\n }\r\n if (!this._dtsBaseInited) {\r\n this._calculateDtsBase(audioTrack, videoTrack);\r\n }\r\n this._remuxVideo(videoTrack);\r\n this._remuxAudio(audioTrack);\r\n }\r\n\r\n _onTrackMetadataReceived(type, metadata) {\r\n let metabox = null;\r\n\r\n let container = 'mp4';\r\n let codec = metadata.codec;\r\n\r\n if (type === 'audio') {\r\n this._audioMeta = metadata;\r\n if (metadata.codec === 'mp3' && this._mp3UseMpegAudio) {\r\n // 'audio/mpeg' for MP3 audio track\r\n container = 'mpeg';\r\n codec = '';\r\n metabox = new Uint8Array();\r\n } else {\r\n // 'audio/mp4, codecs=\"codec\"'\r\n metabox = MP4.generateInitSegment(metadata);\r\n }\r\n } else if (type === 'video') {\r\n this._videoMeta = metadata;\r\n metabox = MP4.generateInitSegment(metadata);\r\n } else {\r\n return;\r\n }\r\n\r\n // dispatch metabox (Initialization Segment)\r\n if (!this._onInitSegment) {\r\n throw new IllegalStateException('MP4Remuxer: onInitSegment callback must be specified!');\r\n }\r\n this._onInitSegment(type, {\r\n type: type,\r\n data: metabox.buffer,\r\n codec: codec,\r\n container: `${type}/${container}`,\r\n mediaDuration: metadata.duration // in timescale 1000 (milliseconds)\r\n });\r\n }\r\n\r\n _calculateDtsBase(audioTrack, videoTrack) {\r\n if (this._dtsBaseInited) {\r\n return;\r\n }\r\n\r\n if (audioTrack.samples && audioTrack.samples.length) {\r\n this._audioDtsBase = audioTrack.samples[0].dts;\r\n }\r\n if (videoTrack.samples && videoTrack.samples.length) {\r\n this._videoDtsBase = videoTrack.samples[0].dts;\r\n }\r\n\r\n this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase);\r\n this._dtsBaseInited = true;\r\n }\r\n\r\n flushStashedSamples() {\r\n let videoSample = this._videoStashedLastSample;\r\n let audioSample = this._audioStashedLastSample;\r\n\r\n let videoTrack = {\r\n type: 'video',\r\n id: 1,\r\n sequenceNumber: 0,\r\n samples: [],\r\n length: 0\r\n };\r\n\r\n if (videoSample != null) {\r\n videoTrack.samples.push(videoSample);\r\n videoTrack.length = videoSample.length;\r\n }\r\n\r\n let audioTrack = {\r\n type: 'audio',\r\n id: 2,\r\n sequenceNumber: 0,\r\n samples: [],\r\n length: 0\r\n };\r\n\r\n if (audioSample != null) {\r\n audioTrack.samples.push(audioSample);\r\n audioTrack.length = audioSample.length;\r\n }\r\n\r\n this._videoStashedLastSample = null;\r\n this._audioStashedLastSample = null;\r\n\r\n this._remuxVideo(videoTrack, true);\r\n this._remuxAudio(audioTrack, true);\r\n }\r\n\r\n _remuxAudio(audioTrack, force) {\r\n if (this._audioMeta == null) {\r\n return;\r\n }\r\n\r\n let track = audioTrack;\r\n let samples = track.samples;\r\n let dtsCorrection = undefined;\r\n let firstDts = -1, lastDts = -1, lastPts = -1;\r\n let refSampleDuration = this._audioMeta.refSampleDuration;\r\n\r\n let mpegRawTrack = this._audioMeta.codec === 'mp3' && this._mp3UseMpegAudio;\r\n let firstSegmentAfterSeek = this._dtsBaseInited && this._audioNextDts === undefined;\r\n\r\n let insertPrefixSilentFrame = false;\r\n\r\n if (!samples || samples.length === 0) {\r\n return;\r\n }\r\n if (samples.length === 1 && !force) {\r\n // If [sample count in current batch] === 1 && (force != true)\r\n // Ignore and keep in demuxer's queue\r\n return;\r\n } // else if (force === true) do remux\r\n\r\n let offset = 0;\r\n let mdatbox = null;\r\n let mdatBytes = 0;\r\n\r\n // calculate initial mdat size\r\n if (mpegRawTrack) {\r\n // for raw mpeg buffer\r\n offset = 0;\r\n mdatBytes = track.length;\r\n } else {\r\n // for fmp4 mdat box\r\n offset = 8; // size + type\r\n mdatBytes = 8 + track.length;\r\n }\r\n\r\n\r\n let lastSample = null;\r\n\r\n // Pop the lastSample and waiting for stash\r\n if (samples.length > 1) {\r\n lastSample = samples.pop();\r\n mdatBytes -= lastSample.length;\r\n }\r\n\r\n // Insert [stashed lastSample in the previous batch] to the front\r\n if (this._audioStashedLastSample != null) {\r\n let sample = this._audioStashedLastSample;\r\n this._audioStashedLastSample = null;\r\n samples.unshift(sample);\r\n mdatBytes += sample.length;\r\n }\r\n\r\n // Stash the lastSample of current batch, waiting for next batch\r\n if (lastSample != null) {\r\n this._audioStashedLastSample = lastSample;\r\n }\r\n\r\n\r\n let firstSampleOriginalDts = samples[0].dts - this._dtsBase;\r\n\r\n // calculate dtsCorrection\r\n if (this._audioNextDts) {\r\n dtsCorrection = firstSampleOriginalDts - this._audioNextDts;\r\n } else { // this._audioNextDts == undefined\r\n if (this._audioSegmentInfoList.isEmpty()) {\r\n dtsCorrection = 0;\r\n if (this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty()) {\r\n if (this._audioMeta.originalCodec !== 'mp3') {\r\n insertPrefixSilentFrame = true;\r\n }\r\n }\r\n } else {\r\n let lastSample = this._audioSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);\r\n if (lastSample != null) {\r\n let distance = (firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration));\r\n if (distance <= 3) {\r\n distance = 0;\r\n }\r\n let expectedDts = lastSample.dts + lastSample.duration + distance;\r\n dtsCorrection = firstSampleOriginalDts - expectedDts;\r\n } else { // lastSample == null, cannot found\r\n dtsCorrection = 0;\r\n }\r\n }\r\n }\r\n\r\n if (insertPrefixSilentFrame) {\r\n // align audio segment beginDts to match with current video segment's beginDts\r\n let firstSampleDts = firstSampleOriginalDts - dtsCorrection;\r\n let videoSegment = this._videoSegmentInfoList.getLastSegmentBefore(firstSampleOriginalDts);\r\n if (videoSegment != null && videoSegment.beginDts < firstSampleDts) {\r\n let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);\r\n if (silentUnit) {\r\n let dts = videoSegment.beginDts;\r\n let silentFrameDuration = firstSampleDts - videoSegment.beginDts;\r\n Log.v(this.TAG, `InsertPrefixSilentAudio: dts: ${dts}, duration: ${silentFrameDuration}`);\r\n samples.unshift({ unit: silentUnit, dts: dts, pts: dts });\r\n mdatBytes += silentUnit.byteLength;\r\n } // silentUnit == null: Cannot generate, skip\r\n } else {\r\n insertPrefixSilentFrame = false;\r\n }\r\n }\r\n\r\n let mp4Samples = [];\r\n\r\n // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples\r\n for (let i = 0; i < samples.length; i++) {\r\n let sample = samples[i];\r\n let unit = sample.unit;\r\n let originalDts = sample.dts - this._dtsBase;\r\n let dts = originalDts;\r\n let needFillSilentFrames = false;\r\n let silentFrames = null;\r\n let sampleDuration = 0;\r\n\r\n if (originalDts < -0.001) {\r\n continue; //pass the first sample with the invalid dts\r\n }\r\n\r\n if (this._audioMeta.codec !== 'mp3') {\r\n // for AAC codec, we need to keep dts increase based on refSampleDuration\r\n let curRefDts = originalDts;\r\n const maxAudioFramesDrift = 3;\r\n if (this._audioNextDts) {\r\n curRefDts = this._audioNextDts;\r\n }\r\n\r\n dtsCorrection = originalDts - curRefDts;\r\n if (dtsCorrection <= -maxAudioFramesDrift * refSampleDuration) {\r\n // If we're overlapping by more than maxAudioFramesDrift number of frame, drop this sample\r\n Log.w(this.TAG, `Dropping 1 audio frame (originalDts: ${originalDts} ms ,curRefDts: ${curRefDts} ms) due to dtsCorrection: ${dtsCorrection} ms overlap.`);\r\n continue;\r\n }\r\n else if (dtsCorrection >= maxAudioFramesDrift * refSampleDuration && this._fillAudioTimestampGap && !Browser.safari) {\r\n // Silent frame generation, if large timestamp gap detected && config.fixAudioTimestampGap\r\n needFillSilentFrames = true;\r\n // We need to insert silent frames to fill timestamp gap\r\n let frameCount = Math.floor(dtsCorrection / refSampleDuration);\r\n Log.w(this.TAG, 'Large audio timestamp gap detected, may cause AV sync to drift. ' +\r\n 'Silent frames will be generated to avoid unsync.\\n' +\r\n `originalDts: ${originalDts} ms, curRefDts: ${curRefDts} ms, ` +\r\n `dtsCorrection: ${Math.round(dtsCorrection)} ms, generate: ${frameCount} frames`);\r\n\r\n\r\n dts = Math.floor(curRefDts);\r\n sampleDuration = Math.floor(curRefDts + refSampleDuration) - dts;\r\n\r\n let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);\r\n if (silentUnit == null) {\r\n Log.w(this.TAG, 'Unable to generate silent frame for ' +\r\n `${this._audioMeta.originalCodec} with ${this._audioMeta.channelCount} channels, repeat last frame`);\r\n // Repeat last frame\r\n silentUnit = unit;\r\n }\r\n silentFrames = [];\r\n\r\n for (let j = 0; j < frameCount; j++) {\r\n curRefDts = curRefDts + refSampleDuration;\r\n let intDts = Math.floor(curRefDts); // change to integer\r\n let intDuration = Math.floor(curRefDts + refSampleDuration) - intDts;\r\n let frame = {\r\n dts: intDts,\r\n pts: intDts,\r\n cts: 0,\r\n unit: silentUnit,\r\n size: silentUnit.byteLength,\r\n duration: intDuration, // wait for next sample\r\n originalDts: originalDts,\r\n flags: {\r\n isLeading: 0,\r\n dependsOn: 1,\r\n isDependedOn: 0,\r\n hasRedundancy: 0\r\n }\r\n };\r\n silentFrames.push(frame);\r\n mdatBytes += frame.size;;\r\n\r\n }\r\n\r\n this._audioNextDts = curRefDts + refSampleDuration;\r\n\r\n } else {\r\n\r\n dts = Math.floor(curRefDts);\r\n sampleDuration = Math.floor(curRefDts + refSampleDuration) - dts;\r\n this._audioNextDts = curRefDts + refSampleDuration;\r\n\r\n }\r\n } else {\r\n // keep the original dts calculate algorithm for mp3\r\n dts = originalDts - dtsCorrection;\r\n\r\n\r\n if (i !== samples.length - 1) {\r\n let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;\r\n sampleDuration = nextDts - dts;\r\n } else { // the last sample\r\n if (lastSample != null) { // use stashed sample's dts to calculate sample duration\r\n let nextDts = lastSample.dts - this._dtsBase - dtsCorrection;\r\n sampleDuration = nextDts - dts;\r\n } else if (mp4Samples.length >= 1) { // use second last sample duration\r\n sampleDuration = mp4Samples[mp4Samples.length - 1].duration;\r\n } else { // the only one sample, use reference sample duration\r\n sampleDuration = Math.floor(refSampleDuration);\r\n }\r\n }\r\n this._audioNextDts = dts + sampleDuration;\r\n }\r\n\r\n if (firstDts === -1) {\r\n firstDts = dts;\r\n }\r\n mp4Samples.push({\r\n dts: dts,\r\n pts: dts,\r\n cts: 0,\r\n unit: sample.unit,\r\n size: sample.unit.byteLength,\r\n duration: sampleDuration,\r\n originalDts: originalDts,\r\n flags: {\r\n isLeading: 0,\r\n dependsOn: 1,\r\n isDependedOn: 0,\r\n hasRedundancy: 0\r\n }\r\n });\r\n\r\n if (needFillSilentFrames) {\r\n // Silent frames should be inserted after wrong-duration frame\r\n mp4Samples.push.apply(mp4Samples, silentFrames);\r\n }\r\n }\r\n\r\n if (mp4Samples.length === 0) {\r\n //no samples need to remux\r\n track.samples = [];\r\n track.length = 0;\r\n return;\r\n }\r\n\r\n // allocate mdatbox\r\n if (mpegRawTrack) {\r\n // allocate for raw mpeg buffer\r\n mdatbox = new Uint8Array(mdatBytes);\r\n } else {\r\n // allocate for fmp4 mdat box\r\n mdatbox = new Uint8Array(mdatBytes);\r\n // size field\r\n mdatbox[0] = (mdatBytes >>> 24) & 0xFF;\r\n mdatbox[1] = (mdatBytes >>> 16) & 0xFF;\r\n mdatbox[2] = (mdatBytes >>> 8) & 0xFF;\r\n mdatbox[3] = (mdatBytes) & 0xFF;\r\n // type field (fourCC)\r\n mdatbox.set(MP4.types.mdat, 4);\r\n }\r\n\r\n // Write samples into mdatbox\r\n for (let i = 0; i < mp4Samples.length; i++) {\r\n let unit = mp4Samples[i].unit;\r\n mdatbox.set(unit, offset);\r\n offset += unit.byteLength;\r\n }\r\n\r\n let latest = mp4Samples[mp4Samples.length - 1];\r\n lastDts = latest.dts + latest.duration;\r\n //this._audioNextDts = lastDts;\r\n\r\n // fill media segment info & add to info list\r\n let info = new MediaSegmentInfo();\r\n info.beginDts = firstDts;\r\n info.endDts = lastDts;\r\n info.beginPts = firstDts;\r\n info.endPts = lastDts;\r\n info.originalBeginDts = mp4Samples[0].originalDts;\r\n info.originalEndDts = latest.originalDts + latest.duration;\r\n info.firstSample = new SampleInfo(mp4Samples[0].dts,\r\n mp4Samples[0].pts,\r\n mp4Samples[0].duration,\r\n mp4Samples[0].originalDts,\r\n false);\r\n info.lastSample = new SampleInfo(latest.dts,\r\n latest.pts,\r\n latest.duration,\r\n latest.originalDts,\r\n false);\r\n if (!this._isLive) {\r\n this._audioSegmentInfoList.append(info);\r\n }\r\n\r\n track.samples = mp4Samples;\r\n track.sequenceNumber++;\r\n\r\n let moofbox = null;\r\n\r\n if (mpegRawTrack) {\r\n // Generate empty buffer, because useless for raw mpeg\r\n moofbox = new Uint8Array();\r\n } else {\r\n // Generate moof for fmp4 segment\r\n moofbox = MP4.moof(track, firstDts);\r\n }\r\n\r\n track.samples = [];\r\n track.length = 0;\r\n\r\n let segment = {\r\n type: 'audio',\r\n data: this._mergeBoxes(moofbox, mdatbox).buffer,\r\n sampleCount: mp4Samples.length,\r\n info: info\r\n };\r\n\r\n if (mpegRawTrack && firstSegmentAfterSeek) {\r\n // For MPEG audio stream in MSE, if seeking occurred, before appending new buffer\r\n // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.\r\n segment.timestampOffset = firstDts;\r\n }\r\n\r\n this._onMediaSegment('audio', segment);\r\n }\r\n\r\n _remuxVideo(videoTrack, force) {\r\n if (this._videoMeta == null) {\r\n return;\r\n }\r\n\r\n let track = videoTrack;\r\n let samples = track.samples;\r\n let dtsCorrection = undefined;\r\n let firstDts = -1, lastDts = -1;\r\n let firstPts = -1, lastPts = -1;\r\n\r\n if (!samples || samples.length === 0) {\r\n return;\r\n }\r\n if (samples.length === 1 && !force) {\r\n // If [sample count in current batch] === 1 && (force != true)\r\n // Ignore and keep in demuxer's queue\r\n return;\r\n } // else if (force === true) do remux\r\n\r\n let offset = 8;\r\n let mdatbox = null;\r\n let mdatBytes = 8 + videoTrack.length;\r\n\r\n\r\n let lastSample = null;\r\n\r\n // Pop the lastSample and waiting for stash\r\n if (samples.length > 1) {\r\n lastSample = samples.pop();\r\n mdatBytes -= lastSample.length;\r\n }\r\n\r\n // Insert [stashed lastSample in the previous batch] to the front\r\n if (this._videoStashedLastSample != null) {\r\n let sample = this._videoStashedLastSample;\r\n this._videoStashedLastSample = null;\r\n samples.unshift(sample);\r\n mdatBytes += sample.length;\r\n }\r\n\r\n // Stash the lastSample of current batch, waiting for next batch\r\n if (lastSample != null) {\r\n this._videoStashedLastSample = lastSample;\r\n }\r\n\r\n\r\n let firstSampleOriginalDts = samples[0].dts - this._dtsBase;\r\n\r\n // calculate dtsCorrection\r\n if (this._videoNextDts) {\r\n dtsCorrection = firstSampleOriginalDts - this._videoNextDts;\r\n } else { // this._videoNextDts == undefined\r\n if (this._videoSegmentInfoList.isEmpty()) {\r\n dtsCorrection = 0;\r\n } else {\r\n let lastSample = this._videoSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);\r\n if (lastSample != null) {\r\n let distance = (firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration));\r\n if (distance <= 3) {\r\n distance = 0;\r\n }\r\n let expectedDts = lastSample.dts + lastSample.duration + distance;\r\n dtsCorrection = firstSampleOriginalDts - expectedDts;\r\n } else { // lastSample == null, cannot found\r\n dtsCorrection = 0;\r\n }\r\n }\r\n }\r\n\r\n let info = new MediaSegmentInfo();\r\n let mp4Samples = [];\r\n\r\n // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples\r\n for (let i = 0; i < samples.length; i++) {\r\n let sample = samples[i];\r\n let originalDts = sample.dts - this._dtsBase;\r\n let isKeyframe = sample.isKeyframe;\r\n let dts = originalDts - dtsCorrection;\r\n let cts = sample.cts;\r\n let pts = dts + cts;\r\n\r\n if (firstDts === -1) {\r\n firstDts = dts;\r\n firstPts = pts;\r\n }\r\n\r\n let sampleDuration = 0;\r\n\r\n if (i !== samples.length - 1) {\r\n let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;\r\n sampleDuration = nextDts - dts;\r\n } else { // the last sample\r\n if (lastSample != null) { // use stashed sample's dts to calculate sample duration\r\n let nextDts = lastSample.dts - this._dtsBase - dtsCorrection;\r\n sampleDuration = nextDts - dts;\r\n } else if (mp4Samples.length >= 1) { // use second last sample duration\r\n sampleDuration = mp4Samples[mp4Samples.length - 1].duration;\r\n } else { // the only one sample, use reference sample duration\r\n sampleDuration = Math.floor(this._videoMeta.refSampleDuration);\r\n }\r\n }\r\n\r\n if (isKeyframe) {\r\n let syncPoint = new SampleInfo(dts, pts, sampleDuration, sample.dts, true);\r\n syncPoint.fileposition = sample.fileposition;\r\n info.appendSyncPoint(syncPoint);\r\n }\r\n\r\n mp4Samples.push({\r\n dts: dts,\r\n pts: pts,\r\n cts: cts,\r\n units: sample.units,\r\n size: sample.length,\r\n isKeyframe: isKeyframe,\r\n duration: sampleDuration,\r\n originalDts: originalDts,\r\n flags: {\r\n isLeading: 0,\r\n dependsOn: isKeyframe ? 2 : 1,\r\n isDependedOn: isKeyframe ? 1 : 0,\r\n hasRedundancy: 0,\r\n isNonSync: isKeyframe ? 0 : 1\r\n }\r\n });\r\n }\r\n\r\n // allocate mdatbox\r\n mdatbox = new Uint8Array(mdatBytes);\r\n mdatbox[0] = (mdatBytes >>> 24) & 0xFF;\r\n mdatbox[1] = (mdatBytes >>> 16) & 0xFF;\r\n mdatbox[2] = (mdatBytes >>> 8) & 0xFF;\r\n mdatbox[3] = (mdatBytes) & 0xFF;\r\n mdatbox.set(MP4.types.mdat, 4);\r\n\r\n // Write samples into mdatbox\r\n for (let i = 0; i < mp4Samples.length; i++) {\r\n let units = mp4Samples[i].units;\r\n while (units.length) {\r\n let unit = units.shift();\r\n let data = unit.data;\r\n mdatbox.set(data, offset);\r\n offset += data.byteLength;\r\n }\r\n }\r\n\r\n let latest = mp4Samples[mp4Samples.length - 1];\r\n lastDts = latest.dts + latest.duration;\r\n lastPts = latest.pts + latest.duration;\r\n this._videoNextDts = lastDts;\r\n\r\n // fill media segment info & add to info list\r\n info.beginDts = firstDts;\r\n info.endDts = lastDts;\r\n info.beginPts = firstPts;\r\n info.endPts = lastPts;\r\n info.originalBeginDts = mp4Samples[0].originalDts;\r\n info.originalEndDts = latest.originalDts + latest.duration;\r\n info.firstSample = new SampleInfo(mp4Samples[0].dts,\r\n mp4Samples[0].pts,\r\n mp4Samples[0].duration,\r\n mp4Samples[0].originalDts,\r\n mp4Samples[0].isKeyframe);\r\n info.lastSample = new SampleInfo(latest.dts,\r\n latest.pts,\r\n latest.duration,\r\n latest.originalDts,\r\n latest.isKeyframe);\r\n if (!this._isLive) {\r\n this._videoSegmentInfoList.append(info);\r\n }\r\n\r\n track.samples = mp4Samples;\r\n track.sequenceNumber++;\r\n\r\n // workaround for chrome < 50: force first sample as a random access point\r\n // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412\r\n if (this._forceFirstIDR) {\r\n let flags = mp4Samples[0].flags;\r\n flags.dependsOn = 2;\r\n flags.isNonSync = 0;\r\n }\r\n\r\n let moofbox = MP4.moof(track, firstDts);\r\n track.samples = [];\r\n track.length = 0;\r\n\r\n this._onMediaSegment('video', {\r\n type: 'video',\r\n data: this._mergeBoxes(moofbox, mdatbox).buffer,\r\n sampleCount: mp4Samples.length,\r\n info: info\r\n });\r\n }\r\n\r\n _mergeBoxes(moof, mdat) {\r\n let result = new Uint8Array(moof.byteLength + mdat.byteLength);\r\n result.set(moof, 0);\r\n result.set(mdat, moof.byteLength);\r\n return result;\r\n }\r\n\r\n}\r\n\r\nexport default MP4Remuxer;\r\n","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport Log from '../utils/logger.js';\r\nimport Browser from '../utils/browser.js';\r\nimport MediaInfo from './media-info.js';\r\nimport FLVDemuxer from '../demux/flv-demuxer.js';\r\nimport MP4Remuxer from '../remux/mp4-remuxer.js';\r\nimport DemuxErrors from '../demux/demux-errors.js';\r\nimport IOController from '../io/io-controller.js';\r\nimport TransmuxingEvents from './transmuxing-events.js';\r\nimport {LoaderStatus, LoaderErrors} from '../io/loader.js';\r\n\r\n// Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support\r\nclass TransmuxingController {\r\n\r\n constructor(mediaDataSource, config) {\r\n this.TAG = 'TransmuxingController';\r\n this._emitter = new EventEmitter();\r\n\r\n this._config = config;\r\n\r\n // treat single part media as multipart media, which has only one segment\r\n if (!mediaDataSource.segments) {\r\n mediaDataSource.segments = [{\r\n duration: mediaDataSource.duration,\r\n filesize: mediaDataSource.filesize,\r\n url: mediaDataSource.url\r\n }];\r\n }\r\n\r\n // fill in default IO params if not exists\r\n if (typeof mediaDataSource.cors !== 'boolean') {\r\n mediaDataSource.cors = true;\r\n }\r\n if (typeof mediaDataSource.withCredentials !== 'boolean') {\r\n mediaDataSource.withCredentials = false;\r\n }\r\n\r\n this._mediaDataSource = mediaDataSource;\r\n this._currentSegmentIndex = 0;\r\n let totalDuration = 0;\r\n\r\n this._mediaDataSource.segments.forEach((segment) => {\r\n // timestampBase for each segment, and calculate total duration\r\n segment.timestampBase = totalDuration;\r\n totalDuration += segment.duration;\r\n // params needed by IOController\r\n segment.cors = mediaDataSource.cors;\r\n segment.withCredentials = mediaDataSource.withCredentials;\r\n // referrer policy control, if exist\r\n if (config.referrerPolicy) {\r\n segment.referrerPolicy = config.referrerPolicy;\r\n }\r\n });\r\n\r\n if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) {\r\n this._mediaDataSource.duration = totalDuration;\r\n }\r\n\r\n this._mediaInfo = null;\r\n this._demuxer = null;\r\n this._remuxer = null;\r\n this._ioctl = null;\r\n\r\n this._pendingSeekTime = null;\r\n this._pendingResolveSeekPoint = null;\r\n\r\n this._statisticsReporter = null;\r\n }\r\n\r\n destroy() {\r\n this._mediaInfo = null;\r\n this._mediaDataSource = null;\r\n\r\n if (this._statisticsReporter) {\r\n this._disableStatisticsReporter();\r\n }\r\n if (this._ioctl) {\r\n this._ioctl.destroy();\r\n this._ioctl = null;\r\n }\r\n if (this._demuxer) {\r\n this._demuxer.destroy();\r\n this._demuxer = null;\r\n }\r\n if (this._remuxer) {\r\n this._remuxer.destroy();\r\n this._remuxer = null;\r\n }\r\n\r\n this._emitter.removeAllListeners();\r\n this._emitter = null;\r\n }\r\n\r\n on(event, listener) {\r\n this._emitter.addListener(event, listener);\r\n }\r\n\r\n off(event, listener) {\r\n this._emitter.removeListener(event, listener);\r\n }\r\n\r\n start() {\r\n this._loadSegment(0);\r\n this._enableStatisticsReporter();\r\n }\r\n\r\n _loadSegment(segmentIndex, optionalFrom) {\r\n this._currentSegmentIndex = segmentIndex;\r\n let dataSource = this._mediaDataSource.segments[segmentIndex];\r\n\r\n let ioctl = this._ioctl = new IOController(dataSource, this._config, segmentIndex);\r\n ioctl.onError = this._onIOException.bind(this);\r\n ioctl.onSeeked = this._onIOSeeked.bind(this);\r\n ioctl.onComplete = this._onIOComplete.bind(this);\r\n ioctl.onRedirect = this._onIORedirect.bind(this);\r\n ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this);\r\n\r\n if (optionalFrom) {\r\n this._demuxer.bindDataSource(this._ioctl);\r\n } else {\r\n ioctl.onDataArrival = this._onInitChunkArrival.bind(this);\r\n }\r\n\r\n ioctl.open(optionalFrom);\r\n }\r\n\r\n stop() {\r\n this._internalAbort();\r\n this._disableStatisticsReporter();\r\n }\r\n\r\n _internalAbort() {\r\n if (this._ioctl) {\r\n this._ioctl.destroy();\r\n this._ioctl = null;\r\n }\r\n }\r\n\r\n pause() { // take a rest\r\n if (this._ioctl && this._ioctl.isWorking()) {\r\n this._ioctl.pause();\r\n this._disableStatisticsReporter();\r\n }\r\n }\r\n\r\n resume() {\r\n if (this._ioctl && this._ioctl.isPaused()) {\r\n this._ioctl.resume();\r\n this._enableStatisticsReporter();\r\n }\r\n }\r\n\r\n seek(milliseconds) {\r\n if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) {\r\n return;\r\n }\r\n\r\n let targetSegmentIndex = this._searchSegmentIndexContains(milliseconds);\r\n\r\n if (targetSegmentIndex === this._currentSegmentIndex) {\r\n // intra-segment seeking\r\n let segmentInfo = this._mediaInfo.segments[targetSegmentIndex];\r\n\r\n if (segmentInfo == undefined) {\r\n // current segment loading started, but mediainfo hasn't received yet\r\n // wait for the metadata loaded, then seek to expected position\r\n this._pendingSeekTime = milliseconds;\r\n } else {\r\n let keyframe = segmentInfo.getNearestKeyframe(milliseconds);\r\n this._remuxer.seek(keyframe.milliseconds);\r\n this._ioctl.seek(keyframe.fileposition);\r\n // Will be resolved in _onRemuxerMediaSegmentArrival()\r\n this._pendingResolveSeekPoint = keyframe.milliseconds;\r\n }\r\n } else {\r\n // cross-segment seeking\r\n let targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex];\r\n\r\n if (targetSegmentInfo == undefined) {\r\n // target segment hasn't been loaded. We need metadata then seek to expected time\r\n this._pendingSeekTime = milliseconds;\r\n this._internalAbort();\r\n this._remuxer.seek();\r\n this._remuxer.insertDiscontinuity();\r\n this._loadSegment(targetSegmentIndex);\r\n // Here we wait for the metadata loaded, then seek to expected position\r\n } else {\r\n // We have target segment's metadata, direct seek to target position\r\n let keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds);\r\n this._internalAbort();\r\n this._remuxer.seek(milliseconds);\r\n this._remuxer.insertDiscontinuity();\r\n this._demuxer.resetMediaInfo();\r\n this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase;\r\n this._loadSegment(targetSegmentIndex, keyframe.fileposition);\r\n this._pendingResolveSeekPoint = keyframe.milliseconds;\r\n this._reportSegmentMediaInfo(targetSegmentIndex);\r\n }\r\n }\r\n\r\n this._enableStatisticsReporter();\r\n }\r\n\r\n _searchSegmentIndexContains(milliseconds) {\r\n let segments = this._mediaDataSource.segments;\r\n let idx = segments.length - 1;\r\n\r\n for (let i = 0; i < segments.length; i++) {\r\n if (milliseconds < segments[i].timestampBase) {\r\n idx = i - 1;\r\n break;\r\n }\r\n }\r\n return idx;\r\n }\r\n\r\n _onInitChunkArrival(data, byteStart) {\r\n let probeData = null;\r\n let consumed = 0;\r\n\r\n if (byteStart > 0) {\r\n // IOController seeked immediately after opened, byteStart > 0 callback may received\r\n this._demuxer.bindDataSource(this._ioctl);\r\n this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase;\r\n\r\n consumed = this._demuxer.parseChunks(data, byteStart);\r\n } else if ((probeData = FLVDemuxer.probe(data)).match) {\r\n // Always create new FLVDemuxer\r\n this._demuxer = new FLVDemuxer(probeData, this._config);\r\n\r\n if (!this._remuxer) {\r\n this._remuxer = new MP4Remuxer(this._config);\r\n }\r\n\r\n let mds = this._mediaDataSource;\r\n if (mds.duration != undefined && !isNaN(mds.duration)) {\r\n this._demuxer.overridedDuration = mds.duration;\r\n }\r\n if (typeof mds.hasAudio === 'boolean') {\r\n this._demuxer.overridedHasAudio = mds.hasAudio;\r\n }\r\n if (typeof mds.hasVideo === 'boolean') {\r\n this._demuxer.overridedHasVideo = mds.hasVideo;\r\n }\r\n\r\n this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase;\r\n\r\n this._demuxer.onError = this._onDemuxException.bind(this);\r\n this._demuxer.onMediaInfo = this._onMediaInfo.bind(this);\r\n this._demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this);\r\n this._demuxer.onScriptDataArrived = this._onScriptDataArrived.bind(this);\r\n\r\n this._remuxer.bindDataSource(this._demuxer\r\n .bindDataSource(this._ioctl\r\n ));\r\n\r\n this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this);\r\n this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this);\r\n\r\n consumed = this._demuxer.parseChunks(data, byteStart);\r\n } else {\r\n probeData = null;\r\n Log.e(this.TAG, 'Non-FLV, Unsupported media type!');\r\n Promise.resolve().then(() => {\r\n this._internalAbort();\r\n });\r\n this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, DemuxErrors.FORMAT_UNSUPPORTED, 'Non-FLV, Unsupported media type');\r\n\r\n consumed = 0;\r\n }\r\n\r\n return consumed;\r\n }\r\n\r\n _onMediaInfo(mediaInfo) {\r\n if (this._mediaInfo == null) {\r\n // Store first segment's mediainfo as global mediaInfo\r\n this._mediaInfo = Object.assign({}, mediaInfo);\r\n this._mediaInfo.keyframesIndex = null;\r\n this._mediaInfo.segments = [];\r\n this._mediaInfo.segmentCount = this._mediaDataSource.segments.length;\r\n Object.setPrototypeOf(this._mediaInfo, MediaInfo.prototype);\r\n }\r\n\r\n let segmentInfo = Object.assign({}, mediaInfo);\r\n Object.setPrototypeOf(segmentInfo, MediaInfo.prototype);\r\n this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo;\r\n\r\n // notify mediaInfo update\r\n this._reportSegmentMediaInfo(this._currentSegmentIndex);\r\n\r\n if (this._pendingSeekTime != null) {\r\n Promise.resolve().then(() => {\r\n let target = this._pendingSeekTime;\r\n this._pendingSeekTime = null;\r\n this.seek(target);\r\n });\r\n }\r\n }\r\n\r\n _onMetaDataArrived(metadata) {\r\n this._emitter.emit(TransmuxingEvents.METADATA_ARRIVED, metadata);\r\n }\r\n\r\n _onScriptDataArrived(data) {\r\n this._emitter.emit(TransmuxingEvents.SCRIPTDATA_ARRIVED, data);\r\n }\r\n\r\n _onIOSeeked() {\r\n this._remuxer.insertDiscontinuity();\r\n }\r\n\r\n _onIOComplete(extraData) {\r\n let segmentIndex = extraData;\r\n let nextSegmentIndex = segmentIndex + 1;\r\n\r\n if (nextSegmentIndex < this._mediaDataSource.segments.length) {\r\n this._internalAbort();\r\n this._remuxer.flushStashedSamples();\r\n this._loadSegment(nextSegmentIndex);\r\n } else {\r\n this._remuxer.flushStashedSamples();\r\n this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE);\r\n this._disableStatisticsReporter();\r\n }\r\n }\r\n\r\n _onIORedirect(redirectedURL) {\r\n let segmentIndex = this._ioctl.extraData;\r\n this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL;\r\n }\r\n\r\n _onIORecoveredEarlyEof() {\r\n this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF);\r\n }\r\n\r\n _onIOException(type, info) {\r\n Log.e(this.TAG, `IOException: type = ${type}, code = ${info.code}, msg = ${info.msg}`);\r\n this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info);\r\n this._disableStatisticsReporter();\r\n }\r\n\r\n _onDemuxException(type, info) {\r\n Log.e(this.TAG, `DemuxException: type = ${type}, info = ${info}`);\r\n this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info);\r\n }\r\n\r\n _onRemuxerInitSegmentArrival(type, initSegment) {\r\n this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment);\r\n }\r\n\r\n _onRemuxerMediaSegmentArrival(type, mediaSegment) {\r\n if (this._pendingSeekTime != null) {\r\n // Media segments after new-segment cross-seeking should be dropped.\r\n return;\r\n }\r\n this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment);\r\n\r\n // Resolve pending seekPoint\r\n if (this._pendingResolveSeekPoint != null && type === 'video') {\r\n let syncPoints = mediaSegment.info.syncPoints;\r\n let seekpoint = this._pendingResolveSeekPoint;\r\n this._pendingResolveSeekPoint = null;\r\n\r\n // Safari: Pass PTS for recommend_seekpoint\r\n if (Browser.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) {\r\n seekpoint = syncPoints[0].pts;\r\n }\r\n // else: use original DTS (keyframe.milliseconds)\r\n\r\n this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, seekpoint);\r\n }\r\n }\r\n\r\n _enableStatisticsReporter() {\r\n if (this._statisticsReporter == null) {\r\n this._statisticsReporter = self.setInterval(\r\n this._reportStatisticsInfo.bind(this),\r\n this._config.statisticsInfoReportInterval);\r\n }\r\n }\r\n\r\n _disableStatisticsReporter() {\r\n if (this._statisticsReporter) {\r\n self.clearInterval(this._statisticsReporter);\r\n this._statisticsReporter = null;\r\n }\r\n }\r\n\r\n _reportSegmentMediaInfo(segmentIndex) {\r\n let segmentInfo = this._mediaInfo.segments[segmentIndex];\r\n let exportInfo = Object.assign({}, segmentInfo);\r\n\r\n exportInfo.duration = this._mediaInfo.duration;\r\n exportInfo.segmentCount = this._mediaInfo.segmentCount;\r\n delete exportInfo.segments;\r\n delete exportInfo.keyframesIndex;\r\n\r\n this._emitter.emit(TransmuxingEvents.MEDIA_INFO, exportInfo);\r\n }\r\n\r\n _reportStatisticsInfo() {\r\n let info = {};\r\n\r\n info.url = this._ioctl.currentURL;\r\n info.hasRedirect = this._ioctl.hasRedirect;\r\n if (info.hasRedirect) {\r\n info.redirectedURL = this._ioctl.currentRedirectedURL;\r\n }\r\n\r\n info.speed = this._ioctl.currentSpeed;\r\n info.loaderType = this._ioctl.loaderType;\r\n info.currentSegmentIndex = this._currentSegmentIndex;\r\n info.totalSegmentCount = this._mediaDataSource.segments.length;\r\n\r\n this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, info);\r\n }\r\n\r\n}\r\n\r\nexport default TransmuxingController;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nconst TransmuxingEvents = {\r\n IO_ERROR: 'io_error',\r\n DEMUX_ERROR: 'demux_error',\r\n INIT_SEGMENT: 'init_segment',\r\n MEDIA_SEGMENT: 'media_segment',\r\n LOADING_COMPLETE: 'loading_complete',\r\n RECOVERED_EARLY_EOF: 'recovered_early_eof',\r\n MEDIA_INFO: 'media_info',\r\n METADATA_ARRIVED: 'metadata_arrived',\r\n SCRIPTDATA_ARRIVED: 'scriptdata_arrived',\r\n STATISTICS_INFO: 'statistics_info',\r\n RECOMMEND_SEEKPOINT: 'recommend_seekpoint'\r\n};\r\n\r\nexport default TransmuxingEvents;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nconst DemuxErrors = {\r\n OK: 'OK',\r\n FORMAT_ERROR: 'FormatError',\r\n FORMAT_UNSUPPORTED: 'FormatUnsupported',\r\n CODEC_UNSUPPORTED: 'CodecUnsupported'\r\n};\r\n\r\nexport default DemuxErrors;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nexport const defaultConfig = {\r\n enableWorker: false,\r\n enableStashBuffer: true,\r\n stashInitialSize: undefined,\r\n\r\n isLive: false,\r\n\r\n lazyLoad: true,\r\n lazyLoadMaxDuration: 3 * 60,\r\n lazyLoadRecoverDuration: 30,\r\n deferLoadAfterSourceOpen: true,\r\n\r\n // autoCleanupSourceBuffer: default as false, leave unspecified\r\n autoCleanupMaxBackwardDuration: 3 * 60,\r\n autoCleanupMinBackwardDuration: 2 * 60,\r\n\r\n statisticsInfoReportInterval: 600,\r\n\r\n fixAudioTimestampGap: true,\r\n\r\n accurateSeek: false,\r\n seekType: 'range', // [range, param, custom]\r\n seekParamStart: 'bstart',\r\n seekParamEnd: 'bend',\r\n rangeLoadZeroStart: false,\r\n customSeekHandler: undefined,\r\n reuseRedirectedURL: false,\r\n // referrerPolicy: leave as unspecified\r\n\r\n headers: undefined,\r\n customLoader: undefined\r\n};\r\n\r\nexport function createDefaultConfig() {\r\n return Object.assign({}, defaultConfig);\r\n}","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport IOController from '../io/io-controller.js';\r\nimport {createDefaultConfig} from '../config.js';\r\n\r\nclass Features {\r\n\r\n static supportMSEH264Playback() {\r\n return window.MediaSource &&\r\n window.MediaSource.isTypeSupported('video/mp4; codecs=\"avc1.42E01E,mp4a.40.2\"');\r\n }\r\n\r\n static supportNetworkStreamIO() {\r\n let ioctl = new IOController({}, createDefaultConfig());\r\n let loaderType = ioctl.loaderType;\r\n ioctl.destroy();\r\n return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader';\r\n }\r\n\r\n static getNetworkLoaderTypeName() {\r\n let ioctl = new IOController({}, createDefaultConfig());\r\n let loaderType = ioctl.loaderType;\r\n ioctl.destroy();\r\n return loaderType;\r\n }\r\n\r\n static supportNativeMediaPlayback(mimeType) {\r\n if (Features.videoElement == undefined) {\r\n Features.videoElement = window.document.createElement('video');\r\n }\r\n let canPlay = Features.videoElement.canPlayType(mimeType);\r\n return canPlay === 'probably' || canPlay == 'maybe';\r\n }\r\n\r\n static getFeatureList() {\r\n let features = {\r\n mseFlvPlayback: false,\r\n mseLiveFlvPlayback: false,\r\n networkStreamIO: false,\r\n networkLoaderName: '',\r\n nativeMP4H264Playback: false,\r\n nativeWebmVP8Playback: false,\r\n nativeWebmVP9Playback: false\r\n };\r\n\r\n features.mseFlvPlayback = Features.supportMSEH264Playback();\r\n features.networkStreamIO = Features.supportNetworkStreamIO();\r\n features.networkLoaderName = Features.getNetworkLoaderTypeName();\r\n features.mseLiveFlvPlayback = features.mseFlvPlayback && features.networkStreamIO;\r\n features.nativeMP4H264Playback = Features.supportNativeMediaPlayback('video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"');\r\n features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs=\"vp8.0, vorbis\"');\r\n features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs=\"vp9\"');\r\n\r\n return features;\r\n }\r\n\r\n}\r\n\r\nexport default Features;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nconst PlayerEvents = {\r\n ERROR: 'error',\r\n LOADING_COMPLETE: 'loading_complete',\r\n RECOVERED_EARLY_EOF: 'recovered_early_eof',\r\n MEDIA_INFO: 'media_info',\r\n METADATA_ARRIVED: 'metadata_arrived',\r\n SCRIPTDATA_ARRIVED: 'scriptdata_arrived',\r\n STATISTICS_INFO: 'statistics_info'\r\n};\r\n\r\nexport default PlayerEvents;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport work from 'webworkify-webpack';\r\nimport Log from '../utils/logger.js';\r\nimport LoggingControl from '../utils/logging-control.js';\r\nimport TransmuxingController from './transmuxing-controller.js';\r\nimport TransmuxingEvents from './transmuxing-events.js';\r\nimport TransmuxingWorker from './transmuxing-worker.js';\r\nimport MediaInfo from './media-info.js';\r\n\r\nclass Transmuxer {\r\n\r\n constructor(mediaDataSource, config) {\r\n this.TAG = 'Transmuxer';\r\n this._emitter = new EventEmitter();\r\n\r\n if (config.enableWorker && typeof (Worker) !== 'undefined') {\r\n try {\r\n this._worker = work(require.resolve('./transmuxing-worker'));\r\n this._workerDestroying = false;\r\n this._worker.addEventListener('message', this._onWorkerMessage.bind(this));\r\n this._worker.postMessage({cmd: 'init', param: [mediaDataSource, config]});\r\n this.e = {\r\n onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this)\r\n };\r\n LoggingControl.registerListener(this.e.onLoggingConfigChanged);\r\n this._worker.postMessage({cmd: 'logging_config', param: LoggingControl.getConfig()});\r\n } catch (error) {\r\n Log.e(this.TAG, 'Error while initialize transmuxing worker, fallback to inline transmuxing');\r\n this._worker = null;\r\n this._controller = new TransmuxingController(mediaDataSource, config);\r\n }\r\n } else {\r\n this._controller = new TransmuxingController(mediaDataSource, config);\r\n }\r\n\r\n if (this._controller) {\r\n let ctl = this._controller;\r\n ctl.on(TransmuxingEvents.IO_ERROR, this._onIOError.bind(this));\r\n ctl.on(TransmuxingEvents.DEMUX_ERROR, this._onDemuxError.bind(this));\r\n ctl.on(TransmuxingEvents.INIT_SEGMENT, this._onInitSegment.bind(this));\r\n ctl.on(TransmuxingEvents.MEDIA_SEGMENT, this._onMediaSegment.bind(this));\r\n ctl.on(TransmuxingEvents.LOADING_COMPLETE, this._onLoadingComplete.bind(this));\r\n ctl.on(TransmuxingEvents.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this));\r\n ctl.on(TransmuxingEvents.MEDIA_INFO, this._onMediaInfo.bind(this));\r\n ctl.on(TransmuxingEvents.METADATA_ARRIVED, this._onMetaDataArrived.bind(this));\r\n ctl.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, this._onScriptDataArrived.bind(this));\r\n ctl.on(TransmuxingEvents.STATISTICS_INFO, this._onStatisticsInfo.bind(this));\r\n ctl.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this));\r\n }\r\n }\r\n\r\n destroy() {\r\n if (this._worker) {\r\n if (!this._workerDestroying) {\r\n this._workerDestroying = true;\r\n this._worker.postMessage({cmd: 'destroy'});\r\n LoggingControl.removeListener(this.e.onLoggingConfigChanged);\r\n this.e = null;\r\n }\r\n } else {\r\n this._controller.destroy();\r\n this._controller = null;\r\n }\r\n this._emitter.removeAllListeners();\r\n this._emitter = null;\r\n }\r\n\r\n on(event, listener) {\r\n this._emitter.addListener(event, listener);\r\n }\r\n\r\n off(event, listener) {\r\n this._emitter.removeListener(event, listener);\r\n }\r\n\r\n hasWorker() {\r\n return this._worker != null;\r\n }\r\n\r\n open() {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'start'});\r\n } else {\r\n this._controller.start();\r\n }\r\n }\r\n\r\n close() {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'stop'});\r\n } else {\r\n this._controller.stop();\r\n }\r\n }\r\n\r\n seek(milliseconds) {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'seek', param: milliseconds});\r\n } else {\r\n this._controller.seek(milliseconds);\r\n }\r\n }\r\n\r\n pause() {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'pause'});\r\n } else {\r\n this._controller.pause();\r\n }\r\n }\r\n\r\n resume() {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'resume'});\r\n } else {\r\n this._controller.resume();\r\n }\r\n }\r\n\r\n _onInitSegment(type, initSegment) {\r\n // do async invoke\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment);\r\n });\r\n }\r\n\r\n _onMediaSegment(type, mediaSegment) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment);\r\n });\r\n }\r\n\r\n _onLoadingComplete() {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE);\r\n });\r\n }\r\n\r\n _onRecoveredEarlyEof() {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF);\r\n });\r\n }\r\n\r\n _onMediaInfo(mediaInfo) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.MEDIA_INFO, mediaInfo);\r\n });\r\n }\r\n\r\n _onMetaDataArrived(metadata) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.METADATA_ARRIVED, metadata);\r\n });\r\n }\r\n\r\n _onScriptDataArrived(data) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.SCRIPTDATA_ARRIVED, data);\r\n });\r\n }\r\n\r\n _onStatisticsInfo(statisticsInfo) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, statisticsInfo);\r\n });\r\n }\r\n\r\n _onIOError(type, info) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info);\r\n });\r\n }\r\n\r\n _onDemuxError(type, info) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info);\r\n });\r\n }\r\n\r\n _onRecommendSeekpoint(milliseconds) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, milliseconds);\r\n });\r\n }\r\n\r\n _onLoggingConfigChanged(config) {\r\n if (this._worker) {\r\n this._worker.postMessage({cmd: 'logging_config', param: config});\r\n }\r\n }\r\n\r\n _onWorkerMessage(e) {\r\n let message = e.data;\r\n let data = message.data;\r\n\r\n if (message.msg === 'destroyed' || this._workerDestroying) {\r\n this._workerDestroying = false;\r\n this._worker.terminate();\r\n this._worker = null;\r\n return;\r\n }\r\n\r\n switch (message.msg) {\r\n case TransmuxingEvents.INIT_SEGMENT:\r\n case TransmuxingEvents.MEDIA_SEGMENT:\r\n this._emitter.emit(message.msg, data.type, data.data);\r\n break;\r\n case TransmuxingEvents.LOADING_COMPLETE:\r\n case TransmuxingEvents.RECOVERED_EARLY_EOF:\r\n this._emitter.emit(message.msg);\r\n break;\r\n case TransmuxingEvents.MEDIA_INFO:\r\n Object.setPrototypeOf(data, MediaInfo.prototype);\r\n this._emitter.emit(message.msg, data);\r\n break;\r\n case TransmuxingEvents.METADATA_ARRIVED:\r\n case TransmuxingEvents.SCRIPTDATA_ARRIVED:\r\n case TransmuxingEvents.STATISTICS_INFO:\r\n this._emitter.emit(message.msg, data);\r\n break;\r\n case TransmuxingEvents.IO_ERROR:\r\n case TransmuxingEvents.DEMUX_ERROR:\r\n this._emitter.emit(message.msg, data.type, data.info);\r\n break;\r\n case TransmuxingEvents.RECOMMEND_SEEKPOINT:\r\n this._emitter.emit(message.msg, data);\r\n break;\r\n case 'logcat_callback':\r\n Log.emitter.emit('log', data.type, data.logcat);\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default Transmuxer;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nconst MSEEvents = {\r\n ERROR: 'error',\r\n SOURCE_OPEN: 'source_open',\r\n UPDATE_END: 'update_end',\r\n BUFFER_FULL: 'buffer_full'\r\n};\r\n\r\nexport default MSEEvents;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport Log from '../utils/logger.js';\r\nimport Browser from '../utils/browser.js';\r\nimport MSEEvents from './mse-events.js';\r\nimport {SampleInfo, IDRSampleList} from './media-segment-info.js';\r\nimport {IllegalStateException} from '../utils/exception.js';\r\n\r\n// Media Source Extensions controller\r\nclass MSEController {\r\n\r\n constructor(config) {\r\n this.TAG = 'MSEController';\r\n\r\n this._config = config;\r\n this._emitter = new EventEmitter();\r\n\r\n if (this._config.isLive && this._config.autoCleanupSourceBuffer == undefined) {\r\n // For live stream, do auto cleanup by default\r\n this._config.autoCleanupSourceBuffer = true;\r\n }\r\n\r\n this.e = {\r\n onSourceOpen: this._onSourceOpen.bind(this),\r\n onSourceEnded: this._onSourceEnded.bind(this),\r\n onSourceClose: this._onSourceClose.bind(this),\r\n onSourceBufferError: this._onSourceBufferError.bind(this),\r\n onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this)\r\n };\r\n\r\n this._mediaSource = null;\r\n this._mediaSourceObjectURL = null;\r\n this._mediaElement = null;\r\n\r\n this._isBufferFull = false;\r\n this._hasPendingEos = false;\r\n\r\n this._requireSetMediaDuration = false;\r\n this._pendingMediaDuration = 0;\r\n\r\n this._pendingSourceBufferInit = [];\r\n this._mimeTypes = {\r\n video: null,\r\n audio: null\r\n };\r\n this._sourceBuffers = {\r\n video: null,\r\n audio: null\r\n };\r\n this._lastInitSegments = {\r\n video: null,\r\n audio: null\r\n };\r\n this._pendingSegments = {\r\n video: [],\r\n audio: []\r\n };\r\n this._pendingRemoveRanges = {\r\n video: [],\r\n audio: []\r\n };\r\n this._idrList = new IDRSampleList();\r\n }\r\n\r\n destroy() {\r\n if (this._mediaElement || this._mediaSource) {\r\n this.detachMediaElement();\r\n }\r\n this.e = null;\r\n this._emitter.removeAllListeners();\r\n this._emitter = null;\r\n }\r\n\r\n on(event, listener) {\r\n this._emitter.addListener(event, listener);\r\n }\r\n\r\n off(event, listener) {\r\n this._emitter.removeListener(event, listener);\r\n }\r\n\r\n attachMediaElement(mediaElement) {\r\n if (this._mediaSource) {\r\n throw new IllegalStateException('MediaSource has been attached to an HTMLMediaElement!');\r\n }\r\n let ms = this._mediaSource = new window.MediaSource();\r\n ms.addEventListener('sourceopen', this.e.onSourceOpen);\r\n ms.addEventListener('sourceended', this.e.onSourceEnded);\r\n ms.addEventListener('sourceclose', this.e.onSourceClose);\r\n\r\n this._mediaElement = mediaElement;\r\n this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);\r\n mediaElement.src = this._mediaSourceObjectURL;\r\n }\r\n\r\n detachMediaElement() {\r\n if (this._mediaSource) {\r\n let ms = this._mediaSource;\r\n for (let type in this._sourceBuffers) {\r\n // pending segments should be discard\r\n let ps = this._pendingSegments[type];\r\n ps.splice(0, ps.length);\r\n this._pendingSegments[type] = null;\r\n this._pendingRemoveRanges[type] = null;\r\n this._lastInitSegments[type] = null;\r\n\r\n // remove all sourcebuffers\r\n let sb = this._sourceBuffers[type];\r\n if (sb) {\r\n if (ms.readyState !== 'closed') {\r\n // ms edge can throw an error: Unexpected call to method or property access\r\n try {\r\n ms.removeSourceBuffer(sb);\r\n } catch (error) {\r\n Log.e(this.TAG, error.message);\r\n }\r\n sb.removeEventListener('error', this.e.onSourceBufferError);\r\n sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd);\r\n }\r\n this._mimeTypes[type] = null;\r\n this._sourceBuffers[type] = null;\r\n }\r\n }\r\n if (ms.readyState === 'open') {\r\n try {\r\n ms.endOfStream();\r\n } catch (error) {\r\n Log.e(this.TAG, error.message);\r\n }\r\n }\r\n ms.removeEventListener('sourceopen', this.e.onSourceOpen);\r\n ms.removeEventListener('sourceended', this.e.onSourceEnded);\r\n ms.removeEventListener('sourceclose', this.e.onSourceClose);\r\n this._pendingSourceBufferInit = [];\r\n this._isBufferFull = false;\r\n this._idrList.clear();\r\n this._mediaSource = null;\r\n }\r\n\r\n if (this._mediaElement) {\r\n this._mediaElement.src = '';\r\n this._mediaElement.removeAttribute('src');\r\n this._mediaElement = null;\r\n }\r\n if (this._mediaSourceObjectURL) {\r\n window.URL.revokeObjectURL(this._mediaSourceObjectURL);\r\n this._mediaSourceObjectURL = null;\r\n }\r\n }\r\n\r\n appendInitSegment(initSegment, deferred) {\r\n if (!this._mediaSource || this._mediaSource.readyState !== 'open') {\r\n // sourcebuffer creation requires mediaSource.readyState === 'open'\r\n // so we defer the sourcebuffer creation, until sourceopen event triggered\r\n this._pendingSourceBufferInit.push(initSegment);\r\n // make sure that this InitSegment is in the front of pending segments queue\r\n this._pendingSegments[initSegment.type].push(initSegment);\r\n return;\r\n }\r\n\r\n let is = initSegment;\r\n let mimeType = `${is.container}`;\r\n if (is.codec && is.codec.length > 0) {\r\n mimeType += `;codecs=${is.codec}`;\r\n }\r\n\r\n let firstInitSegment = false;\r\n\r\n Log.v(this.TAG, 'Received Initialization Segment, mimeType: ' + mimeType);\r\n this._lastInitSegments[is.type] = is;\r\n\r\n if (mimeType !== this._mimeTypes[is.type]) {\r\n if (!this._mimeTypes[is.type]) { // empty, first chance create sourcebuffer\r\n firstInitSegment = true;\r\n try {\r\n let sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType);\r\n sb.addEventListener('error', this.e.onSourceBufferError);\r\n sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd);\r\n } catch (error) {\r\n Log.e(this.TAG, error.message);\r\n this._emitter.emit(MSEEvents.ERROR, {code: error.code, msg: error.message});\r\n return;\r\n }\r\n } else {\r\n Log.v(this.TAG, `Notice: ${is.type} mimeType changed, origin: ${this._mimeTypes[is.type]}, target: ${mimeType}`);\r\n }\r\n this._mimeTypes[is.type] = mimeType;\r\n }\r\n\r\n if (!deferred) {\r\n // deferred means this InitSegment has been pushed to pendingSegments queue\r\n this._pendingSegments[is.type].push(is);\r\n }\r\n if (!firstInitSegment) { // append immediately only if init segment in subsequence\r\n if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) {\r\n this._doAppendSegments();\r\n }\r\n }\r\n if (Browser.safari && is.container === 'audio/mpeg' && is.mediaDuration > 0) {\r\n // 'audio/mpeg' track under Safari may cause MediaElement's duration to be NaN\r\n // Manually correct MediaSource.duration to make progress bar seekable, and report right duration\r\n this._requireSetMediaDuration = true;\r\n this._pendingMediaDuration = is.mediaDuration / 1000; // in seconds\r\n this._updateMediaSourceDuration();\r\n }\r\n }\r\n\r\n appendMediaSegment(mediaSegment) {\r\n let ms = mediaSegment;\r\n this._pendingSegments[ms.type].push(ms);\r\n\r\n if (this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer()) {\r\n this._doCleanupSourceBuffer();\r\n }\r\n\r\n let sb = this._sourceBuffers[ms.type];\r\n if (sb && !sb.updating && !this._hasPendingRemoveRanges()) {\r\n this._doAppendSegments();\r\n }\r\n }\r\n\r\n seek(seconds) {\r\n // remove all appended buffers\r\n for (let type in this._sourceBuffers) {\r\n if (!this._sourceBuffers[type]) {\r\n continue;\r\n }\r\n\r\n // abort current buffer append algorithm\r\n let sb = this._sourceBuffers[type];\r\n if (this._mediaSource.readyState === 'open') {\r\n try {\r\n // If range removal algorithm is running, InvalidStateError will be throwed\r\n // Ignore it.\r\n sb.abort();\r\n } catch (error) {\r\n Log.e(this.TAG, error.message);\r\n }\r\n }\r\n\r\n // IDRList should be clear\r\n this._idrList.clear();\r\n\r\n // pending segments should be discard\r\n let ps = this._pendingSegments[type];\r\n ps.splice(0, ps.length);\r\n\r\n if (this._mediaSource.readyState === 'closed') {\r\n // Parent MediaSource object has been detached from HTMLMediaElement\r\n continue;\r\n }\r\n\r\n // record ranges to be remove from SourceBuffer\r\n for (let i = 0; i < sb.buffered.length; i++) {\r\n let start = sb.buffered.start(i);\r\n let end = sb.buffered.end(i);\r\n this._pendingRemoveRanges[type].push({start, end});\r\n }\r\n\r\n // if sb is not updating, let's remove ranges now!\r\n if (!sb.updating) {\r\n this._doRemoveRanges();\r\n }\r\n\r\n // Safari 10 may get InvalidStateError in the later appendBuffer() after SourceBuffer.remove() call\r\n // Internal parser's state may be invalid at this time. Re-append last InitSegment to workaround.\r\n // Related issue: https://bugs.webkit.org/show_bug.cgi?id=159230\r\n if (Browser.safari) {\r\n let lastInitSegment = this._lastInitSegments[type];\r\n if (lastInitSegment) {\r\n this._pendingSegments[type].push(lastInitSegment);\r\n if (!sb.updating) {\r\n this._doAppendSegments();\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n endOfStream() {\r\n let ms = this._mediaSource;\r\n let sb = this._sourceBuffers;\r\n if (!ms || ms.readyState !== 'open') {\r\n if (ms && ms.readyState === 'closed' && this._hasPendingSegments()) {\r\n // If MediaSource hasn't turned into open state, and there're pending segments\r\n // Mark pending endOfStream, defer call until all pending segments appended complete\r\n this._hasPendingEos = true;\r\n }\r\n return;\r\n }\r\n if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) {\r\n // If any sourcebuffer is updating, defer endOfStream operation\r\n // See _onSourceBufferUpdateEnd()\r\n this._hasPendingEos = true;\r\n } else {\r\n this._hasPendingEos = false;\r\n // Notify media data loading complete\r\n // This is helpful for correcting total duration to match last media segment\r\n // Otherwise MediaElement's ended event may not be triggered\r\n ms.endOfStream();\r\n }\r\n }\r\n\r\n getNearestKeyframe(dts) {\r\n return this._idrList.getLastSyncPointBeforeDts(dts);\r\n }\r\n\r\n _needCleanupSourceBuffer() {\r\n if (!this._config.autoCleanupSourceBuffer) {\r\n return false;\r\n }\r\n\r\n let currentTime = this._mediaElement.currentTime;\r\n\r\n for (let type in this._sourceBuffers) {\r\n let sb = this._sourceBuffers[type];\r\n if (sb) {\r\n let buffered = sb.buffered;\r\n if (buffered.length >= 1) {\r\n if (currentTime - buffered.start(0) >= this._config.autoCleanupMaxBackwardDuration) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n _doCleanupSourceBuffer() {\r\n let currentTime = this._mediaElement.currentTime;\r\n\r\n for (let type in this._sourceBuffers) {\r\n let sb = this._sourceBuffers[type];\r\n if (sb) {\r\n let buffered = sb.buffered;\r\n let doRemove = false;\r\n\r\n for (let i = 0; i < buffered.length; i++) {\r\n let start = buffered.start(i);\r\n let end = buffered.end(i);\r\n\r\n if (start <= currentTime && currentTime < end + 3) { // padding 3 seconds\r\n if (currentTime - start >= this._config.autoCleanupMaxBackwardDuration) {\r\n doRemove = true;\r\n let removeEnd = currentTime - this._config.autoCleanupMinBackwardDuration;\r\n this._pendingRemoveRanges[type].push({start: start, end: removeEnd});\r\n }\r\n } else if (end < currentTime) {\r\n doRemove = true;\r\n this._pendingRemoveRanges[type].push({start: start, end: end});\r\n }\r\n }\r\n\r\n if (doRemove && !sb.updating) {\r\n this._doRemoveRanges();\r\n }\r\n }\r\n }\r\n }\r\n\r\n _updateMediaSourceDuration() {\r\n let sb = this._sourceBuffers;\r\n if (this._mediaElement.readyState === 0 || this._mediaSource.readyState !== 'open') {\r\n return;\r\n }\r\n if ((sb.video && sb.video.updating) || (sb.audio && sb.audio.updating)) {\r\n return;\r\n }\r\n\r\n let current = this._mediaSource.duration;\r\n let target = this._pendingMediaDuration;\r\n\r\n if (target > 0 && (isNaN(current) || target > current)) {\r\n Log.v(this.TAG, `Update MediaSource duration from ${current} to ${target}`);\r\n this._mediaSource.duration = target;\r\n }\r\n\r\n this._requireSetMediaDuration = false;\r\n this._pendingMediaDuration = 0;\r\n }\r\n\r\n _doRemoveRanges() {\r\n for (let type in this._pendingRemoveRanges) {\r\n if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {\r\n continue;\r\n }\r\n let sb = this._sourceBuffers[type];\r\n let ranges = this._pendingRemoveRanges[type];\r\n while (ranges.length && !sb.updating) {\r\n let range = ranges.shift();\r\n sb.remove(range.start, range.end);\r\n }\r\n }\r\n }\r\n\r\n _doAppendSegments() {\r\n let pendingSegments = this._pendingSegments;\r\n\r\n for (let type in pendingSegments) {\r\n if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {\r\n continue;\r\n }\r\n\r\n if (pendingSegments[type].length > 0) {\r\n let segment = pendingSegments[type].shift();\r\n\r\n if (segment.timestampOffset) {\r\n // For MPEG audio stream in MSE, if unbuffered-seeking occurred\r\n // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.\r\n let currentOffset = this._sourceBuffers[type].timestampOffset;\r\n let targetOffset = segment.timestampOffset / 1000; // in seconds\r\n\r\n let delta = Math.abs(currentOffset - targetOffset);\r\n if (delta > 0.1) { // If time delta > 100ms\r\n Log.v(this.TAG, `Update MPEG audio timestampOffset from ${currentOffset} to ${targetOffset}`);\r\n this._sourceBuffers[type].timestampOffset = targetOffset;\r\n }\r\n delete segment.timestampOffset;\r\n }\r\n\r\n if (!segment.data || segment.data.byteLength === 0) {\r\n // Ignore empty buffer\r\n continue;\r\n }\r\n\r\n try {\r\n this._sourceBuffers[type].appendBuffer(segment.data);\r\n this._isBufferFull = false;\r\n if (type === 'video' && segment.hasOwnProperty('info')) {\r\n this._idrList.appendArray(segment.info.syncPoints);\r\n }\r\n } catch (error) {\r\n this._pendingSegments[type].unshift(segment);\r\n if (error.code === 22) { // QuotaExceededError\r\n /* Notice that FireFox may not throw QuotaExceededError if SourceBuffer is full\r\n * Currently we can only do lazy-load to avoid SourceBuffer become scattered.\r\n * SourceBuffer eviction policy may be changed in future version of FireFox.\r\n *\r\n * Related issues:\r\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1279885\r\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1280023\r\n */\r\n\r\n // report buffer full, abort network IO\r\n if (!this._isBufferFull) {\r\n this._emitter.emit(MSEEvents.BUFFER_FULL);\r\n }\r\n this._isBufferFull = true;\r\n } else {\r\n Log.e(this.TAG, error.message);\r\n this._emitter.emit(MSEEvents.ERROR, {code: error.code, msg: error.message});\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n _onSourceOpen() {\r\n Log.v(this.TAG, 'MediaSource onSourceOpen');\r\n this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);\r\n // deferred sourcebuffer creation / initialization\r\n if (this._pendingSourceBufferInit.length > 0) {\r\n let pendings = this._pendingSourceBufferInit;\r\n while (pendings.length) {\r\n let segment = pendings.shift();\r\n this.appendInitSegment(segment, true);\r\n }\r\n }\r\n // there may be some pending media segments, append them\r\n if (this._hasPendingSegments()) {\r\n this._doAppendSegments();\r\n }\r\n this._emitter.emit(MSEEvents.SOURCE_OPEN);\r\n }\r\n\r\n _onSourceEnded() {\r\n // fired on endOfStream\r\n Log.v(this.TAG, 'MediaSource onSourceEnded');\r\n }\r\n\r\n _onSourceClose() {\r\n // fired on detaching from media element\r\n Log.v(this.TAG, 'MediaSource onSourceClose');\r\n if (this._mediaSource && this.e != null) {\r\n this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);\r\n this._mediaSource.removeEventListener('sourceended', this.e.onSourceEnded);\r\n this._mediaSource.removeEventListener('sourceclose', this.e.onSourceClose);\r\n }\r\n }\r\n\r\n _hasPendingSegments() {\r\n let ps = this._pendingSegments;\r\n return ps.video.length > 0 || ps.audio.length > 0;\r\n }\r\n\r\n _hasPendingRemoveRanges() {\r\n let prr = this._pendingRemoveRanges;\r\n return prr.video.length > 0 || prr.audio.length > 0;\r\n }\r\n\r\n _onSourceBufferUpdateEnd() {\r\n if (this._requireSetMediaDuration) {\r\n this._updateMediaSourceDuration();\r\n } else if (this._hasPendingRemoveRanges()) {\r\n this._doRemoveRanges();\r\n } else if (this._hasPendingSegments()) {\r\n this._doAppendSegments();\r\n } else if (this._hasPendingEos) {\r\n this.endOfStream();\r\n }\r\n this._emitter.emit(MSEEvents.UPDATE_END);\r\n }\r\n\r\n _onSourceBufferError(e) {\r\n Log.e(this.TAG, `SourceBuffer Error: ${e}`);\r\n // this error might not always be fatal, just ignore it\r\n }\r\n\r\n}\r\n\r\nexport default MSEController;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport {LoaderErrors} from '../io/loader.js';\r\nimport DemuxErrors from '../demux/demux-errors.js';\r\n\r\nexport const ErrorTypes = {\r\n NETWORK_ERROR: 'NetworkError',\r\n MEDIA_ERROR: 'MediaError',\r\n OTHER_ERROR: 'OtherError'\r\n};\r\n\r\nexport const ErrorDetails = {\r\n NETWORK_EXCEPTION: LoaderErrors.EXCEPTION,\r\n NETWORK_STATUS_CODE_INVALID: LoaderErrors.HTTP_STATUS_CODE_INVALID,\r\n NETWORK_TIMEOUT: LoaderErrors.CONNECTING_TIMEOUT,\r\n NETWORK_UNRECOVERABLE_EARLY_EOF: LoaderErrors.UNRECOVERABLE_EARLY_EOF,\r\n\r\n MEDIA_MSE_ERROR: 'MediaMSEError',\r\n\r\n MEDIA_FORMAT_ERROR: DemuxErrors.FORMAT_ERROR,\r\n MEDIA_FORMAT_UNSUPPORTED: DemuxErrors.FORMAT_UNSUPPORTED,\r\n MEDIA_CODEC_UNSUPPORTED: DemuxErrors.CODEC_UNSUPPORTED\r\n};","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport Log from '../utils/logger.js';\r\nimport Browser from '../utils/browser.js';\r\nimport PlayerEvents from './player-events.js';\r\nimport Transmuxer from '../core/transmuxer.js';\r\nimport TransmuxingEvents from '../core/transmuxing-events.js';\r\nimport MSEController from '../core/mse-controller.js';\r\nimport MSEEvents from '../core/mse-events.js';\r\nimport {ErrorTypes, ErrorDetails} from './player-errors.js';\r\nimport {createDefaultConfig} from '../config.js';\r\nimport {InvalidArgumentException, IllegalStateException} from '../utils/exception.js';\r\n\r\nclass FlvPlayer {\r\n\r\n constructor(mediaDataSource, config) {\r\n this.TAG = 'FlvPlayer';\r\n this._type = 'FlvPlayer';\r\n this._emitter = new EventEmitter();\r\n\r\n this._config = createDefaultConfig();\r\n if (typeof config === 'object') {\r\n Object.assign(this._config, config);\r\n }\r\n\r\n if (mediaDataSource.type.toLowerCase() !== 'flv') {\r\n throw new InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!');\r\n }\r\n\r\n if (mediaDataSource.isLive === true) {\r\n this._config.isLive = true;\r\n }\r\n\r\n this.e = {\r\n onvLoadedMetadata: this._onvLoadedMetadata.bind(this),\r\n onvSeeking: this._onvSeeking.bind(this),\r\n onvCanPlay: this._onvCanPlay.bind(this),\r\n onvStalled: this._onvStalled.bind(this),\r\n onvProgress: this._onvProgress.bind(this)\r\n };\r\n\r\n if (self.performance && self.performance.now) {\r\n this._now = self.performance.now.bind(self.performance);\r\n } else {\r\n this._now = Date.now;\r\n }\r\n\r\n this._pendingSeekTime = null; // in seconds\r\n this._requestSetTime = false;\r\n this._seekpointRecord = null;\r\n this._progressChecker = null;\r\n\r\n this._mediaDataSource = mediaDataSource;\r\n this._mediaElement = null;\r\n this._msectl = null;\r\n this._transmuxer = null;\r\n\r\n this._mseSourceOpened = false;\r\n this._hasPendingLoad = false;\r\n this._receivedCanPlay = false;\r\n\r\n this._mediaInfo = null;\r\n this._statisticsInfo = null;\r\n\r\n let chromeNeedIDRFix = (Browser.chrome &&\r\n (Browser.version.major < 50 ||\r\n (Browser.version.major === 50 && Browser.version.build < 2661)));\r\n this._alwaysSeekKeyframe = (chromeNeedIDRFix || Browser.msedge || Browser.msie) ? true : false;\r\n\r\n if (this._alwaysSeekKeyframe) {\r\n this._config.accurateSeek = false;\r\n }\r\n }\r\n\r\n destroy() {\r\n if (this._progressChecker != null) {\r\n window.clearInterval(this._progressChecker);\r\n this._progressChecker = null;\r\n }\r\n if (this._transmuxer) {\r\n this.unload();\r\n }\r\n if (this._mediaElement) {\r\n this.detachMediaElement();\r\n }\r\n this.e = null;\r\n this._mediaDataSource = null;\r\n\r\n this._emitter.removeAllListeners();\r\n this._emitter = null;\r\n }\r\n\r\n on(event, listener) {\r\n if (event === PlayerEvents.MEDIA_INFO) {\r\n if (this._mediaInfo != null) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);\r\n });\r\n }\r\n } else if (event === PlayerEvents.STATISTICS_INFO) {\r\n if (this._statisticsInfo != null) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);\r\n });\r\n }\r\n }\r\n this._emitter.addListener(event, listener);\r\n }\r\n\r\n off(event, listener) {\r\n this._emitter.removeListener(event, listener);\r\n }\r\n\r\n attachMediaElement(mediaElement) {\r\n this._mediaElement = mediaElement;\r\n mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);\r\n mediaElement.addEventListener('seeking', this.e.onvSeeking);\r\n mediaElement.addEventListener('canplay', this.e.onvCanPlay);\r\n mediaElement.addEventListener('stalled', this.e.onvStalled);\r\n mediaElement.addEventListener('progress', this.e.onvProgress);\r\n\r\n this._msectl = new MSEController(this._config);\r\n\r\n this._msectl.on(MSEEvents.UPDATE_END, this._onmseUpdateEnd.bind(this));\r\n this._msectl.on(MSEEvents.BUFFER_FULL, this._onmseBufferFull.bind(this));\r\n this._msectl.on(MSEEvents.SOURCE_OPEN, () => {\r\n this._mseSourceOpened = true;\r\n if (this._hasPendingLoad) {\r\n this._hasPendingLoad = false;\r\n this.load();\r\n }\r\n });\r\n this._msectl.on(MSEEvents.ERROR, (info) => {\r\n this._emitter.emit(PlayerEvents.ERROR,\r\n ErrorTypes.MEDIA_ERROR,\r\n ErrorDetails.MEDIA_MSE_ERROR,\r\n info\r\n );\r\n });\r\n\r\n this._msectl.attachMediaElement(mediaElement);\r\n\r\n if (this._pendingSeekTime != null) {\r\n try {\r\n mediaElement.currentTime = this._pendingSeekTime;\r\n this._pendingSeekTime = null;\r\n } catch (e) {\r\n // IE11 may throw InvalidStateError if readyState === 0\r\n // We can defer set currentTime operation after loadedmetadata\r\n }\r\n }\r\n }\r\n\r\n detachMediaElement() {\r\n if (this._mediaElement) {\r\n this._msectl.detachMediaElement();\r\n this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);\r\n this._mediaElement.removeEventListener('seeking', this.e.onvSeeking);\r\n this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);\r\n this._mediaElement.removeEventListener('stalled', this.e.onvStalled);\r\n this._mediaElement.removeEventListener('progress', this.e.onvProgress);\r\n this._mediaElement = null;\r\n }\r\n if (this._msectl) {\r\n this._msectl.destroy();\r\n this._msectl = null;\r\n }\r\n }\r\n\r\n load() {\r\n if (!this._mediaElement) {\r\n throw new IllegalStateException('HTMLMediaElement must be attached before load()!');\r\n }\r\n if (this._transmuxer) {\r\n throw new IllegalStateException('FlvPlayer.load() has been called, please call unload() first!');\r\n }\r\n if (this._hasPendingLoad) {\r\n return;\r\n }\r\n\r\n if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) {\r\n this._hasPendingLoad = true;\r\n return;\r\n }\r\n\r\n if (this._mediaElement.readyState > 0) {\r\n this._requestSetTime = true;\r\n // IE11 may throw InvalidStateError if readyState === 0\r\n this._mediaElement.currentTime = 0;\r\n }\r\n\r\n this._transmuxer = new Transmuxer(this._mediaDataSource, this._config);\r\n\r\n this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => {\r\n this._msectl.appendInitSegment(is);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => {\r\n this._msectl.appendMediaSegment(ms);\r\n\r\n // lazyLoad check\r\n if (this._config.lazyLoad && !this._config.isLive) {\r\n let currentTime = this._mediaElement.currentTime;\r\n if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) {\r\n if (this._progressChecker == null) {\r\n Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');\r\n this._suspendTransmuxer();\r\n }\r\n }\r\n }\r\n });\r\n this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => {\r\n this._msectl.endOfStream();\r\n this._emitter.emit(PlayerEvents.LOADING_COMPLETE);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => {\r\n this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => {\r\n this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => {\r\n this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info});\r\n });\r\n this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => {\r\n this._mediaInfo = mediaInfo;\r\n this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo));\r\n });\r\n this._transmuxer.on(TransmuxingEvents.METADATA_ARRIVED, (metadata) => {\r\n this._emitter.emit(PlayerEvents.METADATA_ARRIVED, metadata);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, (data) => {\r\n this._emitter.emit(PlayerEvents.SCRIPTDATA_ARRIVED, data);\r\n });\r\n this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => {\r\n this._statisticsInfo = this._fillStatisticsInfo(statInfo);\r\n this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo));\r\n });\r\n this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => {\r\n if (this._mediaElement && !this._config.accurateSeek) {\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = milliseconds / 1000;\r\n }\r\n });\r\n\r\n this._transmuxer.open();\r\n }\r\n\r\n unload() {\r\n if (this._mediaElement) {\r\n this._mediaElement.pause();\r\n }\r\n if (this._msectl) {\r\n this._msectl.seek(0);\r\n }\r\n if (this._transmuxer) {\r\n this._transmuxer.close();\r\n this._transmuxer.destroy();\r\n this._transmuxer = null;\r\n }\r\n }\r\n\r\n play() {\r\n return this._mediaElement.play();\r\n }\r\n\r\n pause() {\r\n this._mediaElement.pause();\r\n }\r\n\r\n get type() {\r\n return this._type;\r\n }\r\n\r\n get buffered() {\r\n return this._mediaElement.buffered;\r\n }\r\n\r\n get duration() {\r\n return this._mediaElement.duration;\r\n }\r\n\r\n get volume() {\r\n return this._mediaElement.volume;\r\n }\r\n\r\n set volume(value) {\r\n this._mediaElement.volume = value;\r\n }\r\n\r\n get muted() {\r\n return this._mediaElement.muted;\r\n }\r\n\r\n set muted(muted) {\r\n this._mediaElement.muted = muted;\r\n }\r\n\r\n get currentTime() {\r\n if (this._mediaElement) {\r\n return this._mediaElement.currentTime;\r\n }\r\n return 0;\r\n }\r\n\r\n set currentTime(seconds) {\r\n if (this._mediaElement) {\r\n this._internalSeek(seconds);\r\n } else {\r\n this._pendingSeekTime = seconds;\r\n }\r\n }\r\n\r\n get mediaInfo() {\r\n return Object.assign({}, this._mediaInfo);\r\n }\r\n\r\n get statisticsInfo() {\r\n if (this._statisticsInfo == null) {\r\n this._statisticsInfo = {};\r\n }\r\n this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo);\r\n return Object.assign({}, this._statisticsInfo);\r\n }\r\n\r\n _fillStatisticsInfo(statInfo) {\r\n statInfo.playerType = this._type;\r\n\r\n if (!(this._mediaElement instanceof HTMLVideoElement)) {\r\n return statInfo;\r\n }\r\n\r\n let hasQualityInfo = true;\r\n let decoded = 0;\r\n let dropped = 0;\r\n\r\n if (this._mediaElement.getVideoPlaybackQuality) {\r\n let quality = this._mediaElement.getVideoPlaybackQuality();\r\n decoded = quality.totalVideoFrames;\r\n dropped = quality.droppedVideoFrames;\r\n } else if (this._mediaElement.webkitDecodedFrameCount != undefined) {\r\n decoded = this._mediaElement.webkitDecodedFrameCount;\r\n dropped = this._mediaElement.webkitDroppedFrameCount;\r\n } else {\r\n hasQualityInfo = false;\r\n }\r\n\r\n if (hasQualityInfo) {\r\n statInfo.decodedFrames = decoded;\r\n statInfo.droppedFrames = dropped;\r\n }\r\n\r\n return statInfo;\r\n }\r\n\r\n _onmseUpdateEnd() {\r\n if (!this._config.lazyLoad || this._config.isLive) {\r\n return;\r\n }\r\n\r\n let buffered = this._mediaElement.buffered;\r\n let currentTime = this._mediaElement.currentTime;\r\n let currentRangeStart = 0;\r\n let currentRangeEnd = 0;\r\n\r\n for (let i = 0; i < buffered.length; i++) {\r\n let start = buffered.start(i);\r\n let end = buffered.end(i);\r\n if (start <= currentTime && currentTime < end) {\r\n currentRangeStart = start;\r\n currentRangeEnd = end;\r\n break;\r\n }\r\n }\r\n\r\n if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) {\r\n Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');\r\n this._suspendTransmuxer();\r\n }\r\n }\r\n\r\n _onmseBufferFull() {\r\n Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task');\r\n if (this._progressChecker == null) {\r\n this._suspendTransmuxer();\r\n }\r\n }\r\n\r\n _suspendTransmuxer() {\r\n if (this._transmuxer) {\r\n this._transmuxer.pause();\r\n\r\n if (this._progressChecker == null) {\r\n this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000);\r\n }\r\n }\r\n }\r\n\r\n _checkProgressAndResume() {\r\n let currentTime = this._mediaElement.currentTime;\r\n let buffered = this._mediaElement.buffered;\r\n\r\n let needResume = false;\r\n\r\n for (let i = 0; i < buffered.length; i++) {\r\n let from = buffered.start(i);\r\n let to = buffered.end(i);\r\n if (currentTime >= from && currentTime < to) {\r\n if (currentTime >= to - this._config.lazyLoadRecoverDuration) {\r\n needResume = true;\r\n }\r\n break;\r\n }\r\n }\r\n\r\n if (needResume) {\r\n window.clearInterval(this._progressChecker);\r\n this._progressChecker = null;\r\n if (needResume) {\r\n Log.v(this.TAG, 'Continue loading from paused position');\r\n this._transmuxer.resume();\r\n }\r\n }\r\n }\r\n\r\n _isTimepointBuffered(seconds) {\r\n let buffered = this._mediaElement.buffered;\r\n\r\n for (let i = 0; i < buffered.length; i++) {\r\n let from = buffered.start(i);\r\n let to = buffered.end(i);\r\n if (seconds >= from && seconds < to) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n _internalSeek(seconds) {\r\n let directSeek = this._isTimepointBuffered(seconds);\r\n\r\n let directSeekBegin = false;\r\n let directSeekBeginTime = 0;\r\n\r\n if (seconds < 1.0 && this._mediaElement.buffered.length > 0) {\r\n let videoBeginTime = this._mediaElement.buffered.start(0);\r\n if ((videoBeginTime < 1.0 && seconds < videoBeginTime) || Browser.safari) {\r\n directSeekBegin = true;\r\n // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid\r\n directSeekBeginTime = Browser.safari ? 0.1 : videoBeginTime;\r\n }\r\n }\r\n\r\n if (directSeekBegin) { // seek to video begin, set currentTime directly if beginPTS buffered\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = directSeekBeginTime;\r\n } else if (directSeek) { // buffered position\r\n if (!this._alwaysSeekKeyframe) {\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = seconds;\r\n } else {\r\n let idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000));\r\n this._requestSetTime = true;\r\n if (idr != null) {\r\n this._mediaElement.currentTime = idr.dts / 1000;\r\n } else {\r\n this._mediaElement.currentTime = seconds;\r\n }\r\n }\r\n if (this._progressChecker != null) {\r\n this._checkProgressAndResume();\r\n }\r\n } else {\r\n if (this._progressChecker != null) {\r\n window.clearInterval(this._progressChecker);\r\n this._progressChecker = null;\r\n }\r\n this._msectl.seek(seconds);\r\n this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds\r\n // no need to set mediaElement.currentTime if non-accurateSeek,\r\n // just wait for the recommend_seekpoint callback\r\n if (this._config.accurateSeek) {\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = seconds;\r\n }\r\n }\r\n }\r\n\r\n _checkAndApplyUnbufferedSeekpoint() {\r\n if (this._seekpointRecord) {\r\n if (this._seekpointRecord.recordTime <= this._now() - 100) {\r\n let target = this._mediaElement.currentTime;\r\n this._seekpointRecord = null;\r\n if (!this._isTimepointBuffered(target)) {\r\n if (this._progressChecker != null) {\r\n window.clearTimeout(this._progressChecker);\r\n this._progressChecker = null;\r\n }\r\n // .currentTime is consists with .buffered timestamp\r\n // Chrome/Edge use DTS, while FireFox/Safari use PTS\r\n this._msectl.seek(target);\r\n this._transmuxer.seek(Math.floor(target * 1000));\r\n // set currentTime if accurateSeek, or wait for recommend_seekpoint callback\r\n if (this._config.accurateSeek) {\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = target;\r\n }\r\n }\r\n } else {\r\n window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);\r\n }\r\n }\r\n }\r\n\r\n _checkAndResumeStuckPlayback(stalled) {\r\n let media = this._mediaElement;\r\n if (stalled || !this._receivedCanPlay || media.readyState < 2) { // HAVE_CURRENT_DATA\r\n let buffered = media.buffered;\r\n if (buffered.length > 0 && media.currentTime < buffered.start(0)) {\r\n Log.w(this.TAG, `Playback seems stuck at ${media.currentTime}, seek to ${buffered.start(0)}`);\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = buffered.start(0);\r\n this._mediaElement.removeEventListener('progress', this.e.onvProgress);\r\n }\r\n } else {\r\n // Playback didn't stuck, remove progress event listener\r\n this._mediaElement.removeEventListener('progress', this.e.onvProgress);\r\n }\r\n }\r\n\r\n _onvLoadedMetadata(e) {\r\n if (this._pendingSeekTime != null) {\r\n this._mediaElement.currentTime = this._pendingSeekTime;\r\n this._pendingSeekTime = null;\r\n }\r\n }\r\n\r\n _onvSeeking(e) { // handle seeking request from browser's progress bar\r\n let target = this._mediaElement.currentTime;\r\n let buffered = this._mediaElement.buffered;\r\n\r\n if (this._requestSetTime) {\r\n this._requestSetTime = false;\r\n return;\r\n }\r\n\r\n if (target < 1.0 && buffered.length > 0) {\r\n // seek to video begin, set currentTime directly if beginPTS buffered\r\n let videoBeginTime = buffered.start(0);\r\n if ((videoBeginTime < 1.0 && target < videoBeginTime) || Browser.safari) {\r\n this._requestSetTime = true;\r\n // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid\r\n this._mediaElement.currentTime = Browser.safari ? 0.1 : videoBeginTime;\r\n return;\r\n }\r\n }\r\n\r\n if (this._isTimepointBuffered(target)) {\r\n if (this._alwaysSeekKeyframe) {\r\n let idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000));\r\n if (idr != null) {\r\n this._requestSetTime = true;\r\n this._mediaElement.currentTime = idr.dts / 1000;\r\n }\r\n }\r\n if (this._progressChecker != null) {\r\n this._checkProgressAndResume();\r\n }\r\n return;\r\n }\r\n\r\n this._seekpointRecord = {\r\n seekPoint: target,\r\n recordTime: this._now()\r\n };\r\n window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);\r\n }\r\n\r\n _onvCanPlay(e) {\r\n this._receivedCanPlay = true;\r\n this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);\r\n }\r\n\r\n _onvStalled(e) {\r\n this._checkAndResumeStuckPlayback(true);\r\n }\r\n\r\n _onvProgress(e) {\r\n this._checkAndResumeStuckPlayback();\r\n }\r\n\r\n}\r\n\r\nexport default FlvPlayer;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport PlayerEvents from './player-events.js';\r\nimport {createDefaultConfig} from '../config.js';\r\nimport {InvalidArgumentException, IllegalStateException} from '../utils/exception.js';\r\n\r\n// Player wrapper for browser's native player (HTMLVideoElement) without MediaSource src. \r\nclass NativePlayer {\r\n\r\n constructor(mediaDataSource, config) {\r\n this.TAG = 'NativePlayer';\r\n this._type = 'NativePlayer';\r\n this._emitter = new EventEmitter();\r\n\r\n this._config = createDefaultConfig();\r\n if (typeof config === 'object') {\r\n Object.assign(this._config, config);\r\n }\r\n\r\n if (mediaDataSource.type.toLowerCase() === 'flv') {\r\n throw new InvalidArgumentException('NativePlayer does\\'t support flv MediaDataSource input!');\r\n }\r\n if (mediaDataSource.hasOwnProperty('segments')) {\r\n throw new InvalidArgumentException(`NativePlayer(${mediaDataSource.type}) doesn't support multipart playback!`);\r\n }\r\n\r\n this.e = {\r\n onvLoadedMetadata: this._onvLoadedMetadata.bind(this)\r\n };\r\n\r\n this._pendingSeekTime = null;\r\n this._statisticsReporter = null;\r\n\r\n this._mediaDataSource = mediaDataSource;\r\n this._mediaElement = null;\r\n }\r\n\r\n destroy() {\r\n if (this._mediaElement) {\r\n this.unload();\r\n this.detachMediaElement();\r\n }\r\n this.e = null;\r\n this._mediaDataSource = null;\r\n this._emitter.removeAllListeners();\r\n this._emitter = null;\r\n }\r\n\r\n on(event, listener) {\r\n if (event === PlayerEvents.MEDIA_INFO) {\r\n if (this._mediaElement != null && this._mediaElement.readyState !== 0) { // HAVE_NOTHING\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);\r\n });\r\n }\r\n } else if (event === PlayerEvents.STATISTICS_INFO) {\r\n if (this._mediaElement != null && this._mediaElement.readyState !== 0) {\r\n Promise.resolve().then(() => {\r\n this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);\r\n });\r\n }\r\n }\r\n this._emitter.addListener(event, listener);\r\n }\r\n\r\n off(event, listener) {\r\n this._emitter.removeListener(event, listener);\r\n }\r\n\r\n attachMediaElement(mediaElement) {\r\n this._mediaElement = mediaElement;\r\n mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);\r\n\r\n if (this._pendingSeekTime != null) {\r\n try {\r\n mediaElement.currentTime = this._pendingSeekTime;\r\n this._pendingSeekTime = null;\r\n } catch (e) {\r\n // IE11 may throw InvalidStateError if readyState === 0\r\n // Defer set currentTime operation after loadedmetadata\r\n }\r\n }\r\n }\r\n\r\n detachMediaElement() {\r\n if (this._mediaElement) {\r\n this._mediaElement.src = '';\r\n this._mediaElement.removeAttribute('src');\r\n this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);\r\n this._mediaElement = null;\r\n }\r\n if (this._statisticsReporter != null) {\r\n window.clearInterval(this._statisticsReporter);\r\n this._statisticsReporter = null;\r\n }\r\n }\r\n\r\n load() {\r\n if (!this._mediaElement) {\r\n throw new IllegalStateException('HTMLMediaElement must be attached before load()!');\r\n }\r\n this._mediaElement.src = this._mediaDataSource.url;\r\n\r\n if (this._mediaElement.readyState > 0) {\r\n this._mediaElement.currentTime = 0;\r\n }\r\n\r\n this._mediaElement.preload = 'auto';\r\n this._mediaElement.load();\r\n this._statisticsReporter = window.setInterval(\r\n this._reportStatisticsInfo.bind(this),\r\n this._config.statisticsInfoReportInterval);\r\n }\r\n\r\n unload() {\r\n if (this._mediaElement) {\r\n this._mediaElement.src = '';\r\n this._mediaElement.removeAttribute('src');\r\n }\r\n if (this._statisticsReporter != null) {\r\n window.clearInterval(this._statisticsReporter);\r\n this._statisticsReporter = null;\r\n }\r\n }\r\n\r\n play() {\r\n return this._mediaElement.play();\r\n }\r\n\r\n pause() {\r\n this._mediaElement.pause();\r\n }\r\n\r\n get type() {\r\n return this._type;\r\n }\r\n\r\n get buffered() {\r\n return this._mediaElement.buffered;\r\n }\r\n\r\n get duration() {\r\n return this._mediaElement.duration;\r\n }\r\n\r\n get volume() {\r\n return this._mediaElement.volume;\r\n }\r\n\r\n set volume(value) {\r\n this._mediaElement.volume = value;\r\n }\r\n\r\n get muted() {\r\n return this._mediaElement.muted;\r\n }\r\n\r\n set muted(muted) {\r\n this._mediaElement.muted = muted;\r\n }\r\n\r\n get currentTime() {\r\n if (this._mediaElement) {\r\n return this._mediaElement.currentTime;\r\n }\r\n return 0;\r\n }\r\n\r\n set currentTime(seconds) {\r\n if (this._mediaElement) {\r\n this._mediaElement.currentTime = seconds;\r\n } else {\r\n this._pendingSeekTime = seconds;\r\n }\r\n }\r\n\r\n get mediaInfo() {\r\n let mediaPrefix = (this._mediaElement instanceof HTMLAudioElement) ? 'audio/' : 'video/';\r\n let info = {\r\n mimeType: mediaPrefix + this._mediaDataSource.type\r\n };\r\n if (this._mediaElement) {\r\n info.duration = Math.floor(this._mediaElement.duration * 1000);\r\n if (this._mediaElement instanceof HTMLVideoElement) {\r\n info.width = this._mediaElement.videoWidth;\r\n info.height = this._mediaElement.videoHeight;\r\n }\r\n }\r\n return info;\r\n }\r\n\r\n get statisticsInfo() {\r\n let info = {\r\n playerType: this._type,\r\n url: this._mediaDataSource.url\r\n };\r\n\r\n if (!(this._mediaElement instanceof HTMLVideoElement)) {\r\n return info;\r\n }\r\n\r\n let hasQualityInfo = true;\r\n let decoded = 0;\r\n let dropped = 0;\r\n\r\n if (this._mediaElement.getVideoPlaybackQuality) {\r\n let quality = this._mediaElement.getVideoPlaybackQuality();\r\n decoded = quality.totalVideoFrames;\r\n dropped = quality.droppedVideoFrames;\r\n } else if (this._mediaElement.webkitDecodedFrameCount != undefined) {\r\n decoded = this._mediaElement.webkitDecodedFrameCount;\r\n dropped = this._mediaElement.webkitDroppedFrameCount;\r\n } else {\r\n hasQualityInfo = false;\r\n }\r\n\r\n if (hasQualityInfo) {\r\n info.decodedFrames = decoded;\r\n info.droppedFrames = dropped;\r\n }\r\n \r\n return info;\r\n }\r\n\r\n _onvLoadedMetadata(e) {\r\n if (this._pendingSeekTime != null) {\r\n this._mediaElement.currentTime = this._pendingSeekTime;\r\n this._pendingSeekTime = null;\r\n }\r\n this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);\r\n }\r\n\r\n _reportStatisticsInfo() {\r\n this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);\r\n }\r\n\r\n}\r\n\r\nexport default NativePlayer;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Polyfill from './utils/polyfill.js';\r\nimport Features from './core/features.js';\r\nimport {BaseLoader, LoaderStatus, LoaderErrors} from './io/loader.js';\r\nimport FlvPlayer from './player/flv-player.js';\r\nimport NativePlayer from './player/native-player.js';\r\nimport PlayerEvents from './player/player-events.js';\r\nimport {ErrorTypes, ErrorDetails} from './player/player-errors.js';\r\nimport LoggingControl from './utils/logging-control.js';\r\nimport {InvalidArgumentException} from './utils/exception.js';\r\n\r\n// here are all the interfaces\r\n\r\n// install polyfills\r\nPolyfill.install();\r\n\r\n\r\n// factory method\r\nfunction createPlayer(mediaDataSource, optionalConfig) {\r\n let mds = mediaDataSource;\r\n if (mds == null || typeof mds !== 'object') {\r\n throw new InvalidArgumentException('MediaDataSource must be an javascript object!');\r\n }\r\n\r\n if (!mds.hasOwnProperty('type')) {\r\n throw new InvalidArgumentException('MediaDataSource must has type field to indicate video file type!');\r\n }\r\n\r\n switch (mds.type) {\r\n case 'flv':\r\n return new FlvPlayer(mds, optionalConfig);\r\n default:\r\n return new NativePlayer(mds, optionalConfig);\r\n }\r\n}\r\n\r\n\r\n// feature detection\r\nfunction isSupported() {\r\n return Features.supportMSEH264Playback();\r\n}\r\n\r\nfunction getFeatureList() {\r\n return Features.getFeatureList();\r\n}\r\n\r\n\r\n// interfaces\r\nlet flvjs = {};\r\n\r\nflvjs.createPlayer = createPlayer;\r\nflvjs.isSupported = isSupported;\r\nflvjs.getFeatureList = getFeatureList;\r\n\r\nflvjs.BaseLoader = BaseLoader;\r\nflvjs.LoaderStatus = LoaderStatus;\r\nflvjs.LoaderErrors = LoaderErrors;\r\n\r\nflvjs.Events = PlayerEvents;\r\nflvjs.ErrorTypes = ErrorTypes;\r\nflvjs.ErrorDetails = ErrorDetails;\r\n\r\nflvjs.FlvPlayer = FlvPlayer;\r\nflvjs.NativePlayer = NativePlayer;\r\nflvjs.LoggingControl = LoggingControl;\r\n\r\nObject.defineProperty(flvjs, 'version', {\r\n enumerable: true,\r\n get: function () {\r\n // replace by webpack.DefinePlugin\r\n return __VERSION__;\r\n }\r\n});\r\n\r\nexport default flvjs;","// entry/index file\r\n\r\n// make it compatible with browserify's umd wrapper\r\nmodule.exports = require('./flv.js').default;\r\n","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n// Utility class to calculate realtime network I/O speed\r\nclass SpeedSampler {\r\n\r\n constructor() {\r\n // milliseconds\r\n this._firstCheckpoint = 0;\r\n this._lastCheckpoint = 0;\r\n this._intervalBytes = 0;\r\n this._totalBytes = 0;\r\n this._lastSecondBytes = 0;\r\n\r\n // compatibility detection\r\n if (self.performance && self.performance.now) {\r\n this._now = self.performance.now.bind(self.performance);\r\n } else {\r\n this._now = Date.now;\r\n }\r\n }\r\n\r\n reset() {\r\n this._firstCheckpoint = this._lastCheckpoint = 0;\r\n this._totalBytes = this._intervalBytes = 0;\r\n this._lastSecondBytes = 0;\r\n }\r\n\r\n addBytes(bytes) {\r\n if (this._firstCheckpoint === 0) {\r\n this._firstCheckpoint = this._now();\r\n this._lastCheckpoint = this._firstCheckpoint;\r\n this._intervalBytes += bytes;\r\n this._totalBytes += bytes;\r\n } else if (this._now() - this._lastCheckpoint < 1000) {\r\n this._intervalBytes += bytes;\r\n this._totalBytes += bytes;\r\n } else { // duration >= 1000\r\n this._lastSecondBytes = this._intervalBytes;\r\n this._intervalBytes = bytes;\r\n this._totalBytes += bytes;\r\n this._lastCheckpoint = this._now();\r\n }\r\n }\r\n\r\n get currentKBps() {\r\n this.addBytes(0);\r\n\r\n let durationSeconds = (this._now() - this._lastCheckpoint) / 1000;\r\n if (durationSeconds == 0) durationSeconds = 1;\r\n return (this._intervalBytes / durationSeconds) / 1024;\r\n }\r\n\r\n get lastSecondKBps() {\r\n this.addBytes(0);\r\n\r\n if (this._lastSecondBytes !== 0) {\r\n return this._lastSecondBytes / 1024;\r\n } else { // lastSecondBytes === 0\r\n if (this._now() - this._lastCheckpoint >= 500) {\r\n // if time interval since last checkpoint has exceeded 500ms\r\n // the speed is nearly accurate\r\n return this.currentKBps;\r\n } else {\r\n // We don't know\r\n return 0;\r\n }\r\n }\r\n }\r\n\r\n get averageKBps() {\r\n let durationSeconds = (this._now() - this._firstCheckpoint) / 1000;\r\n return (this._totalBytes / durationSeconds) / 1024;\r\n }\r\n\r\n}\r\n\r\nexport default SpeedSampler;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport Browser from '../utils/browser.js';\r\nimport {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';\r\nimport {RuntimeException} from '../utils/exception.js';\r\n\r\n/* fetch + stream IO loader. Currently working on chrome 43+.\r\n * fetch provides a better alternative http API to XMLHttpRequest\r\n *\r\n * fetch spec https://fetch.spec.whatwg.org/\r\n * stream spec https://streams.spec.whatwg.org/\r\n */\r\nclass FetchStreamLoader extends BaseLoader {\r\n\r\n static isSupported() {\r\n try {\r\n // fetch + stream is broken on Microsoft Edge. Disable before build 15048.\r\n // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/\r\n // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist.\r\n let isWorkWellEdge = Browser.msedge && Browser.version.minor >= 15048;\r\n let browserNotBlacklisted = Browser.msedge ? isWorkWellEdge : true;\r\n return (self.fetch && self.ReadableStream && browserNotBlacklisted);\r\n } catch (e) {\r\n return false;\r\n }\r\n }\r\n\r\n constructor(seekHandler, config) {\r\n super('fetch-stream-loader');\r\n this.TAG = 'FetchStreamLoader';\r\n\r\n this._seekHandler = seekHandler;\r\n this._config = config;\r\n this._needStash = true;\r\n\r\n this._requestAbort = false;\r\n this._contentLength = null;\r\n this._receivedLength = 0;\r\n }\r\n\r\n destroy() {\r\n if (this.isWorking()) {\r\n this.abort();\r\n }\r\n super.destroy();\r\n }\r\n\r\n open(dataSource, range) {\r\n this._dataSource = dataSource;\r\n this._range = range;\r\n\r\n let sourceURL = dataSource.url;\r\n if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {\r\n sourceURL = dataSource.redirectedURL;\r\n }\r\n\r\n let seekConfig = this._seekHandler.getConfig(sourceURL, range);\r\n\r\n let headers = new self.Headers();\r\n\r\n if (typeof seekConfig.headers === 'object') {\r\n let configHeaders = seekConfig.headers;\r\n for (let key in configHeaders) {\r\n if (configHeaders.hasOwnProperty(key)) {\r\n headers.append(key, configHeaders[key]);\r\n }\r\n }\r\n }\r\n\r\n let params = {\r\n method: 'GET',\r\n headers: headers,\r\n mode: 'cors',\r\n cache: 'default',\r\n // The default policy of Fetch API in the whatwg standard\r\n // Safari incorrectly indicates 'no-referrer' as default policy, fuck it\r\n referrerPolicy: 'no-referrer-when-downgrade'\r\n };\r\n\r\n // add additional headers\r\n if (typeof this._config.headers === 'object') {\r\n for (let key in this._config.headers) {\r\n headers.append(key, this._config.headers[key]);\r\n }\r\n }\r\n\r\n // cors is enabled by default\r\n if (dataSource.cors === false) {\r\n // no-cors means 'disregard cors policy', which can only be used in ServiceWorker\r\n params.mode = 'same-origin';\r\n }\r\n\r\n // withCredentials is disabled by default\r\n if (dataSource.withCredentials) {\r\n params.credentials = 'include';\r\n }\r\n\r\n // referrerPolicy from config\r\n if (dataSource.referrerPolicy) {\r\n params.referrerPolicy = dataSource.referrerPolicy;\r\n }\r\n\r\n // add abort controller, by wmlgl 2019-5-10 12:21:27\r\n if (self.AbortController) {\r\n this._abortController = new self.AbortController();\r\n params.signal = this._abortController.signal; \r\n }\r\n\r\n this._status = LoaderStatus.kConnecting;\r\n self.fetch(seekConfig.url, params).then((res) => {\r\n if (this._requestAbort) {\r\n this._status = LoaderStatus.kIdle;\r\n res.body.cancel();\r\n return;\r\n }\r\n if (res.ok && (res.status >= 200 && res.status <= 299)) {\r\n if (res.url !== seekConfig.url) {\r\n if (this._onURLRedirect) {\r\n let redirectedURL = this._seekHandler.removeURLParameters(res.url);\r\n this._onURLRedirect(redirectedURL);\r\n }\r\n }\r\n\r\n let lengthHeader = res.headers.get('Content-Length');\r\n if (lengthHeader != null) {\r\n this._contentLength = parseInt(lengthHeader);\r\n if (this._contentLength !== 0) {\r\n if (this._onContentLengthKnown) {\r\n this._onContentLengthKnown(this._contentLength);\r\n }\r\n }\r\n }\r\n\r\n return this._pump.call(this, res.body.getReader());\r\n } else {\r\n this._status = LoaderStatus.kError;\r\n if (this._onError) {\r\n this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: res.status, msg: res.statusText});\r\n } else {\r\n throw new RuntimeException('FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText);\r\n }\r\n }\r\n }).catch((e) => {\r\n if (this._abortController && this._abortController.signal.aborted) {\r\n return;\r\n }\r\n\r\n this._status = LoaderStatus.kError;\r\n if (this._onError) {\r\n this._onError(LoaderErrors.EXCEPTION, {code: -1, msg: e.message});\r\n } else {\r\n throw e;\r\n }\r\n });\r\n }\r\n\r\n abort() {\r\n this._requestAbort = true;\r\n\r\n if (this._status !== LoaderStatus.kBuffering || !Browser.chrome) {\r\n // Chrome may throw Exception-like things here, avoid using if is buffering\r\n if (this._abortController) {\r\n try {\r\n this._abortController.abort();\r\n } catch (e) {}\r\n }\r\n }\r\n }\r\n\r\n _pump(reader) { // ReadableStreamReader\r\n return reader.read().then((result) => {\r\n if (result.done) {\r\n // First check received length\r\n if (this._contentLength !== null && this._receivedLength < this._contentLength) {\r\n // Report Early-EOF\r\n this._status = LoaderStatus.kError;\r\n let type = LoaderErrors.EARLY_EOF;\r\n let info = {code: -1, msg: 'Fetch stream meet Early-EOF'};\r\n if (this._onError) {\r\n this._onError(type, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n } else {\r\n // OK. Download complete\r\n this._status = LoaderStatus.kComplete;\r\n if (this._onComplete) {\r\n this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);\r\n }\r\n }\r\n } else {\r\n if (this._abortController && this._abortController.signal.aborted) {\r\n this._status = LoaderStatus.kComplete;\r\n return;\r\n } else if (this._requestAbort === true) {\r\n this._status = LoaderStatus.kComplete;\r\n return reader.cancel();\r\n }\r\n\r\n this._status = LoaderStatus.kBuffering;\r\n\r\n let chunk = result.value.buffer;\r\n let byteStart = this._range.from + this._receivedLength;\r\n this._receivedLength += chunk.byteLength;\r\n\r\n if (this._onDataArrival) {\r\n this._onDataArrival(chunk, byteStart, this._receivedLength);\r\n }\r\n\r\n this._pump(reader);\r\n }\r\n }).catch((e) => {\r\n if (this._abortController && this._abortController.signal.aborted) {\r\n this._status = LoaderStatus.kComplete;\r\n return;\r\n }\r\n\r\n if (e.code === 11 && Browser.msedge) { // InvalidStateError on Microsoft Edge\r\n // Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call\r\n // Ignore the unknown exception.\r\n // Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/\r\n return;\r\n }\r\n\r\n this._status = LoaderStatus.kError;\r\n let type = 0;\r\n let info = null;\r\n\r\n if ((e.code === 19 || e.message === 'network error') && // NETWORK_ERR\r\n (this._contentLength === null ||\r\n (this._contentLength !== null && this._receivedLength < this._contentLength))) {\r\n type = LoaderErrors.EARLY_EOF;\r\n info = {code: e.code, msg: 'Fetch stream meet Early-EOF'};\r\n } else {\r\n type = LoaderErrors.EXCEPTION;\r\n info = {code: e.code, msg: e.message};\r\n }\r\n\r\n if (this._onError) {\r\n this._onError(type, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n });\r\n }\r\n\r\n}\r\n\r\nexport default FetchStreamLoader;\r\n","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';\r\nimport {RuntimeException} from '../utils/exception.js';\r\n\r\n// For FireFox browser which supports `xhr.responseType = 'moz-chunked-arraybuffer'`\r\nclass MozChunkedLoader extends BaseLoader {\r\n\r\n static isSupported() {\r\n try {\r\n let xhr = new XMLHttpRequest();\r\n // Firefox 37- requires .open() to be called before setting responseType\r\n xhr.open('GET', 'https://example.com', true);\r\n xhr.responseType = 'moz-chunked-arraybuffer';\r\n return (xhr.responseType === 'moz-chunked-arraybuffer');\r\n } catch (e) {\r\n Log.w('MozChunkedLoader', e.message);\r\n return false;\r\n }\r\n }\r\n\r\n constructor(seekHandler, config) {\r\n super('xhr-moz-chunked-loader');\r\n this.TAG = 'MozChunkedLoader';\r\n\r\n this._seekHandler = seekHandler;\r\n this._config = config;\r\n this._needStash = true;\r\n\r\n this._xhr = null;\r\n this._requestAbort = false;\r\n this._contentLength = null;\r\n this._receivedLength = 0;\r\n }\r\n\r\n destroy() {\r\n if (this.isWorking()) {\r\n this.abort();\r\n }\r\n if (this._xhr) {\r\n this._xhr.onreadystatechange = null;\r\n this._xhr.onprogress = null;\r\n this._xhr.onloadend = null;\r\n this._xhr.onerror = null;\r\n this._xhr = null;\r\n }\r\n super.destroy();\r\n }\r\n\r\n open(dataSource, range) {\r\n this._dataSource = dataSource;\r\n this._range = range;\r\n\r\n let sourceURL = dataSource.url;\r\n if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {\r\n sourceURL = dataSource.redirectedURL;\r\n }\r\n\r\n let seekConfig = this._seekHandler.getConfig(sourceURL, range);\r\n this._requestURL = seekConfig.url;\r\n\r\n let xhr = this._xhr = new XMLHttpRequest();\r\n xhr.open('GET', seekConfig.url, true);\r\n xhr.responseType = 'moz-chunked-arraybuffer';\r\n xhr.onreadystatechange = this._onReadyStateChange.bind(this);\r\n xhr.onprogress = this._onProgress.bind(this);\r\n xhr.onloadend = this._onLoadEnd.bind(this);\r\n xhr.onerror = this._onXhrError.bind(this);\r\n\r\n // cors is auto detected and enabled by xhr\r\n\r\n // withCredentials is disabled by default\r\n if (dataSource.withCredentials) {\r\n xhr.withCredentials = true;\r\n }\r\n\r\n if (typeof seekConfig.headers === 'object') {\r\n let headers = seekConfig.headers;\r\n\r\n for (let key in headers) {\r\n if (headers.hasOwnProperty(key)) {\r\n xhr.setRequestHeader(key, headers[key]);\r\n }\r\n }\r\n }\r\n\r\n // add additional headers\r\n if (typeof this._config.headers === 'object') {\r\n let headers = this._config.headers;\r\n\r\n for (let key in headers) {\r\n if (headers.hasOwnProperty(key)) {\r\n xhr.setRequestHeader(key, headers[key]);\r\n }\r\n }\r\n }\r\n\r\n this._status = LoaderStatus.kConnecting;\r\n xhr.send();\r\n }\r\n\r\n abort() {\r\n this._requestAbort = true;\r\n if (this._xhr) {\r\n this._xhr.abort();\r\n }\r\n this._status = LoaderStatus.kComplete;\r\n }\r\n\r\n _onReadyStateChange(e) {\r\n let xhr = e.target;\r\n\r\n if (xhr.readyState === 2) { // HEADERS_RECEIVED\r\n if (xhr.responseURL != undefined && xhr.responseURL !== this._requestURL) {\r\n if (this._onURLRedirect) {\r\n let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);\r\n this._onURLRedirect(redirectedURL);\r\n }\r\n }\r\n\r\n if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) {\r\n this._status = LoaderStatus.kError;\r\n if (this._onError) {\r\n this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: xhr.status, msg: xhr.statusText});\r\n } else {\r\n throw new RuntimeException('MozChunkedLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);\r\n }\r\n } else {\r\n this._status = LoaderStatus.kBuffering;\r\n }\r\n }\r\n }\r\n\r\n _onProgress(e) {\r\n if (this._status === LoaderStatus.kError) {\r\n // Ignore error response\r\n return;\r\n }\r\n\r\n if (this._contentLength === null) {\r\n if (e.total !== null && e.total !== 0) {\r\n this._contentLength = e.total;\r\n if (this._onContentLengthKnown) {\r\n this._onContentLengthKnown(this._contentLength);\r\n }\r\n }\r\n }\r\n\r\n let chunk = e.target.response;\r\n let byteStart = this._range.from + this._receivedLength;\r\n this._receivedLength += chunk.byteLength;\r\n\r\n if (this._onDataArrival) {\r\n this._onDataArrival(chunk, byteStart, this._receivedLength);\r\n }\r\n }\r\n\r\n _onLoadEnd(e) {\r\n if (this._requestAbort === true) {\r\n this._requestAbort = false;\r\n return;\r\n } else if (this._status === LoaderStatus.kError) {\r\n return;\r\n }\r\n\r\n this._status = LoaderStatus.kComplete;\r\n if (this._onComplete) {\r\n this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);\r\n }\r\n }\r\n\r\n _onXhrError(e) {\r\n this._status = LoaderStatus.kError;\r\n let type = 0;\r\n let info = null;\r\n\r\n if (this._contentLength && e.loaded < this._contentLength) {\r\n type = LoaderErrors.EARLY_EOF;\r\n info = {code: -1, msg: 'Moz-Chunked stream meet Early-Eof'};\r\n } else {\r\n type = LoaderErrors.EXCEPTION;\r\n info = {code: -1, msg: e.constructor.name + ' ' + e.type};\r\n }\r\n\r\n if (this._onError) {\r\n this._onError(type, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default MozChunkedLoader;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport SpeedSampler from './speed-sampler.js';\r\nimport {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';\r\nimport {RuntimeException} from '../utils/exception.js';\r\n\r\n// Universal IO Loader, implemented by adding Range header in xhr's request header\r\nclass RangeLoader extends BaseLoader {\r\n\r\n static isSupported() {\r\n try {\r\n let xhr = new XMLHttpRequest();\r\n xhr.open('GET', 'https://example.com', true);\r\n xhr.responseType = 'arraybuffer';\r\n return (xhr.responseType === 'arraybuffer');\r\n } catch (e) {\r\n Log.w('RangeLoader', e.message);\r\n return false;\r\n }\r\n }\r\n\r\n constructor(seekHandler, config) {\r\n super('xhr-range-loader');\r\n this.TAG = 'RangeLoader';\r\n\r\n this._seekHandler = seekHandler;\r\n this._config = config;\r\n this._needStash = false;\r\n\r\n this._chunkSizeKBList = [\r\n 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192\r\n ];\r\n this._currentChunkSizeKB = 384;\r\n this._currentSpeedNormalized = 0;\r\n this._zeroSpeedChunkCount = 0;\r\n\r\n this._xhr = null;\r\n this._speedSampler = new SpeedSampler();\r\n\r\n this._requestAbort = false;\r\n this._waitForTotalLength = false;\r\n this._totalLengthReceived = false;\r\n\r\n this._currentRequestURL = null;\r\n this._currentRedirectedURL = null;\r\n this._currentRequestRange = null;\r\n this._totalLength = null; // size of the entire file\r\n this._contentLength = null; // Content-Length of entire request range\r\n this._receivedLength = 0; // total received bytes\r\n this._lastTimeLoaded = 0; // received bytes of current request sub-range\r\n }\r\n\r\n destroy() {\r\n if (this.isWorking()) {\r\n this.abort();\r\n }\r\n if (this._xhr) {\r\n this._xhr.onreadystatechange = null;\r\n this._xhr.onprogress = null;\r\n this._xhr.onload = null;\r\n this._xhr.onerror = null;\r\n this._xhr = null;\r\n }\r\n super.destroy();\r\n }\r\n\r\n get currentSpeed() {\r\n return this._speedSampler.lastSecondKBps;\r\n }\r\n\r\n open(dataSource, range) {\r\n this._dataSource = dataSource;\r\n this._range = range;\r\n this._status = LoaderStatus.kConnecting;\r\n\r\n let useRefTotalLength = false;\r\n if (this._dataSource.filesize != undefined && this._dataSource.filesize !== 0) {\r\n useRefTotalLength = true;\r\n this._totalLength = this._dataSource.filesize;\r\n }\r\n\r\n if (!this._totalLengthReceived && !useRefTotalLength) {\r\n // We need total filesize\r\n this._waitForTotalLength = true;\r\n this._internalOpen(this._dataSource, {from: 0, to: -1});\r\n } else {\r\n // We have filesize, start loading\r\n this._openSubRange();\r\n }\r\n }\r\n\r\n _openSubRange() {\r\n let chunkSize = this._currentChunkSizeKB * 1024;\r\n\r\n let from = this._range.from + this._receivedLength;\r\n let to = from + chunkSize;\r\n\r\n if (this._contentLength != null) {\r\n if (to - this._range.from >= this._contentLength) {\r\n to = this._range.from + this._contentLength - 1;\r\n }\r\n }\r\n\r\n this._currentRequestRange = {from, to};\r\n this._internalOpen(this._dataSource, this._currentRequestRange);\r\n }\r\n\r\n _internalOpen(dataSource, range) {\r\n this._lastTimeLoaded = 0;\r\n\r\n let sourceURL = dataSource.url;\r\n if (this._config.reuseRedirectedURL) {\r\n if (this._currentRedirectedURL != undefined) {\r\n sourceURL = this._currentRedirectedURL;\r\n } else if (dataSource.redirectedURL != undefined) {\r\n sourceURL = dataSource.redirectedURL;\r\n }\r\n }\r\n\r\n let seekConfig = this._seekHandler.getConfig(sourceURL, range);\r\n this._currentRequestURL = seekConfig.url;\r\n\r\n let xhr = this._xhr = new XMLHttpRequest();\r\n xhr.open('GET', seekConfig.url, true);\r\n xhr.responseType = 'arraybuffer';\r\n xhr.onreadystatechange = this._onReadyStateChange.bind(this);\r\n xhr.onprogress = this._onProgress.bind(this);\r\n xhr.onload = this._onLoad.bind(this);\r\n xhr.onerror = this._onXhrError.bind(this);\r\n\r\n if (dataSource.withCredentials) {\r\n xhr.withCredentials = true;\r\n }\r\n\r\n if (typeof seekConfig.headers === 'object') {\r\n let headers = seekConfig.headers;\r\n\r\n for (let key in headers) {\r\n if (headers.hasOwnProperty(key)) {\r\n xhr.setRequestHeader(key, headers[key]);\r\n }\r\n }\r\n }\r\n\r\n // add additional headers\r\n if (typeof this._config.headers === 'object') {\r\n let headers = this._config.headers;\r\n\r\n for (let key in headers) {\r\n if (headers.hasOwnProperty(key)) {\r\n xhr.setRequestHeader(key, headers[key]);\r\n }\r\n }\r\n }\r\n\r\n xhr.send();\r\n }\r\n\r\n abort() {\r\n this._requestAbort = true;\r\n this._internalAbort();\r\n this._status = LoaderStatus.kComplete;\r\n }\r\n\r\n _internalAbort() {\r\n if (this._xhr) {\r\n this._xhr.onreadystatechange = null;\r\n this._xhr.onprogress = null;\r\n this._xhr.onload = null;\r\n this._xhr.onerror = null;\r\n this._xhr.abort();\r\n this._xhr = null;\r\n }\r\n }\r\n\r\n _onReadyStateChange(e) {\r\n let xhr = e.target;\r\n\r\n if (xhr.readyState === 2) { // HEADERS_RECEIVED\r\n if (xhr.responseURL != undefined) { // if the browser support this property\r\n let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);\r\n if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {\r\n this._currentRedirectedURL = redirectedURL;\r\n if (this._onURLRedirect) {\r\n this._onURLRedirect(redirectedURL);\r\n }\r\n }\r\n }\r\n\r\n if ((xhr.status >= 200 && xhr.status <= 299)) {\r\n if (this._waitForTotalLength) {\r\n return;\r\n }\r\n this._status = LoaderStatus.kBuffering;\r\n } else {\r\n this._status = LoaderStatus.kError;\r\n if (this._onError) {\r\n this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: xhr.status, msg: xhr.statusText});\r\n } else {\r\n throw new RuntimeException('RangeLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);\r\n }\r\n }\r\n }\r\n }\r\n\r\n _onProgress(e) {\r\n if (this._status === LoaderStatus.kError) {\r\n // Ignore error response\r\n return;\r\n }\r\n\r\n if (this._contentLength === null) {\r\n let openNextRange = false;\r\n\r\n if (this._waitForTotalLength) {\r\n this._waitForTotalLength = false;\r\n this._totalLengthReceived = true;\r\n openNextRange = true;\r\n\r\n let total = e.total;\r\n this._internalAbort();\r\n if (total != null & total !== 0) {\r\n this._totalLength = total;\r\n }\r\n }\r\n\r\n // calculate currrent request range's contentLength\r\n if (this._range.to === -1) {\r\n this._contentLength = this._totalLength - this._range.from;\r\n } else { // to !== -1\r\n this._contentLength = this._range.to - this._range.from + 1;\r\n }\r\n\r\n if (openNextRange) {\r\n this._openSubRange();\r\n return;\r\n }\r\n if (this._onContentLengthKnown) {\r\n this._onContentLengthKnown(this._contentLength);\r\n }\r\n }\r\n\r\n let delta = e.loaded - this._lastTimeLoaded;\r\n this._lastTimeLoaded = e.loaded;\r\n this._speedSampler.addBytes(delta);\r\n }\r\n\r\n _normalizeSpeed(input) {\r\n let list = this._chunkSizeKBList;\r\n let last = list.length - 1;\r\n let mid = 0;\r\n let lbound = 0;\r\n let ubound = last;\r\n\r\n if (input < list[0]) {\r\n return list[0];\r\n }\r\n\r\n while (lbound <= ubound) {\r\n mid = lbound + Math.floor((ubound - lbound) / 2);\r\n if (mid === last || (input >= list[mid] && input < list[mid + 1])) {\r\n return list[mid];\r\n } else if (list[mid] < input) {\r\n lbound = mid + 1;\r\n } else {\r\n ubound = mid - 1;\r\n }\r\n }\r\n }\r\n\r\n _onLoad(e) {\r\n if (this._status === LoaderStatus.kError) {\r\n // Ignore error response\r\n return;\r\n }\r\n\r\n if (this._waitForTotalLength) {\r\n this._waitForTotalLength = false;\r\n return;\r\n }\r\n\r\n this._lastTimeLoaded = 0;\r\n let KBps = this._speedSampler.lastSecondKBps;\r\n if (KBps === 0) {\r\n this._zeroSpeedChunkCount++;\r\n if (this._zeroSpeedChunkCount >= 3) {\r\n // Try get currentKBps after 3 chunks\r\n KBps = this._speedSampler.currentKBps;\r\n }\r\n }\r\n\r\n if (KBps !== 0) {\r\n let normalized = this._normalizeSpeed(KBps);\r\n if (this._currentSpeedNormalized !== normalized) {\r\n this._currentSpeedNormalized = normalized;\r\n this._currentChunkSizeKB = normalized;\r\n }\r\n }\r\n\r\n let chunk = e.target.response;\r\n let byteStart = this._range.from + this._receivedLength;\r\n this._receivedLength += chunk.byteLength;\r\n\r\n let reportComplete = false;\r\n\r\n if (this._contentLength != null && this._receivedLength < this._contentLength) {\r\n // continue load next chunk\r\n this._openSubRange();\r\n } else {\r\n reportComplete = true;\r\n }\r\n\r\n // dispatch received chunk\r\n if (this._onDataArrival) {\r\n this._onDataArrival(chunk, byteStart, this._receivedLength);\r\n }\r\n\r\n if (reportComplete) {\r\n this._status = LoaderStatus.kComplete;\r\n if (this._onComplete) {\r\n this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);\r\n }\r\n }\r\n }\r\n\r\n _onXhrError(e) {\r\n this._status = LoaderStatus.kError;\r\n let type = 0;\r\n let info = null;\r\n\r\n if (this._contentLength && this._receivedLength > 0\r\n && this._receivedLength < this._contentLength) {\r\n type = LoaderErrors.EARLY_EOF;\r\n info = {code: -1, msg: 'RangeLoader meet Early-Eof'};\r\n } else {\r\n type = LoaderErrors.EXCEPTION;\r\n info = {code: -1, msg: e.constructor.name + ' ' + e.type};\r\n }\r\n\r\n if (this._onError) {\r\n this._onError(type, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default RangeLoader;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';\r\nimport {RuntimeException} from '../utils/exception.js';\r\n\r\n// For FLV over WebSocket live stream\r\nclass WebSocketLoader extends BaseLoader {\r\n\r\n static isSupported() {\r\n try {\r\n return (typeof self.WebSocket !== 'undefined');\r\n } catch (e) {\r\n return false;\r\n }\r\n }\r\n\r\n constructor() {\r\n super('websocket-loader');\r\n this.TAG = 'WebSocketLoader';\r\n\r\n this._needStash = true;\r\n\r\n this._ws = null;\r\n this._requestAbort = false;\r\n this._receivedLength = 0;\r\n }\r\n\r\n destroy() {\r\n if (this._ws) {\r\n this.abort();\r\n }\r\n super.destroy();\r\n }\r\n\r\n open(dataSource) {\r\n try {\r\n let ws = this._ws = new self.WebSocket(dataSource.url);\r\n ws.binaryType = 'arraybuffer';\r\n ws.onopen = this._onWebSocketOpen.bind(this);\r\n ws.onclose = this._onWebSocketClose.bind(this);\r\n ws.onmessage = this._onWebSocketMessage.bind(this);\r\n ws.onerror = this._onWebSocketError.bind(this);\r\n\r\n this._status = LoaderStatus.kConnecting;\r\n } catch (e) {\r\n this._status = LoaderStatus.kError;\r\n\r\n let info = {code: e.code, msg: e.message};\r\n\r\n if (this._onError) {\r\n this._onError(LoaderErrors.EXCEPTION, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n }\r\n }\r\n\r\n abort() {\r\n let ws = this._ws;\r\n if (ws && (ws.readyState === 0 || ws.readyState === 1)) { // CONNECTING || OPEN\r\n this._requestAbort = true;\r\n ws.close();\r\n }\r\n\r\n this._ws = null;\r\n this._status = LoaderStatus.kComplete;\r\n }\r\n\r\n _onWebSocketOpen(e) {\r\n this._status = LoaderStatus.kBuffering;\r\n }\r\n\r\n _onWebSocketClose(e) {\r\n if (this._requestAbort === true) {\r\n this._requestAbort = false;\r\n return;\r\n }\r\n\r\n this._status = LoaderStatus.kComplete;\r\n\r\n if (this._onComplete) {\r\n this._onComplete(0, this._receivedLength - 1);\r\n }\r\n }\r\n\r\n _onWebSocketMessage(e) {\r\n if (e.data instanceof ArrayBuffer) {\r\n this._dispatchArrayBuffer(e.data);\r\n } else if (e.data instanceof Blob) {\r\n let reader = new FileReader();\r\n reader.onload = () => {\r\n this._dispatchArrayBuffer(reader.result);\r\n };\r\n reader.readAsArrayBuffer(e.data);\r\n } else {\r\n this._status = LoaderStatus.kError;\r\n let info = {code: -1, msg: 'Unsupported WebSocket message type: ' + e.data.constructor.name};\r\n\r\n if (this._onError) {\r\n this._onError(LoaderErrors.EXCEPTION, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n }\r\n }\r\n\r\n _dispatchArrayBuffer(arraybuffer) {\r\n let chunk = arraybuffer;\r\n let byteStart = this._receivedLength;\r\n this._receivedLength += chunk.byteLength;\r\n\r\n if (this._onDataArrival) {\r\n this._onDataArrival(chunk, byteStart, this._receivedLength);\r\n }\r\n }\r\n\r\n _onWebSocketError(e) {\r\n this._status = LoaderStatus.kError;\r\n\r\n let info = {\r\n code: e.code,\r\n msg: e.message\r\n };\r\n\r\n if (this._onError) {\r\n this._onError(LoaderErrors.EXCEPTION, info);\r\n } else {\r\n throw new RuntimeException(info.msg);\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default WebSocketLoader;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nclass RangeSeekHandler {\r\n\r\n constructor(zeroStart) {\r\n this._zeroStart = zeroStart || false;\r\n }\r\n\r\n getConfig(url, range) {\r\n let headers = {};\r\n\r\n if (range.from !== 0 || range.to !== -1) {\r\n let param;\r\n if (range.to !== -1) {\r\n param = `bytes=${range.from.toString()}-${range.to.toString()}`;\r\n } else {\r\n param = `bytes=${range.from.toString()}-`;\r\n }\r\n headers['Range'] = param;\r\n } else if (this._zeroStart) {\r\n headers['Range'] = 'bytes=0-';\r\n }\r\n\r\n return {\r\n url: url,\r\n headers: headers\r\n };\r\n }\r\n\r\n removeURLParameters(seekedURL) {\r\n return seekedURL;\r\n }\r\n\r\n}\r\n\r\nexport default RangeSeekHandler;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nclass ParamSeekHandler {\r\n\r\n constructor(paramStart, paramEnd) {\r\n this._startName = paramStart;\r\n this._endName = paramEnd;\r\n }\r\n\r\n getConfig(baseUrl, range) {\r\n let url = baseUrl;\r\n\r\n if (range.from !== 0 || range.to !== -1) {\r\n let needAnd = true;\r\n if (url.indexOf('?') === -1) {\r\n url += '?';\r\n needAnd = false;\r\n }\r\n\r\n if (needAnd) {\r\n url += '&';\r\n }\r\n\r\n url += `${this._startName}=${range.from.toString()}`;\r\n\r\n if (range.to !== -1) {\r\n url += `&${this._endName}=${range.to.toString()}`;\r\n }\r\n }\r\n\r\n return {\r\n url: url,\r\n headers: {}\r\n };\r\n }\r\n\r\n removeURLParameters(seekedURL) {\r\n let baseURL = seekedURL.split('?')[0];\r\n let params = undefined;\r\n\r\n let queryIndex = seekedURL.indexOf('?');\r\n if (queryIndex !== -1) {\r\n params = seekedURL.substring(queryIndex + 1);\r\n }\r\n\r\n let resultParams = '';\r\n\r\n if (params != undefined && params.length > 0) {\r\n let pairs = params.split('&');\r\n\r\n for (let i = 0; i < pairs.length; i++) {\r\n let pair = pairs[i].split('=');\r\n let requireAnd = (i > 0);\r\n\r\n if (pair[0] !== this._startName && pair[0] !== this._endName) {\r\n if (requireAnd) {\r\n resultParams += '&';\r\n }\r\n resultParams += pairs[i];\r\n }\r\n }\r\n }\r\n\r\n return (resultParams.length === 0) ? baseURL : baseURL + '?' + resultParams;\r\n }\r\n\r\n}\r\n\r\nexport default ParamSeekHandler;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport Log from '../utils/logger.js';\r\nimport SpeedSampler from './speed-sampler.js';\r\nimport {LoaderStatus, LoaderErrors} from './loader.js';\r\nimport FetchStreamLoader from './fetch-stream-loader.js';\r\nimport MozChunkedLoader from './xhr-moz-chunked-loader.js';\r\nimport MSStreamLoader from './xhr-msstream-loader.js';\r\nimport RangeLoader from './xhr-range-loader.js';\r\nimport WebSocketLoader from './websocket-loader.js';\r\nimport RangeSeekHandler from './range-seek-handler.js';\r\nimport ParamSeekHandler from './param-seek-handler.js';\r\nimport {RuntimeException, IllegalStateException, InvalidArgumentException} from '../utils/exception.js';\r\n\r\n/**\r\n * DataSource: {\r\n * url: string,\r\n * filesize: number,\r\n * cors: boolean,\r\n * withCredentials: boolean\r\n * }\r\n * \r\n */\r\n\r\n// Manage IO Loaders\r\nclass IOController {\r\n\r\n constructor(dataSource, config, extraData) {\r\n this.TAG = 'IOController';\r\n\r\n this._config = config;\r\n this._extraData = extraData;\r\n\r\n this._stashInitialSize = 1024 * 384; // default initial size: 384KB\r\n if (config.stashInitialSize != undefined && config.stashInitialSize > 0) {\r\n // apply from config\r\n this._stashInitialSize = config.stashInitialSize;\r\n }\r\n\r\n this._stashUsed = 0;\r\n this._stashSize = this._stashInitialSize;\r\n this._bufferSize = 1024 * 1024 * 3; // initial size: 3MB\r\n this._stashBuffer = new ArrayBuffer(this._bufferSize);\r\n this._stashByteStart = 0;\r\n this._enableStash = true;\r\n if (config.enableStashBuffer === false) {\r\n this._enableStash = false;\r\n }\r\n\r\n this._loader = null;\r\n this._loaderClass = null;\r\n this._seekHandler = null;\r\n\r\n this._dataSource = dataSource;\r\n this._isWebSocketURL = /wss?:\\/\\/(.+?)/.test(dataSource.url);\r\n this._refTotalLength = dataSource.filesize ? dataSource.filesize : null;\r\n this._totalLength = this._refTotalLength;\r\n this._fullRequestFlag = false;\r\n this._currentRange = null;\r\n this._redirectedURL = null;\r\n\r\n this._speedNormalized = 0;\r\n this._speedSampler = new SpeedSampler();\r\n this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];\r\n\r\n this._isEarlyEofReconnecting = false;\r\n\r\n this._paused = false;\r\n this._resumeFrom = 0;\r\n\r\n this._onDataArrival = null;\r\n this._onSeeked = null;\r\n this._onError = null;\r\n this._onComplete = null;\r\n this._onRedirect = null;\r\n this._onRecoveredEarlyEof = null;\r\n\r\n this._selectSeekHandler();\r\n this._selectLoader();\r\n this._createLoader();\r\n }\r\n\r\n destroy() {\r\n if (this._loader.isWorking()) {\r\n this._loader.abort();\r\n }\r\n this._loader.destroy();\r\n this._loader = null;\r\n this._loaderClass = null;\r\n this._dataSource = null;\r\n this._stashBuffer = null;\r\n this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0;\r\n this._currentRange = null;\r\n this._speedSampler = null;\r\n\r\n this._isEarlyEofReconnecting = false;\r\n\r\n this._onDataArrival = null;\r\n this._onSeeked = null;\r\n this._onError = null;\r\n this._onComplete = null;\r\n this._onRedirect = null;\r\n this._onRecoveredEarlyEof = null;\r\n\r\n this._extraData = null;\r\n }\r\n\r\n isWorking() {\r\n return this._loader && this._loader.isWorking() && !this._paused;\r\n }\r\n\r\n isPaused() {\r\n return this._paused;\r\n }\r\n\r\n get status() {\r\n return this._loader.status;\r\n }\r\n\r\n get extraData() {\r\n return this._extraData;\r\n }\r\n\r\n set extraData(data) {\r\n this._extraData = data;\r\n }\r\n\r\n // prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number\r\n get onDataArrival() {\r\n return this._onDataArrival;\r\n }\r\n\r\n set onDataArrival(callback) {\r\n this._onDataArrival = callback;\r\n }\r\n\r\n get onSeeked() {\r\n return this._onSeeked;\r\n }\r\n\r\n set onSeeked(callback) {\r\n this._onSeeked = callback;\r\n }\r\n\r\n // prototype: function onError(type: number, info: {code: number, msg: string}): void\r\n get onError() {\r\n return this._onError;\r\n }\r\n\r\n set onError(callback) {\r\n this._onError = callback;\r\n }\r\n\r\n get onComplete() {\r\n return this._onComplete;\r\n }\r\n\r\n set onComplete(callback) {\r\n this._onComplete = callback;\r\n }\r\n\r\n get onRedirect() {\r\n return this._onRedirect;\r\n }\r\n\r\n set onRedirect(callback) {\r\n this._onRedirect = callback;\r\n }\r\n\r\n get onRecoveredEarlyEof() {\r\n return this._onRecoveredEarlyEof;\r\n }\r\n\r\n set onRecoveredEarlyEof(callback) {\r\n this._onRecoveredEarlyEof = callback;\r\n }\r\n\r\n get currentURL() {\r\n return this._dataSource.url;\r\n }\r\n\r\n get hasRedirect() {\r\n return (this._redirectedURL != null || this._dataSource.redirectedURL != undefined);\r\n }\r\n\r\n get currentRedirectedURL() {\r\n return this._redirectedURL || this._dataSource.redirectedURL;\r\n }\r\n\r\n // in KB/s\r\n get currentSpeed() {\r\n if (this._loaderClass === RangeLoader) {\r\n // SpeedSampler is inaccuracy if loader is RangeLoader\r\n return this._loader.currentSpeed;\r\n }\r\n return this._speedSampler.lastSecondKBps;\r\n }\r\n\r\n get loaderType() {\r\n return this._loader.type;\r\n }\r\n\r\n _selectSeekHandler() {\r\n let config = this._config;\r\n\r\n if (config.seekType === 'range') {\r\n this._seekHandler = new RangeSeekHandler(this._config.rangeLoadZeroStart);\r\n } else if (config.seekType === 'param') {\r\n let paramStart = config.seekParamStart || 'bstart';\r\n let paramEnd = config.seekParamEnd || 'bend';\r\n\r\n this._seekHandler = new ParamSeekHandler(paramStart, paramEnd);\r\n } else if (config.seekType === 'custom') {\r\n if (typeof config.customSeekHandler !== 'function') {\r\n throw new InvalidArgumentException('Custom seekType specified in config but invalid customSeekHandler!');\r\n }\r\n this._seekHandler = new config.customSeekHandler();\r\n } else {\r\n throw new InvalidArgumentException(`Invalid seekType in config: ${config.seekType}`);\r\n }\r\n }\r\n\r\n _selectLoader() {\r\n if (this._config.customLoader != null) {\r\n this._loaderClass = this._config.customLoader;\r\n } else if (this._isWebSocketURL) {\r\n this._loaderClass = WebSocketLoader;\r\n } else if (FetchStreamLoader.isSupported()) {\r\n this._loaderClass = FetchStreamLoader;\r\n } else if (MozChunkedLoader.isSupported()) {\r\n this._loaderClass = MozChunkedLoader;\r\n } else if (RangeLoader.isSupported()) {\r\n this._loaderClass = RangeLoader;\r\n } else {\r\n throw new RuntimeException('Your browser doesn\\'t support xhr with arraybuffer responseType!');\r\n }\r\n }\r\n\r\n _createLoader() {\r\n this._loader = new this._loaderClass(this._seekHandler, this._config);\r\n if (this._loader.needStashBuffer === false) {\r\n this._enableStash = false;\r\n }\r\n this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this);\r\n this._loader.onURLRedirect = this._onURLRedirect.bind(this);\r\n this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this);\r\n this._loader.onComplete = this._onLoaderComplete.bind(this);\r\n this._loader.onError = this._onLoaderError.bind(this);\r\n }\r\n\r\n open(optionalFrom) {\r\n this._currentRange = {from: 0, to: -1};\r\n if (optionalFrom) {\r\n this._currentRange.from = optionalFrom;\r\n }\r\n\r\n this._speedSampler.reset();\r\n if (!optionalFrom) {\r\n this._fullRequestFlag = true;\r\n }\r\n\r\n this._loader.open(this._dataSource, Object.assign({}, this._currentRange));\r\n }\r\n\r\n abort() {\r\n this._loader.abort();\r\n\r\n if (this._paused) {\r\n this._paused = false;\r\n this._resumeFrom = 0;\r\n }\r\n }\r\n\r\n pause() {\r\n if (this.isWorking()) {\r\n this._loader.abort();\r\n\r\n if (this._stashUsed !== 0) {\r\n this._resumeFrom = this._stashByteStart;\r\n this._currentRange.to = this._stashByteStart - 1;\r\n } else {\r\n this._resumeFrom = this._currentRange.to + 1;\r\n }\r\n this._stashUsed = 0;\r\n this._stashByteStart = 0;\r\n this._paused = true;\r\n }\r\n }\r\n\r\n resume() {\r\n if (this._paused) {\r\n this._paused = false;\r\n let bytes = this._resumeFrom;\r\n this._resumeFrom = 0;\r\n this._internalSeek(bytes, true);\r\n }\r\n }\r\n\r\n seek(bytes) {\r\n this._paused = false;\r\n this._stashUsed = 0;\r\n this._stashByteStart = 0;\r\n this._internalSeek(bytes, true);\r\n }\r\n\r\n /**\r\n * When seeking request is from media seeking, unconsumed stash data should be dropped\r\n * However, stash data shouldn't be dropped if seeking requested from http reconnection\r\n *\r\n * @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer\r\n */\r\n _internalSeek(bytes, dropUnconsumed) {\r\n if (this._loader.isWorking()) {\r\n this._loader.abort();\r\n }\r\n\r\n // dispatch & flush stash buffer before seek\r\n this._flushStashBuffer(dropUnconsumed);\r\n\r\n this._loader.destroy();\r\n this._loader = null;\r\n\r\n let requestRange = {from: bytes, to: -1};\r\n this._currentRange = {from: requestRange.from, to: -1};\r\n\r\n this._speedSampler.reset();\r\n this._stashSize = this._stashInitialSize;\r\n this._createLoader();\r\n this._loader.open(this._dataSource, requestRange);\r\n\r\n if (this._onSeeked) {\r\n this._onSeeked();\r\n }\r\n }\r\n\r\n updateUrl(url) {\r\n if (!url || typeof url !== 'string' || url.length === 0) {\r\n throw new InvalidArgumentException('Url must be a non-empty string!');\r\n }\r\n\r\n this._dataSource.url = url;\r\n\r\n // TODO: replace with new url\r\n }\r\n\r\n _expandBuffer(expectedBytes) {\r\n let bufferNewSize = this._stashSize;\r\n while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) {\r\n bufferNewSize *= 2;\r\n }\r\n\r\n bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB\r\n if (bufferNewSize === this._bufferSize) {\r\n return;\r\n }\r\n\r\n let newBuffer = new ArrayBuffer(bufferNewSize);\r\n\r\n if (this._stashUsed > 0) { // copy existing data into new buffer\r\n let stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed);\r\n let stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize);\r\n stashNewArray.set(stashOldArray, 0);\r\n }\r\n\r\n this._stashBuffer = newBuffer;\r\n this._bufferSize = bufferNewSize;\r\n }\r\n\r\n _normalizeSpeed(input) {\r\n let list = this._speedNormalizeList;\r\n let last = list.length - 1;\r\n let mid = 0;\r\n let lbound = 0;\r\n let ubound = last;\r\n\r\n if (input < list[0]) {\r\n return list[0];\r\n }\r\n\r\n // binary search\r\n while (lbound <= ubound) {\r\n mid = lbound + Math.floor((ubound - lbound) / 2);\r\n if (mid === last || (input >= list[mid] && input < list[mid + 1])) {\r\n return list[mid];\r\n } else if (list[mid] < input) {\r\n lbound = mid + 1;\r\n } else {\r\n ubound = mid - 1;\r\n }\r\n }\r\n }\r\n\r\n _adjustStashSize(normalized) {\r\n let stashSizeKB = 0;\r\n\r\n if (this._config.isLive) {\r\n // live stream: always use single normalized speed for size of stashSizeKB\r\n stashSizeKB = normalized;\r\n } else {\r\n if (normalized < 512) {\r\n stashSizeKB = normalized;\r\n } else if (normalized >= 512 && normalized <= 1024) {\r\n stashSizeKB = Math.floor(normalized * 1.5);\r\n } else {\r\n stashSizeKB = normalized * 2;\r\n }\r\n }\r\n\r\n if (stashSizeKB > 8192) {\r\n stashSizeKB = 8192;\r\n }\r\n\r\n let bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB\r\n if (this._bufferSize < bufferSize) {\r\n this._expandBuffer(bufferSize);\r\n }\r\n this._stashSize = stashSizeKB * 1024;\r\n }\r\n\r\n _dispatchChunks(chunks, byteStart) {\r\n this._currentRange.to = byteStart + chunks.byteLength - 1;\r\n return this._onDataArrival(chunks, byteStart);\r\n }\r\n\r\n _onURLRedirect(redirectedURL) {\r\n this._redirectedURL = redirectedURL;\r\n if (this._onRedirect) {\r\n this._onRedirect(redirectedURL);\r\n }\r\n }\r\n\r\n _onContentLengthKnown(contentLength) {\r\n if (contentLength && this._fullRequestFlag) {\r\n this._totalLength = contentLength;\r\n this._fullRequestFlag = false;\r\n }\r\n }\r\n\r\n _onLoaderChunkArrival(chunk, byteStart, receivedLength) {\r\n if (!this._onDataArrival) {\r\n throw new IllegalStateException('IOController: No existing consumer (onDataArrival) callback!');\r\n }\r\n if (this._paused) {\r\n return;\r\n }\r\n if (this._isEarlyEofReconnecting) {\r\n // Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback\r\n this._isEarlyEofReconnecting = false;\r\n if (this._onRecoveredEarlyEof) {\r\n this._onRecoveredEarlyEof();\r\n }\r\n }\r\n\r\n this._speedSampler.addBytes(chunk.byteLength);\r\n\r\n // adjust stash buffer size according to network speed dynamically\r\n let KBps = this._speedSampler.lastSecondKBps;\r\n if (KBps !== 0) {\r\n let normalized = this._normalizeSpeed(KBps);\r\n if (this._speedNormalized !== normalized) {\r\n this._speedNormalized = normalized;\r\n this._adjustStashSize(normalized);\r\n }\r\n }\r\n\r\n if (!this._enableStash) { // disable stash\r\n if (this._stashUsed === 0) {\r\n // dispatch chunk directly to consumer;\r\n // check ret value (consumed bytes) and stash unconsumed to stashBuffer\r\n let consumed = this._dispatchChunks(chunk, byteStart);\r\n if (consumed < chunk.byteLength) { // unconsumed data remain.\r\n let remain = chunk.byteLength - consumed;\r\n if (remain > this._bufferSize) {\r\n this._expandBuffer(remain);\r\n }\r\n let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n stashArray.set(new Uint8Array(chunk, consumed), 0);\r\n this._stashUsed += remain;\r\n this._stashByteStart = byteStart + consumed;\r\n }\r\n } else {\r\n // else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer.\r\n if (this._stashUsed + chunk.byteLength > this._bufferSize) {\r\n this._expandBuffer(this._stashUsed + chunk.byteLength);\r\n }\r\n let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n stashArray.set(new Uint8Array(chunk), this._stashUsed);\r\n this._stashUsed += chunk.byteLength;\r\n let consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart);\r\n if (consumed < this._stashUsed && consumed > 0) { // unconsumed data remain\r\n let remainArray = new Uint8Array(this._stashBuffer, consumed);\r\n stashArray.set(remainArray, 0);\r\n }\r\n this._stashUsed -= consumed;\r\n this._stashByteStart += consumed;\r\n }\r\n } else { // enable stash\r\n if (this._stashUsed === 0 && this._stashByteStart === 0) { // seeked? or init chunk?\r\n // This is the first chunk after seek action\r\n this._stashByteStart = byteStart;\r\n }\r\n if (this._stashUsed + chunk.byteLength <= this._stashSize) {\r\n // just stash\r\n let stashArray = new Uint8Array(this._stashBuffer, 0, this._stashSize);\r\n stashArray.set(new Uint8Array(chunk), this._stashUsed);\r\n this._stashUsed += chunk.byteLength;\r\n } else { // stashUsed + chunkSize > stashSize, size limit exceeded\r\n let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n if (this._stashUsed > 0) { // There're stash datas in buffer\r\n // dispatch the whole stashBuffer, and stash remain data\r\n // then append chunk to stashBuffer (stash)\r\n let buffer = this._stashBuffer.slice(0, this._stashUsed);\r\n let consumed = this._dispatchChunks(buffer, this._stashByteStart);\r\n if (consumed < buffer.byteLength) {\r\n if (consumed > 0) {\r\n let remainArray = new Uint8Array(buffer, consumed);\r\n stashArray.set(remainArray, 0);\r\n this._stashUsed = remainArray.byteLength;\r\n this._stashByteStart += consumed;\r\n }\r\n } else {\r\n this._stashUsed = 0;\r\n this._stashByteStart += consumed;\r\n }\r\n if (this._stashUsed + chunk.byteLength > this._bufferSize) {\r\n this._expandBuffer(this._stashUsed + chunk.byteLength);\r\n stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n }\r\n stashArray.set(new Uint8Array(chunk), this._stashUsed);\r\n this._stashUsed += chunk.byteLength;\r\n } else { // stash buffer empty, but chunkSize > stashSize (oh, holy shit)\r\n // dispatch chunk directly and stash remain data\r\n let consumed = this._dispatchChunks(chunk, byteStart);\r\n if (consumed < chunk.byteLength) {\r\n let remain = chunk.byteLength - consumed;\r\n if (remain > this._bufferSize) {\r\n this._expandBuffer(remain);\r\n stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n }\r\n stashArray.set(new Uint8Array(chunk, consumed), 0);\r\n this._stashUsed += remain;\r\n this._stashByteStart = byteStart + consumed;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n _flushStashBuffer(dropUnconsumed) {\r\n if (this._stashUsed > 0) {\r\n let buffer = this._stashBuffer.slice(0, this._stashUsed);\r\n let consumed = this._dispatchChunks(buffer, this._stashByteStart);\r\n let remain = buffer.byteLength - consumed;\r\n\r\n if (consumed < buffer.byteLength) {\r\n if (dropUnconsumed) {\r\n Log.w(this.TAG, `${remain} bytes unconsumed data remain when flush buffer, dropped`);\r\n } else {\r\n if (consumed > 0) {\r\n let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);\r\n let remainArray = new Uint8Array(buffer, consumed);\r\n stashArray.set(remainArray, 0);\r\n this._stashUsed = remainArray.byteLength;\r\n this._stashByteStart += consumed;\r\n }\r\n return 0;\r\n }\r\n }\r\n this._stashUsed = 0;\r\n this._stashByteStart = 0;\r\n return remain;\r\n }\r\n return 0;\r\n }\r\n\r\n _onLoaderComplete(from, to) {\r\n // Force-flush stash buffer, and drop unconsumed data\r\n this._flushStashBuffer(true);\r\n\r\n if (this._onComplete) {\r\n this._onComplete(this._extraData);\r\n }\r\n }\r\n\r\n _onLoaderError(type, data) {\r\n Log.e(this.TAG, `Loader error, code = ${data.code}, msg = ${data.msg}`);\r\n\r\n this._flushStashBuffer(false);\r\n\r\n if (this._isEarlyEofReconnecting) {\r\n // Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer\r\n this._isEarlyEofReconnecting = false;\r\n type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;\r\n }\r\n\r\n switch (type) {\r\n case LoaderErrors.EARLY_EOF: {\r\n if (!this._config.isLive) {\r\n // Do internal http reconnect if not live stream\r\n if (this._totalLength) {\r\n let nextFrom = this._currentRange.to + 1;\r\n if (nextFrom < this._totalLength) {\r\n Log.w(this.TAG, 'Connection lost, trying reconnect...');\r\n this._isEarlyEofReconnecting = true;\r\n this._internalSeek(nextFrom, false);\r\n }\r\n return;\r\n }\r\n // else: We don't know totalLength, throw UnrecoverableEarlyEof\r\n }\r\n // live stream: throw UnrecoverableEarlyEof error to upper-layer\r\n type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;\r\n break;\r\n }\r\n case LoaderErrors.UNRECOVERABLE_EARLY_EOF:\r\n case LoaderErrors.CONNECTING_TIMEOUT:\r\n case LoaderErrors.HTTP_STATUS_CODE_INVALID:\r\n case LoaderErrors.EXCEPTION:\r\n break;\r\n }\r\n\r\n if (this._onError) {\r\n this._onError(type, data);\r\n } else {\r\n throw new RuntimeException('IOException: ' + data.msg);\r\n }\r\n }\r\n\r\n}\r\n\r\nexport default IOController;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport {NotImplementedException} from '../utils/exception.js';\r\n\r\nexport const LoaderStatus = {\r\n kIdle: 0,\r\n kConnecting: 1,\r\n kBuffering: 2,\r\n kError: 3,\r\n kComplete: 4\r\n};\r\n\r\nexport const LoaderErrors = {\r\n OK: 'OK',\r\n EXCEPTION: 'Exception',\r\n HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid',\r\n CONNECTING_TIMEOUT: 'ConnectingTimeout',\r\n EARLY_EOF: 'EarlyEof',\r\n UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof'\r\n};\r\n\r\n/* Loader has callbacks which have following prototypes:\r\n * function onContentLengthKnown(contentLength: number): void\r\n * function onURLRedirect(url: string): void\r\n * function onDataArrival(chunk: ArrayBuffer, byteStart: number, receivedLength: number): void\r\n * function onError(errorType: number, errorInfo: {code: number, msg: string}): void\r\n * function onComplete(rangeFrom: number, rangeTo: number): void\r\n */\r\nexport class BaseLoader {\r\n\r\n constructor(typeName) {\r\n this._type = typeName || 'undefined';\r\n this._status = LoaderStatus.kIdle;\r\n this._needStash = false;\r\n // callbacks\r\n this._onContentLengthKnown = null;\r\n this._onURLRedirect = null;\r\n this._onDataArrival = null;\r\n this._onError = null;\r\n this._onComplete = null;\r\n }\r\n\r\n destroy() {\r\n this._status = LoaderStatus.kIdle;\r\n this._onContentLengthKnown = null;\r\n this._onURLRedirect = null;\r\n this._onDataArrival = null;\r\n this._onError = null;\r\n this._onComplete = null;\r\n }\r\n\r\n isWorking() {\r\n return this._status === LoaderStatus.kConnecting || this._status === LoaderStatus.kBuffering;\r\n }\r\n\r\n get type() {\r\n return this._type;\r\n }\r\n\r\n get status() {\r\n return this._status;\r\n }\r\n\r\n get needStashBuffer() {\r\n return this._needStash;\r\n }\r\n\r\n get onContentLengthKnown() {\r\n return this._onContentLengthKnown;\r\n }\r\n\r\n set onContentLengthKnown(callback) {\r\n this._onContentLengthKnown = callback;\r\n }\r\n\r\n get onURLRedirect() {\r\n return this._onURLRedirect;\r\n }\r\n\r\n set onURLRedirect(callback) {\r\n this._onURLRedirect = callback;\r\n }\r\n\r\n get onDataArrival() {\r\n return this._onDataArrival;\r\n }\r\n\r\n set onDataArrival(callback) {\r\n this._onDataArrival = callback;\r\n }\r\n\r\n get onError() {\r\n return this._onError;\r\n }\r\n\r\n set onError(callback) {\r\n this._onError = callback;\r\n }\r\n\r\n get onComplete() {\r\n return this._onComplete;\r\n }\r\n\r\n set onComplete(callback) {\r\n this._onComplete = callback;\r\n }\r\n\r\n // pure virtual\r\n open(dataSource, range) {\r\n throw new NotImplementedException('Unimplemented abstract function!');\r\n }\r\n\r\n abort() {\r\n throw new NotImplementedException('Unimplemented abstract function!');\r\n }\r\n\r\n\r\n}","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nlet Browser = {};\r\n\r\nfunction detect() {\r\n // modified from jquery-browser-plugin\r\n\r\n let ua = self.navigator.userAgent.toLowerCase();\r\n\r\n let match = /(edge)\\/([\\w.]+)/.exec(ua) ||\r\n /(opr)[\\/]([\\w.]+)/.exec(ua) ||\r\n /(chrome)[ \\/]([\\w.]+)/.exec(ua) ||\r\n /(iemobile)[\\/]([\\w.]+)/.exec(ua) ||\r\n /(version)(applewebkit)[ \\/]([\\w.]+).*(safari)[ \\/]([\\w.]+)/.exec(ua) ||\r\n /(webkit)[ \\/]([\\w.]+).*(version)[ \\/]([\\w.]+).*(safari)[ \\/]([\\w.]+)/.exec(ua) ||\r\n /(webkit)[ \\/]([\\w.]+)/.exec(ua) ||\r\n /(opera)(?:.*version|)[ \\/]([\\w.]+)/.exec(ua) ||\r\n /(msie) ([\\w.]+)/.exec(ua) ||\r\n ua.indexOf('trident') >= 0 && /(rv)(?::| )([\\w.]+)/.exec(ua) ||\r\n ua.indexOf('compatible') < 0 && /(firefox)[ \\/]([\\w.]+)/.exec(ua) ||\r\n [];\r\n\r\n let platform_match = /(ipad)/.exec(ua) ||\r\n /(ipod)/.exec(ua) ||\r\n /(windows phone)/.exec(ua) ||\r\n /(iphone)/.exec(ua) ||\r\n /(kindle)/.exec(ua) ||\r\n /(android)/.exec(ua) ||\r\n /(windows)/.exec(ua) ||\r\n /(mac)/.exec(ua) ||\r\n /(linux)/.exec(ua) ||\r\n /(cros)/.exec(ua) ||\r\n [];\r\n\r\n let matched = {\r\n browser: match[5] || match[3] || match[1] || '',\r\n version: match[2] || match[4] || '0',\r\n majorVersion: match[4] || match[2] || '0',\r\n platform: platform_match[0] || ''\r\n };\r\n\r\n let browser = {};\r\n if (matched.browser) {\r\n browser[matched.browser] = true;\r\n\r\n let versionArray = matched.majorVersion.split('.');\r\n browser.version = {\r\n major: parseInt(matched.majorVersion, 10),\r\n string: matched.version\r\n };\r\n if (versionArray.length > 1) {\r\n browser.version.minor = parseInt(versionArray[1], 10);\r\n }\r\n if (versionArray.length > 2) {\r\n browser.version.build = parseInt(versionArray[2], 10);\r\n }\r\n }\r\n\r\n if (matched.platform) {\r\n browser[matched.platform] = true;\r\n }\r\n\r\n if (browser.chrome || browser.opr || browser.safari) {\r\n browser.webkit = true;\r\n }\r\n\r\n // MSIE. IE11 has 'rv' identifer\r\n if (browser.rv || browser.iemobile) {\r\n if (browser.rv) {\r\n delete browser.rv;\r\n }\r\n let msie = 'msie';\r\n matched.browser = msie;\r\n browser[msie] = true;\r\n }\r\n\r\n // Microsoft Edge\r\n if (browser.edge) {\r\n delete browser.edge;\r\n let msedge = 'msedge';\r\n matched.browser = msedge;\r\n browser[msedge] = true;\r\n }\r\n\r\n // Opera 15+\r\n if (browser.opr) {\r\n let opera = 'opera';\r\n matched.browser = opera;\r\n browser[opera] = true;\r\n }\r\n\r\n // Stock android browsers are marked as Safari\r\n if (browser.safari && browser.android) {\r\n let android = 'android';\r\n matched.browser = android;\r\n browser[android] = true;\r\n }\r\n\r\n browser.name = matched.browser;\r\n browser.platform = matched.platform;\r\n\r\n for (let key in Browser) {\r\n if (Browser.hasOwnProperty(key)) {\r\n delete Browser[key];\r\n }\r\n }\r\n Object.assign(Browser, browser);\r\n}\r\n\r\ndetect();\r\n\r\nexport default Browser;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nexport class RuntimeException {\r\n\r\n constructor(message) {\r\n this._message = message;\r\n }\r\n\r\n get name() {\r\n return 'RuntimeException';\r\n }\r\n\r\n get message() {\r\n return this._message;\r\n }\r\n\r\n toString() {\r\n return this.name + ': ' + this.message;\r\n }\r\n\r\n}\r\n\r\nexport class IllegalStateException extends RuntimeException {\r\n\r\n constructor(message) {\r\n super(message);\r\n }\r\n\r\n get name() {\r\n return 'IllegalStateException';\r\n }\r\n\r\n}\r\n\r\nexport class InvalidArgumentException extends RuntimeException {\r\n\r\n constructor(message) {\r\n super(message);\r\n }\r\n\r\n get name() {\r\n return 'InvalidArgumentException';\r\n }\r\n\r\n}\r\n\r\nexport class NotImplementedException extends RuntimeException {\r\n\r\n constructor(message) {\r\n super(message);\r\n }\r\n\r\n get name() {\r\n return 'NotImplementedException';\r\n }\r\n\r\n}\r\n","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\n\r\nclass Log {\r\n\r\n static e(tag, msg) {\r\n if (!tag || Log.FORCE_GLOBAL_TAG)\r\n tag = Log.GLOBAL_TAG;\r\n\r\n let str = `[${tag}] > ${msg}`;\r\n\r\n if (Log.ENABLE_CALLBACK) {\r\n Log.emitter.emit('log', 'error', str);\r\n }\r\n\r\n if (!Log.ENABLE_ERROR) {\r\n return;\r\n }\r\n\r\n if (console.error) {\r\n console.error(str);\r\n } else if (console.warn) {\r\n console.warn(str);\r\n } else {\r\n console.log(str);\r\n }\r\n }\r\n\r\n static i(tag, msg) {\r\n if (!tag || Log.FORCE_GLOBAL_TAG)\r\n tag = Log.GLOBAL_TAG;\r\n\r\n let str = `[${tag}] > ${msg}`;\r\n\r\n if (Log.ENABLE_CALLBACK) {\r\n Log.emitter.emit('log', 'info', str);\r\n }\r\n\r\n if (!Log.ENABLE_INFO) {\r\n return;\r\n }\r\n\r\n if (console.info) {\r\n console.info(str);\r\n } else {\r\n console.log(str);\r\n }\r\n }\r\n\r\n static w(tag, msg) {\r\n if (!tag || Log.FORCE_GLOBAL_TAG)\r\n tag = Log.GLOBAL_TAG;\r\n\r\n let str = `[${tag}] > ${msg}`;\r\n\r\n if (Log.ENABLE_CALLBACK) {\r\n Log.emitter.emit('log', 'warn', str);\r\n }\r\n\r\n if (!Log.ENABLE_WARN) {\r\n return;\r\n }\r\n\r\n if (console.warn) {\r\n console.warn(str);\r\n } else {\r\n console.log(str);\r\n }\r\n }\r\n\r\n static d(tag, msg) {\r\n if (!tag || Log.FORCE_GLOBAL_TAG)\r\n tag = Log.GLOBAL_TAG;\r\n\r\n let str = `[${tag}] > ${msg}`;\r\n\r\n if (Log.ENABLE_CALLBACK) {\r\n Log.emitter.emit('log', 'debug', str);\r\n }\r\n\r\n if (!Log.ENABLE_DEBUG) {\r\n return;\r\n }\r\n\r\n if (console.debug) {\r\n console.debug(str);\r\n } else {\r\n console.log(str);\r\n }\r\n }\r\n\r\n static v(tag, msg) {\r\n if (!tag || Log.FORCE_GLOBAL_TAG)\r\n tag = Log.GLOBAL_TAG;\r\n\r\n let str = `[${tag}] > ${msg}`;\r\n\r\n if (Log.ENABLE_CALLBACK) {\r\n Log.emitter.emit('log', 'verbose', str);\r\n }\r\n\r\n if (!Log.ENABLE_VERBOSE) {\r\n return;\r\n }\r\n\r\n console.log(str);\r\n }\r\n\r\n}\r\n\r\nLog.GLOBAL_TAG = 'flv.js';\r\nLog.FORCE_GLOBAL_TAG = false;\r\nLog.ENABLE_ERROR = true;\r\nLog.ENABLE_INFO = true;\r\nLog.ENABLE_WARN = true;\r\nLog.ENABLE_DEBUG = true;\r\nLog.ENABLE_VERBOSE = true;\r\n\r\nLog.ENABLE_CALLBACK = false;\r\n\r\nLog.emitter = new EventEmitter();\r\n\r\nexport default Log;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nimport EventEmitter from 'events';\r\nimport Log from './logger.js';\r\n\r\nclass LoggingControl {\r\n\r\n static get forceGlobalTag() {\r\n return Log.FORCE_GLOBAL_TAG;\r\n }\r\n\r\n static set forceGlobalTag(enable) {\r\n Log.FORCE_GLOBAL_TAG = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get globalTag() {\r\n return Log.GLOBAL_TAG;\r\n }\r\n\r\n static set globalTag(tag) {\r\n Log.GLOBAL_TAG = tag;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableAll() {\r\n return Log.ENABLE_VERBOSE\r\n && Log.ENABLE_DEBUG\r\n && Log.ENABLE_INFO\r\n && Log.ENABLE_WARN\r\n && Log.ENABLE_ERROR;\r\n }\r\n\r\n static set enableAll(enable) {\r\n Log.ENABLE_VERBOSE = enable;\r\n Log.ENABLE_DEBUG = enable;\r\n Log.ENABLE_INFO = enable;\r\n Log.ENABLE_WARN = enable;\r\n Log.ENABLE_ERROR = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableDebug() {\r\n return Log.ENABLE_DEBUG;\r\n }\r\n\r\n static set enableDebug(enable) {\r\n Log.ENABLE_DEBUG = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableVerbose() {\r\n return Log.ENABLE_VERBOSE;\r\n }\r\n\r\n static set enableVerbose(enable) {\r\n Log.ENABLE_VERBOSE = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableInfo() {\r\n return Log.ENABLE_INFO;\r\n }\r\n\r\n static set enableInfo(enable) {\r\n Log.ENABLE_INFO = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableWarn() {\r\n return Log.ENABLE_WARN;\r\n }\r\n\r\n static set enableWarn(enable) {\r\n Log.ENABLE_WARN = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static get enableError() {\r\n return Log.ENABLE_ERROR;\r\n }\r\n\r\n static set enableError(enable) {\r\n Log.ENABLE_ERROR = enable;\r\n LoggingControl._notifyChange();\r\n }\r\n\r\n static getConfig() {\r\n return {\r\n globalTag: Log.GLOBAL_TAG,\r\n forceGlobalTag: Log.FORCE_GLOBAL_TAG,\r\n enableVerbose: Log.ENABLE_VERBOSE,\r\n enableDebug: Log.ENABLE_DEBUG,\r\n enableInfo: Log.ENABLE_INFO,\r\n enableWarn: Log.ENABLE_WARN,\r\n enableError: Log.ENABLE_ERROR,\r\n enableCallback: Log.ENABLE_CALLBACK\r\n };\r\n }\r\n\r\n static applyConfig(config) {\r\n Log.GLOBAL_TAG = config.globalTag;\r\n Log.FORCE_GLOBAL_TAG = config.forceGlobalTag;\r\n Log.ENABLE_VERBOSE = config.enableVerbose;\r\n Log.ENABLE_DEBUG = config.enableDebug;\r\n Log.ENABLE_INFO = config.enableInfo;\r\n Log.ENABLE_WARN = config.enableWarn;\r\n Log.ENABLE_ERROR = config.enableError;\r\n Log.ENABLE_CALLBACK = config.enableCallback;\r\n }\r\n\r\n static _notifyChange() {\r\n let emitter = LoggingControl.emitter;\r\n\r\n if (emitter.listenerCount('change') > 0) {\r\n let config = LoggingControl.getConfig();\r\n emitter.emit('change', config);\r\n }\r\n }\r\n\r\n static registerListener(listener) {\r\n LoggingControl.emitter.addListener('change', listener);\r\n }\r\n\r\n static removeListener(listener) {\r\n LoggingControl.emitter.removeListener('change', listener);\r\n }\r\n\r\n static addLogListener(listener) {\r\n Log.emitter.addListener('log', listener);\r\n if (Log.emitter.listenerCount('log') > 0) {\r\n Log.ENABLE_CALLBACK = true;\r\n LoggingControl._notifyChange();\r\n }\r\n }\r\n\r\n static removeLogListener(listener) {\r\n Log.emitter.removeListener('log', listener);\r\n if (Log.emitter.listenerCount('log') === 0) {\r\n Log.ENABLE_CALLBACK = false;\r\n LoggingControl._notifyChange();\r\n }\r\n }\r\n\r\n}\r\n\r\nLoggingControl.emitter = new EventEmitter();\r\n\r\nexport default LoggingControl;","/*\r\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\r\n *\r\n * @author zheng qian \r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\nclass Polyfill {\r\n\r\n static install() {\r\n // ES6 Object.setPrototypeOf\r\n Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {\r\n obj.__proto__ = proto;\r\n return obj;\r\n };\r\n\r\n // ES6 Object.assign\r\n Object.assign = Object.assign || function (target) {\r\n if (target === undefined || target === null) {\r\n throw new TypeError('Cannot convert undefined or null to object');\r\n }\r\n\r\n let output = Object(target);\r\n for (let i = 1; i < arguments.length; i++) {\r\n let source = arguments[i];\r\n if (source !== undefined && source !== null) {\r\n for (let key in source) {\r\n if (source.hasOwnProperty(key)) {\r\n output[key] = source[key];\r\n }\r\n }\r\n }\r\n }\r\n return output;\r\n };\r\n\r\n // ES6 Promise (missing support in IE11)\r\n if (typeof self.Promise !== 'function') {\r\n require('es6-promise').polyfill();\r\n }\r\n }\r\n\r\n}\r\n\r\nPolyfill.install();\r\n\r\nexport default Polyfill;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// module factories are used so entry inlining is disabled\n// startup\n// Load entry module and return exports\nvar __webpack_exports__ = __webpack_require__(976);\n"],"names":["root","factory","exports","module","define","amd","self","objectOrFunction","x","type","isFunction","isArray","Array","Object","prototype","toString","call","len","vertxNext","customSchedulerFn","asap","callback","arg","queue","flush","scheduleFlush","setScheduler","scheduleFn","setAsap","asapFn","browserWindow","window","undefined","browserGlobal","BrowserMutationObserver","MutationObserver","WebKitMutationObserver","isNode","process","isWorker","Uint8ClampedArray","importScripts","MessageChannel","useNextTick","nextTick","useVertxTimer","useSetTimeout","useMutationObserver","iterations","observer","node","document","createTextNode","observe","characterData","data","useMessageChannel","channel","port1","onmessage","port2","postMessage","globalSetTimeout","setTimeout","i","attemptVertx","vertx","Function","require","runOnLoop","runOnContext","e","then","onFulfillment","onRejection","parent","this","child","constructor","noop","PROMISE_ID","makePromise","_state","arguments","invokeCallback","_result","subscribe","resolve$1","object","Constructor","promise","resolve","Math","random","substring","PENDING","FULFILLED","REJECTED","selfFulfillment","TypeError","cannotReturnOwn","tryThen","then$$1","value","fulfillmentHandler","rejectionHandler","handleForeignThenable","thenable","sealed","error","fulfill","reason","reject","_label","handleOwnThenable","handleMaybeThenable","maybeThenable","publishRejection","_onerror","publish","_subscribers","length","subscribers","settled","detail","hasCallback","succeeded","initializePromise","resolver","id","nextId","validationError","Error","Enumerator","input","_instanceConstructor","_remaining","_enumerate","_eachEntry","entry","c","resolve$$1","_then","didError","_settledAt","Promise$1","_willSettleAt","state","enumerator","all","entries","race","_","reject$1","needsResolver","needsNew","Promise","catch","finally","polyfill","local","g","P","promiseToString","cast","_setScheduler","_setAsap","_asap","ReflectOwnKeys","R","Reflect","ReflectApply","apply","target","receiver","args","ownKeys","getOwnPropertySymbols","getOwnPropertyNames","concat","NumberIsNaN","Number","isNaN","EventEmitter","init","once","emitter","name","errorListener","err","removeListener","slice","eventTargetAgnosticAddListener","handler","flags","on","addErrorHandlerIfEventEmitter","_events","_eventsCount","_maxListeners","defaultMaxListeners","checkListener","listener","_getMaxListeners","that","_addListener","prepend","m","events","existing","warning","create","newListener","emit","unshift","push","warned","w","String","count","console","warn","onceWrapper","fired","wrapFn","_onceWrap","wrapped","bind","_listeners","unwrap","evlistener","arr","ret","unwrapListeners","arrayClone","listenerCount","n","copy","addEventListener","wrapListener","removeEventListener","defineProperty","enumerable","get","set","RangeError","getPrototypeOf","setMaxListeners","getMaxListeners","doError","er","message","context","listeners","addListener","prependListener","prependOnceListener","list","position","originalListener","shift","index","pop","spliceOne","off","removeAllListeners","key","keys","rawListeners","eventNames","webpackBootstrapFunc","modules","installedModules","moduleId","l","d","getter","o","configurable","r","__esModule","property","hasOwnProperty","p","oe","f","s","ENTRY_MODULE","default","moduleNameReqExp","dependencyRegExp","quoteRegExp","str","replace","getModuleDependencies","sources","queueName","retval","fnString","wrapperSignature","match","webpackRequireName","re","RegExp","exec","__webpack_require__","j","hasValuesInQueues","queues","reduce","hasValues","options","main","requiredModules","modulesQueue","seenModules","moduleToCheck","newModules","newModulesKeys","getRequiredModules","src","filter","forEach","entryModule","JSON","stringify","map","join","blob","Blob","bare","workerUrl","URL","webkitURL","mozURL","msURL","createObjectURL","worker","Worker","objectURL","mimeType","duration","hasAudio","hasVideo","audioCodec","videoCodec","audioDataRate","videoDataRate","audioSampleRate","audioChannelCount","width","height","fps","profile","level","refFrames","chromaFormat","sarNum","sarDen","metadata","segments","segmentCount","hasKeyframesIndex","keyframesIndex","isComplete","audioInfoComplete","videoInfoComplete","isSeekable","getNearestKeyframe","milliseconds","table","keyframeIdx","_search","times","fileposition","filepositions","idx","last","mid","lbound","ubound","floor","dts","pts","originalDts","isSync","isSyncPoint","beginDts","endDts","beginPts","endPts","originalBeginDts","originalEndDts","syncPoints","firstSample","lastSample","appendSyncPoint","sampleInfo","_list","clear","appendArray","getLastSyncPointBeforeDts","_type","_lastAppendLocation","isEmpty","_searchNearestSegmentBefore","_searchNearestSegmentAfter","append","mediaSegmentInfo","msi","lastAppendIdx","insertIdx","splice","getLastSegmentBefore","getLastSampleBefore","segment","getLastSyncPointBefore","segmentIdx","checkContinuation","uint8array","start","checkLength","array","buf","out","fromCharCode","ucs4","le","ArrayBuffer","DataView","setInt16","Int16Array","parseScriptData","arrayBuffer","dataOffset","dataSize","AMF","parseValue","size","parseObject","parseString","isObjectEnd","objectEnd","parseVariable","getUint16","Uint8Array","parseLongString","getUint32","parseDate","v","timestamp","getFloat64","localTimeOffset","getInt16","Date","offset","getUint8","amfstr","terminal","amfobj","amfvar","strictArrayLength","val","date","amfLongStr","TAG","_buffer","_buffer_index","_total_bytes","byteLength","_total_bits","_current_word","_current_word_bits_left","destroy","_fillCurrentWord","buffer_bytes_left","bytes_read","min","word","subarray","buffer","readBits","bits","result","bits_need_left","bits_read_next","result2","readBool","readByte","_skipLeadingZero","zero_count","readUEG","leading_zeros","readSEG","_ebsp2rbsp","src_length","dst","dst_idx","parseSPS","rbsp","SPSParser","gb","profile_idc","level_idc","profile_string","getProfileString","level_string","getLevelString","chroma_format_idc","chroma_format","bit_depth","scaling_list_count","_skipScalingList","pic_order_cnt_type","num_ref_frames_in_pic_order_cnt_cycle","ref_frames","pic_width_in_mbs_minus1","pic_height_in_map_units_minus1","frame_mbs_only_flag","frame_crop_left_offset","frame_crop_right_offset","frame_crop_top_offset","frame_crop_bottom_offset","sar_width","sar_height","fps_fixed","fps_num","fps_den","aspect_ratio_idc","num_units_in_tick","time_scale","sarScale","crop_unit_x","crop_unit_y","codec_width","codec_height","present_width","ceil","chroma_format_string","getChromaFormatString","frame_rate","fixed","sar_ratio","codec_size","present_size","last_scale","next_scale","toFixed","chroma","probeData","config","_config","_onError","_onMediaInfo","_onMetaDataArrived","_onScriptDataArrived","_onTrackMetadata","_onDataAvailable","_dataOffset","_firstParse","_dispatch","_hasAudio","hasAudioTrack","_hasVideo","hasVideoTrack","_hasAudioFlagOverrided","_hasVideoFlagOverrided","_audioInitialMetadataDispatched","_videoInitialMetadataDispatched","_mediaInfo","_metadata","_audioMetadata","_videoMetadata","_naluLengthSize","_timestampBase","_timescale","_duration","_durationOverrided","_referenceFrameRate","_flvSoundRateTable","_mpegSamplingRates","_mpegAudioV10SampleRateTable","_mpegAudioV20SampleRateTable","_mpegAudioV25SampleRateTable","_mpegAudioL1BitRateTable","_mpegAudioL2BitRateTable","_mpegAudioL3BitRateTable","_videoTrack","sequenceNumber","samples","_audioTrack","_littleEndian","probe","mismatch","consumed","bindDataSource","loader","onDataArrival","parseChunks","base","resetMediaInfo","_isInitialMetadataDispatched","chunk","byteStart","FLVDemuxer","tagType","ts2","ts1","_parseAudioData","_parseVideoData","_parseScriptData","prevTagSize","scriptData","onMetaData","assign","audiodatarate","videodatarate","framerate","keyframes","_parseKeyframesIndex","time","tagTimestamp","soundSpec","soundFormat","soundRate","soundRateIndex","soundType","meta","track","timescale","channelCount","aacData","_parseAACAudioData","packetType","misc","samplingRate","codec","originalCodec","refSampleDuration","mi","aacSample","unit","_parseMP3AudioData","bitRate","mp3Sample","FORMAT_ERROR","CODEC_UNSUPPORTED","_parseAACAudioSpecificConfig","originalAudioObjectType","samplingIndex","audioObjectType","extensionSamplingIndex","samplingFrequence","channelConfig","userAgent","navigator","toLowerCase","indexOf","requestHeader","ver","layer","bitrate_index","sampling_freq_index","channel_count","sample_rate","bit_rate","tagPosition","spec","frameType","codecId","_parseAVCVideoPacket","cts","_parseAVCDecoderConfigurationRecord","_parseAVCVideoData","avcc","version","avcProfile","spsCount","sps","codecWidth","codecHeight","presentWidth","presentHeight","bitDepth","sarRatio","frameRate","codecArray","codecString","h","ppsCount","units","lengthSize","keyframe","naluSize","unitType","avcSample","isKeyframe","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","charCodeAt","constants","FTYP","STSD_PREFIX","STTS","STSC","STCO","STSZ","HDLR_VIDEO","HDLR_AUDIO","DREF","SMHD","VMHD","box","datas","arrayCount","generateInitSegment","trackId","xmhd","mp3","sampleRate","configSize","baseMediaDecodeTime","sampleCount","isLeading","dependsOn","isDependedOn","hasRedundancy","isNonSync","getSilentFrame","_isLive","isLive","_dtsBase","_dtsBaseInited","_audioDtsBase","Infinity","_videoDtsBase","_audioNextDts","_videoNextDts","_audioStashedLastSample","_videoStashedLastSample","_audioMeta","_videoMeta","_audioSegmentInfoList","_videoSegmentInfoList","_onInitSegment","_onMediaSegment","_forceFirstIDR","chrome","major","build","_fillSilentAfterSeek","msedge","msie","_mp3UseMpegAudio","firefox","_fillAudioTimestampGap","fixAudioTimestampGap","producer","onDataAvailable","remux","onTrackMetadata","_onTrackMetadataReceived","insertDiscontinuity","seek","audioTrack","videoTrack","_calculateDtsBase","_remuxVideo","_remuxAudio","metabox","container","mediaDuration","flushStashedSamples","videoSample","audioSample","force","lastDts","dtsCorrection","firstDts","mpegRawTrack","firstSegmentAfterSeek","insertPrefixSilentFrame","mdatbox","mdatBytes","sample","firstSampleOriginalDts","distance","firstSampleDts","videoSegment","silentUnit","silentFrameDuration","mp4Samples","needFillSilentFrames","silentFrames","sampleDuration","curRefDts","safari","frameCount","round","intDts","intDuration","frame","latest","info","moofbox","_mergeBoxes","timestampOffset","lastPts","firstPts","syncPoint","mediaDataSource","_emitter","filesize","url","cors","withCredentials","_mediaDataSource","_currentSegmentIndex","totalDuration","timestampBase","referrerPolicy","_demuxer","_remuxer","_ioctl","_pendingSeekTime","_pendingResolveSeekPoint","_statisticsReporter","_disableStatisticsReporter","event","_loadSegment","_enableStatisticsReporter","segmentIndex","optionalFrom","dataSource","ioctl","onError","_onIOException","onSeeked","_onIOSeeked","onComplete","_onIOComplete","onRedirect","_onIORedirect","onRecoveredEarlyEof","_onIORecoveredEarlyEof","_onInitChunkArrival","open","stop","_internalAbort","pause","isWorking","resume","isPaused","targetSegmentIndex","_searchSegmentIndexContains","segmentInfo","targetSegmentInfo","_reportSegmentMediaInfo","mds","overridedDuration","overridedHasAudio","overridedHasVideo","_onDemuxException","onMediaInfo","onMetaDataArrived","onScriptDataArrived","onInitSegment","_onRemuxerInitSegmentArrival","onMediaSegment","_onRemuxerMediaSegmentArrival","DEMUX_ERROR","FORMAT_UNSUPPORTED","mediaInfo","setPrototypeOf","METADATA_ARRIVED","SCRIPTDATA_ARRIVED","extraData","nextSegmentIndex","LOADING_COMPLETE","redirectedURL","RECOVERED_EARLY_EOF","code","msg","IO_ERROR","initSegment","INIT_SEGMENT","mediaSegment","MEDIA_SEGMENT","seekpoint","RECOMMEND_SEEKPOINT","setInterval","_reportStatisticsInfo","statisticsInfoReportInterval","clearInterval","exportInfo","MEDIA_INFO","currentURL","hasRedirect","currentRedirectedURL","speed","currentSpeed","loaderType","currentSegmentIndex","totalSegmentCount","STATISTICS_INFO","OK","defaultConfig","enableWorker","enableStashBuffer","stashInitialSize","lazyLoad","lazyLoadMaxDuration","lazyLoadRecoverDuration","deferLoadAfterSourceOpen","autoCleanupMaxBackwardDuration","autoCleanupMinBackwardDuration","accurateSeek","seekType","seekParamStart","seekParamEnd","rangeLoadZeroStart","customSeekHandler","reuseRedirectedURL","headers","customLoader","createDefaultConfig","supportMSEH264Playback","MediaSource","isTypeSupported","supportNetworkStreamIO","getNetworkLoaderTypeName","supportNativeMediaPlayback","Features","videoElement","createElement","canPlay","canPlayType","getFeatureList","features","mseFlvPlayback","mseLiveFlvPlayback","networkStreamIO","networkLoaderName","nativeMP4H264Playback","nativeWebmVP8Playback","nativeWebmVP9Playback","ERROR","_worker","_workerDestroying","_onWorkerMessage","cmd","param","onLoggingConfigChanged","_onLoggingConfigChanged","registerListener","getConfig","_controller","ctl","_onIOError","_onDemuxError","_onLoadingComplete","_onRecoveredEarlyEof","_onStatisticsInfo","_onRecommendSeekpoint","hasWorker","close","statisticsInfo","terminate","logcat","SOURCE_OPEN","UPDATE_END","BUFFER_FULL","autoCleanupSourceBuffer","onSourceOpen","_onSourceOpen","onSourceEnded","_onSourceEnded","onSourceClose","_onSourceClose","onSourceBufferError","_onSourceBufferError","onSourceBufferUpdateEnd","_onSourceBufferUpdateEnd","_mediaSource","_mediaSourceObjectURL","_mediaElement","_isBufferFull","_hasPendingEos","_requireSetMediaDuration","_pendingMediaDuration","_pendingSourceBufferInit","_mimeTypes","video","audio","_sourceBuffers","_lastInitSegments","_pendingSegments","_pendingRemoveRanges","_idrList","detachMediaElement","attachMediaElement","mediaElement","ms","ps","sb","readyState","removeSourceBuffer","endOfStream","removeAttribute","revokeObjectURL","appendInitSegment","deferred","is","firstInitSegment","addSourceBuffer","updating","_doAppendSegments","_updateMediaSourceDuration","appendMediaSegment","_needCleanupSourceBuffer","_doCleanupSourceBuffer","_hasPendingRemoveRanges","seconds","abort","buffered","end","_doRemoveRanges","lastInitSegment","_hasPendingSegments","currentTime","doRemove","removeEnd","current","ranges","range","remove","pendingSegments","currentOffset","targetOffset","abs","appendBuffer","pendings","prr","ErrorTypes","NETWORK_ERROR","MEDIA_ERROR","OTHER_ERROR","ErrorDetails","NETWORK_EXCEPTION","EXCEPTION","NETWORK_STATUS_CODE_INVALID","HTTP_STATUS_CODE_INVALID","NETWORK_TIMEOUT","CONNECTING_TIMEOUT","NETWORK_UNRECOVERABLE_EARLY_EOF","UNRECOVERABLE_EARLY_EOF","MEDIA_MSE_ERROR","MEDIA_FORMAT_ERROR","MEDIA_FORMAT_UNSUPPORTED","MEDIA_CODEC_UNSUPPORTED","onvLoadedMetadata","_onvLoadedMetadata","onvSeeking","_onvSeeking","onvCanPlay","_onvCanPlay","onvStalled","_onvStalled","onvProgress","_onvProgress","performance","now","_now","_requestSetTime","_seekpointRecord","_progressChecker","_msectl","_transmuxer","_mseSourceOpened","_hasPendingLoad","_receivedCanPlay","_statisticsInfo","chromeNeedIDRFix","_alwaysSeekKeyframe","unload","_onmseUpdateEnd","_onmseBufferFull","load","_suspendTransmuxer","statInfo","_fillStatisticsInfo","play","volume","muted","_internalSeek","playerType","HTMLVideoElement","hasQualityInfo","decoded","dropped","getVideoPlaybackQuality","quality","totalVideoFrames","droppedVideoFrames","webkitDecodedFrameCount","webkitDroppedFrameCount","decodedFrames","droppedFrames","currentRangeEnd","_checkProgressAndResume","needResume","from","to","_isTimepointBuffered","directSeek","directSeekBegin","directSeekBeginTime","videoBeginTime","idr","_checkAndApplyUnbufferedSeekpoint","recordTime","clearTimeout","_checkAndResumeStuckPlayback","stalled","media","seekPoint","preload","HTMLAudioElement","videoWidth","videoHeight","install","flvjs","optionalConfig","BaseLoader","LoaderStatus","LoaderErrors","Events","FlvPlayer","NativePlayer","LoggingControl","_firstCheckpoint","_lastCheckpoint","_intervalBytes","_totalBytes","_lastSecondBytes","reset","addBytes","bytes","durationSeconds","currentKBps","seekHandler","_seekHandler","_needStash","_requestAbort","_contentLength","_receivedLength","isSupported","isWorkWellEdge","minor","browserNotBlacklisted","fetch","ReadableStream","_dataSource","_range","sourceURL","seekConfig","Headers","configHeaders","params","method","mode","cache","credentials","AbortController","_abortController","signal","_status","kConnecting","res","kIdle","body","cancel","ok","status","_onURLRedirect","removeURLParameters","lengthHeader","parseInt","_onContentLengthKnown","_pump","getReader","kError","statusText","aborted","kBuffering","reader","read","done","EARLY_EOF","kComplete","_onComplete","_onDataArrival","_xhr","xhr","XMLHttpRequest","responseType","onreadystatechange","onprogress","onloadend","onerror","_requestURL","_onReadyStateChange","_onProgress","_onLoadEnd","_onXhrError","setRequestHeader","send","responseURL","total","response","loaded","_chunkSizeKBList","_currentChunkSizeKB","_currentSpeedNormalized","_zeroSpeedChunkCount","_speedSampler","_waitForTotalLength","_totalLengthReceived","_currentRequestURL","_currentRedirectedURL","_currentRequestRange","_totalLength","_lastTimeLoaded","onload","lastSecondKBps","useRefTotalLength","_openSubRange","_internalOpen","chunkSize","_onLoad","openNextRange","delta","_normalizeSpeed","KBps","normalized","reportComplete","_ws","WebSocket","ws","binaryType","onopen","_onWebSocketOpen","onclose","_onWebSocketClose","_onWebSocketMessage","_onWebSocketError","_dispatchArrayBuffer","FileReader","readAsArrayBuffer","arraybuffer","zeroStart","_zeroStart","seekedURL","paramStart","paramEnd","_startName","_endName","baseUrl","needAnd","baseURL","split","queryIndex","resultParams","pairs","pair","requireAnd","_extraData","_stashInitialSize","_stashUsed","_stashSize","_bufferSize","_stashBuffer","_stashByteStart","_enableStash","_loader","_loaderClass","_isWebSocketURL","test","_refTotalLength","_fullRequestFlag","_currentRange","_redirectedURL","_speedNormalized","_speedNormalizeList","_isEarlyEofReconnecting","_paused","_resumeFrom","_onSeeked","_onRedirect","_selectSeekHandler","_selectLoader","_createLoader","needStashBuffer","onContentLengthKnown","onURLRedirect","_onLoaderChunkArrival","_onLoaderComplete","_onLoaderError","dropUnconsumed","_flushStashBuffer","requestRange","updateUrl","_expandBuffer","expectedBytes","bufferNewSize","newBuffer","stashOldArray","_adjustStashSize","stashSizeKB","bufferSize","_dispatchChunks","chunks","contentLength","receivedLength","stashArray","remainArray","remain","nextFrom","typeName","Browser","ua","platform_match","matched","browser","majorVersion","platform","versionArray","string","opr","webkit","rv","iemobile","edge","opera","android","detect","_message","RuntimeException","tag","Log","FORCE_GLOBAL_TAG","GLOBAL_TAG","ENABLE_CALLBACK","ENABLE_ERROR","log","ENABLE_INFO","ENABLE_WARN","ENABLE_DEBUG","debug","ENABLE_VERBOSE","enable","_notifyChange","globalTag","forceGlobalTag","enableVerbose","enableDebug","enableInfo","enableWarn","enableError","enableCallback","applyConfig","addLogListener","removeLogListener","obj","proto","__proto__","output","source","Polyfill","__webpack_module_cache__","cachedModule","__webpack_modules__","a","definition","globalThis","prop"],"sourceRoot":""} \ No newline at end of file diff --git a/static/js/video.js b/static/js/video.js index 7eb7be7..199e57f 100644 --- a/static/js/video.js +++ b/static/js/video.js @@ -10,7 +10,10 @@ function initPlayer() { let videoElement = document.querySelector('#videoElement'); let flvPlayer = flvjs.createPlayer({ type: 'flv', - url: '/live' + url: '/live', + isLive: true, + hasAudio: true, + hasVideo: true }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load();