//应模块合适安卓手机,因解码流快 //了解当前模块请搜索关键字 》》》TDDE《《《 了解注意注释 var client (function(f) { if(typeof exports === "object" && typeof module !== "undefined") { module.exports = f() } else if(typeof define === "function" && define.amd) { define([], f) } else { var g; if(typeof window !== "undefined") { g = window } else if(typeof global !== "undefined") { g = global } else if(typeof self !== "undefined") { g = self } else { g = this } g.Wfs = f() } })(function() { var define, module, exports; return(function e(t, n, r) { function s(o, u) { if(!n[o]) { if(!t[o]) { var a = typeof require == "function" && require; if(!u && a) return a(o, !0); if(i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function(e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for(var o = 0; o < r.length; o++) s(r[o]); return s })({ 1: [function(require, module, exports) { function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // 向后兼容节点0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // 默认情况下,如果超过10个侦听器 TDDE // 添加到其中。这是一个有用的默认值,有助于查找内存泄漏。 EventEmitter.defaultMaxListeners = 10; // 显然不是所有的发射器都应该限制在10个。此功能允许 // 要增加的。设为零表示无限制。 EventEmitter.prototype.setMaxListeners = function(n) { if(!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if(!this._events) this._events = {}; // 如果没有“错误”事件侦听器,则抛出。 if(type === 'error') { if(!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if(er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if(isUndefined(handler)) return false; if(isFunction(handler)) { switch(arguments.length) { // 快速案件 case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if(isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for(i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if(!isFunction(listener)) throw TypeError('listener must be a function'); if(!this._events) this._events = {}; // 在类型为“newListener”的情况下避免递归!以前 TDDE // 将它添加到侦听器中,首先发出“newListener”。 if(this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if(!this._events[type]) // 优化一个侦听器的情况。不需要额外的数组对象。 this._events[type] = listener; else if(isObject(this._events[type])) // If 如果我们已经有一个数组,只需追加。 this._events[type].push(listener); else // 云游戏添加第二个元素时,需要更改为数组。 this._events[type] = [this._events[type], listener]; // 检查侦听器是否泄漏 if(isObject(this._events[type]) && !this._events[type].warned) { if(!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if(m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if(typeof console.trace === 'function') { console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if(!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if(!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if(!isFunction(listener)) throw TypeError('listener must be a function'); if(!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if(list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if(this._events.removeListener) this.emit('removeListener', type, listener); } else if(isObject(list)) { for(i = length; i-- > 0;) { if(list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if(position < 0) return this; if(list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if(this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if(!this._events) return this; if(!this._events.removeListener) { if(arguments.length === 0) this._events = {}; else if(this._events[type]) delete this._events[type]; return this; } if(arguments.length === 0) { for(key in this._events) { if(key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if(isFunction(listeners)) { this.removeListener(type, listeners); } else if(listeners) { while(listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if(!this._events || !this._events[type]) ret = []; else if(isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.prototype.listenerCount = function(type) { if(this._events) { var evlistener = this._events[type]; if(isFunction(evlistener)) return 1; else if(evlistener) return evlistener.length; } return 0; }; EventEmitter.listenerCount = function(emitter, type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } }, {}], 2: [function(require, module, exports) { var process = module.exports = {}; var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout() { throw new Error('clearTimeout has not been defined'); } (function() { try { if(typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch(e) { cachedSetTimeout = defaultSetTimout; } try { if(typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch(e) { cachedClearTimeout = defaultClearTimeout; } }()) function runTimeout(fun) { if(cachedSetTimeout === setTimeout) { return setTimeout(fun, 0); } if((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { return cachedSetTimeout(fun, 0); } catch(e) { try { return cachedSetTimeout.call(null, fun, 0); } catch(e) { // 渲染不执行8998 return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if(cachedClearTimeout === clearTimeout) { //正常环境 return clearTimeout(marker); } if((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { return cachedClearTimeout(marker); } catch(e) { try { return cachedClearTimeout.call(null, marker); } catch(e) { return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if(!draining || !currentQueue) { return; } draining = false; if(currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if(queue.length) { drainQueue(); } } function drainQueue() { if(draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while(++queueIndex < len) { if(currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function(fun) { var args = new Array(arguments.length - 1); if(arguments.length > 1) { for(var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if(queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 可预测的对象 意思就是 高等画质 function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function() { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.prependListener = noop; process.prependOnceListener = noop; process.listeners = function(name) { return [] } process.binding = function(name) { throw new Error('process.binding is not supported'); }; process.cwd = function() { return '/' }; process.chdir = function(dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; }, {}], 3: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _eventHandler = require('../event-handler'); var _eventHandler2 = _interopRequireDefault(_eventHandler); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if(!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if(typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if(superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* * Buffer Controller */ var BufferController = function(_EventHandler) { _inherits(BufferController, _EventHandler); function BufferController(wfs) { _classCallCheck(this, BufferController); var _this = _possibleConstructorReturn(this, (BufferController.__proto__ || Object.getPrototypeOf(BufferController)).call(this, wfs, _events2.default.MEDIA_ATTACHING, _events2.default.BUFFER_APPENDING, _events2.default.BUFFER_RESET)); _this.mediaSource = null; _this.media = null; _this.pendingTracks = {}; _this.sourceBuffer = {}; _this.segments = []; _this.appended = 0; _this._msDuration = null; _this.onsbue = _this.onSBUpdateEnd.bind(_this); _this.browserType = 0; if(navigator.userAgent.toLowerCase().indexOf('firefox') !== -1) { _this.browserType = 1; } _this.mediaType = 'H264Raw'; _this.websocketName = undefined; _this.channelName = undefined; return _this; } _createClass(BufferController, [{ key: 'destroy', value: function destroy() { _eventHandler2.default.prototype.destroy.call(this); } }, { key: 'onMediaAttaching', value: function onMediaAttaching(data) { var media = this.media = data.media; this.mediaType = data.mediaType; this.websocketName = data.websocketName; this.channelName = data.channelName; if(media) { var ms = this.mediaSource = new MediaSource(); this.onmso = this.onMediaSourceOpen.bind(this); this.onmse = this.onMediaSourceEnded.bind(this); this.onmsc = this.onMediaSourceClose.bind(this); ms.addEventListener('sourceopen', this.onmso); ms.addEventListener('sourceended', this.onmse); ms.addEventListener('sourceclose', this.onmsc); media.src = URL.createObjectURL(ms); } } }, { key: 'onMediaDetaching', value: function onMediaDetaching() {} }, { key: 'onBufferAppending', value: function onBufferAppending(data) { if(!this.segments) { this.segments = [data]; } else { this.segments.push(data); } this.doAppending(); } }, { key: 'onMediaSourceClose', value: function onMediaSourceClose() { console.log('media source closed'); } }, { key: 'onMediaSourceEnded', value: function onMediaSourceEnded() { console.log('media source ended'); } }, { key: 'onSBUpdateEnd', value: function onSBUpdateEnd(event) { // Firefox if(this.browserType === 1) { this.mediaSource.endOfStream(); this.media.play(); } // console.log("黄飞>>>currentTime: " + this.media.currentTime);//1111111111 var buffered = this.sourceBuffer['video'].buffered; var played = this.media.played; for(var j = 0; j < played.length; j++) { // console.log("played start: " + played.start(j));//111111111 // console.log("played end: " + played.end(j)); } // console.log("readystate: " + this.media.readyState); for(var i = 0; i < buffered.length; i++) { // console.log("start: " + buffered.start(i));//1111111111 // console.log("end: " + buffered.end(i)); //this.media.currentTime = buffered.end(i); } this.appending = false; this.doAppending(); this.updateMediaElementDuration(); } }, { key: 'updateMediaElementDuration', value: function updateMediaElementDuration() {} }, { key: 'onMediaSourceOpen', value: function onMediaSourceOpen() { var mediaSource = this.mediaSource; if(mediaSource) { mediaSource.removeEventListener('sourceopen', this.onmso); } if(this.mediaType === 'FMp4') { this.checkPendingTracks(); } this.wfs.trigger(_events2.default.MEDIA_ATTACHED, { media: this.media, channelName: this.channelName, mediaType: this.mediaType, websocketName: this.websocketName }); } }, { key: 'checkPendingTracks', value: function checkPendingTracks() { this.createSourceBuffers({ tracks: 'video', mimeType: '' }); this.pendingTracks = {}; } }, { key: 'onBufferReset', value: function onBufferReset(data) { if(this.mediaType === 'H264Raw') { this.createSourceBuffers({ tracks: 'video', mimeType: data.mimeType }); } } }, { key: 'createSourceBuffers', value: function createSourceBuffers(tracks) { var sourceBuffer = this.sourceBuffer, mediaSource = this.mediaSource; var mimeType = void 0; // 因后端要高等画质 然后 if HUANGFEI if(tracks.mimeType === '') { mimeType = 'video/mp4;codecs=avc1.420028'; // avc1.42c01f avc1.42801e avc1.640028 avc1.420028 } else { mimeType = 'video/mp4;codecs=' + tracks.mimeType; } try { var sb = sourceBuffer['video'] = mediaSource.addSourceBuffer(mimeType); sb.addEventListener('updateend', this.onsbue); track.buffer = sb; } catch(err) {} this.wfs.trigger(_events2.default.BUFFER_CREATED, { tracks: tracks }); this.media.play(); } }, { key: 'doAppending', value: function doAppending() { var wfs = this.wfs, sourceBuffer = this.sourceBuffer, segments = this.segments; if(Object.keys(sourceBuffer).length) { if(this.media.error) { this.segments = []; console.log('trying to append although a media error occured, flush segment and abort'); return; } if(this.appending) { return; } if(segments && segments.length) { var segment = segments.shift(); //console.log("segments len: " + segments.length + " segment len: " + segment.data.length); try { if(sourceBuffer[segment.type]) { this.parent = segment.parent; sourceBuffer[segment.type].appendBuffer(segment.data); this.appendError = 0; this.appended++; this.appending = true; } else {} } catch(err) { segments.unshift(segment); var event = { type: ErrorTypes.MEDIA_ERROR }; if(err.code !== 22) { if(this.appendError) { this.appendError++; } else { this.appendError = 1; } event.details = ErrorDetails.BUFFER_APPEND_ERROR; event.frag = this.fragCurrent; if(this.appendError > wfs.config.appendErrorMaxRetry) { segments = []; event.fatal = true; return; } else { event.fatal = false; } } else { this.segments = []; event.details = ErrorDetails.BUFFER_FULL_ERROR; return; } } } } } }]); return BufferController; }(_eventHandler2.default); exports.default = BufferController; }, { "../event-handler": 8, "../events": 9 }], 4: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _eventHandler = require('../event-handler'); var _eventHandler2 = _interopRequireDefault(_eventHandler); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if(!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if(typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if(superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* * Flow Controller */ var FlowController = function(_EventHandler) { _inherits(FlowController, _EventHandler); function FlowController(wfs) { _classCallCheck(this, FlowController); var _this = _possibleConstructorReturn(this, (FlowController.__proto__ || Object.getPrototypeOf(FlowController)).call(this, wfs, _events2.default.MEDIA_ATTACHED, _events2.default.BUFFER_CREATED, _events2.default.FILE_PARSING_DATA, _events2.default.FILE_HEAD_LOADED, _events2.default.FILE_DATA_LOADED, _events2.default.WEBSOCKET_ATTACHED, _events2.default.FRAG_PARSING_DATA, _events2.default.FRAG_PARSING_INIT_SEGMENT)); _this.fileStart = 0; _this.fileEnd = 0; _this.pendingAppending = 0; _this.mediaType = undefined; channelName: _this.channelName; return _this; } _createClass(FlowController, [{ key: 'destroy', value: function destroy() { _eventHandler2.default.prototype.destroy.call(this); } }, { key: 'onMediaAttached', value: function onMediaAttached(data) { if(data.websocketName != undefined) { //WS拼接url等回调 var query = window.location.search.substring(1); var vars = query.split("&"); var data = {} var url = window.location.href; url = url.split('/') var clientType = vars.find(e => { return e.startsWith('clientType') }) var cardIp = vars.find(e => { return e.startsWith('cardIp') }) var port = vars.find(e => { return e.startsWith('port') }) var sn = vars.find(e => { return e.startsWith('sn') }) var demoTime = vars.find(e => { return e.startsWith('demoTime') }) var data = {} data.clientType=clientType.substring(11, clientType.length) data.cardIp=cardIp.substring(7, cardIp.length) data.port=port.substring(5, port.length) data.sn=sn.substring(3, sn.length) data.demoTime=demoTime.substring(9, demoTime.length) console.log(data) //var client = new WebSocket("wss://xcx.androidscloud.com/videoWebSocket?clientType=0&cardIp=30.30.30.24&port=9100&sn=RK3930C2301900042"); var urlss = url[2] client = new WebSocket("ws://" + urlss + '/videoWebSocket?' + "clientType=" + data.clientType +"&cardIp=" + data.cardIp + "&port=" + data.port + "&sn=" + data.sn); this.wfs.attachWebsocket(client, data.channelName); } else { console.log('websocketName ERROE!!!'); } } }, { key: 'onBufferCreated', value: function onBufferCreated(data) { this.mediaType = data.mediaType; } }, { key: 'onFileHeadLoaded', value: function onFileHeadLoaded(data) {} }, { key: 'onFileDataLoaded', value: function onFileDataLoaded(data) {} }, { key: 'onFileParsingData', value: function onFileParsingData(data) {} }, { key: 'onWebsocketAttached', value: function onWebsocketAttached(data) { this.wfs.trigger(_events2.default.BUFFER_APPENDING, { type: 'video', data: data.payload, parent: 'main' }); } }, { key: 'onFragParsingInitSegment', value: function onFragParsingInitSegment(data) { var tracks = data.tracks, trackName, track; track = tracks.video; if(track) { track.id = data.id; } for(trackName in tracks) { track = tracks[trackName]; var initSegment = track.initSegment; if(initSegment) { this.pendingAppending++; this.wfs.trigger(_events2.default.BUFFER_APPENDING, { type: trackName, data: initSegment, parent: 'main' }); } } } }, { key: 'onFragParsingData', value: function onFragParsingData(data) { var _this2 = this; if(data.type === 'video') {} [data.data1, data.data2].forEach(function(buffer) { if(buffer) { _this2.pendingAppending++; _this2.wfs.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'main' }); } }); } }]); return FlowController; }(_eventHandler2.default); exports.default = FlowController; }, { "../event-handler": 8, "../events": 9 }], 5: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _logger = require('../utils/logger'); function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var ExpGolomb = function() { function ExpGolomb(data) { _classCallCheck(this, ExpGolomb); this.data = data; // 要检查的剩余字节数this.数据 this.bytesAvailable = this.data.byteLength; // 正在检查的当前uint this.word = 0; // :uint // 当前字中还剩要检查的位数 huangfei HDDT this.bitsAvailable = 0; // :uint } // ():void _createClass(ExpGolomb, [{ key: 'loadWord', value: function loadWord() { var position = this.data.byteLength - this.bytesAvailable, workingBytes = new Uint8Array(4), availableBytes = Math.min(4, this.bytesAvailable); if(availableBytes === 0) { throw new Error('no bytes available'); } workingBytes.set(this.data.subarray(position, position + availableBytes)); this.word = new DataView(workingBytes.buffer).getUint32(0); // 跟踪的数量this.数据已经处理过的 this.bitsAvailable = availableBytes * 8; this.bytesAvailable -= availableBytes; } // (count:int):void }, { key: 'skipBits', //船体用头 0001 value: function skipBits(count) { var skipBytes; // :int if(this.bitsAvailable > count) { this.word <<= count; this.bitsAvailable -= count; } else { count -= this.bitsAvailable; skipBytes = count >> 3; count -= skipBytes >> 3; this.bytesAvailable -= skipBytes; this.loadWord(); this.word <<= count; this.bitsAvailable -= count; } } // (size:int):uint }, { key: 'readBits', value: function readBits(size) { var bits = Math.min(this.bitsAvailable, size), // :uint valu = this.word >>> 32 - bits; // :uint if(size > 32) { _logger.logger.error('Cannot read more than 32 bits at a time'); } this.bitsAvailable -= bits; if(this.bitsAvailable > 0) { this.word <<= bits; } else if(this.bytesAvailable > 0) { this.loadWord(); } bits = size - bits; if(bits > 0) { return valu << bits | this.readBits(bits); } else { return valu; } } // ():uint }, { key: 'skipLZ', value: function skipLZ() { var leadingZeroCount; // :uint for(leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { if(0 !== (this.word & 0x80000000 >>> leadingZeroCount)) { // the first bit of working word is 1 this.word <<= leadingZeroCount; this.bitsAvailable -= leadingZeroCount; return leadingZeroCount; } } // we exhausted word and still have not found a 1 this.loadWord(); return leadingZeroCount + this.skipLZ(); } // ():void }, { key: 'skipUEG', value: function skipUEG() { this.skipBits(1 + this.skipLZ()); } // ():void }, { key: 'skipEG', value: function skipEG() { this.skipBits(1 + this.skipLZ()); } // ():uint }, { key: 'readUEG', value: function readUEG() { var clz = this.skipLZ(); // :uint return this.readBits(clz + 1) - 1; } // ():int }, { key: 'readEG', value: function readEG() { var valu = this.readUEG(); // :int if(0x01 & valu) { // 如果设置了低阶位,则该数字为奇数 TDDE HUANG 这个是为了画read return 1 + valu >>> 1; // 加1使其均匀,然后除以2 } else { return -1 * (valu >>> 1); // 除以2,然后设为负数 } } // Some convenience functions // :Boolean }, { key: 'readBoolean', value: function readBoolean() { return 1 === this.readBits(1); } // ():int }, { key: 'readUByte', value: function readUByte() { return this.readBits(8); } // ():int }, { key: 'readUShort', value: function readUShort() { return this.readBits(16); } // ():int }, { key: 'readUInt', value: function readUInt() { return this.readBits(32); } /** *将ExpGolomb解码器移过缩放列表。缩放 *列表可以作为序列参数的一部分进行传输 *与变形无关。 *@param count{number}此缩放列表中的条目数 *@见建议ITU-T H.264,第7.3.2.1.1.1节 */ }, { key: 'skipScalingList', value: function skipScalingList(count) { var lastScale = 8, nextScale = 8, j, deltaScale; for(j = 0; j < count; j++) { if(nextScale !== 0) { deltaScale = this.readEG(); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = nextScale === 0 ? lastScale : nextScale; } } /** *读取序列参数集并返回一些的视频 *财产。序列参数集是H264元数据 *描述即将到来的视频帧的属性。 *@param data{Uint8Array}序列参数集的字节数 *@return{object}从 *序列参数集,包括 *关联的视频帧。 */ }, { key: 'readSPS', value: function readSPS() { var frameCropLeftOffset = 0, frameCropRightOffset = 0, frameCropTopOffset = 0, frameCropBottomOffset = 0, sarScale = 1, profileIdc, profileCompat, levelIdc, numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, picHeightInMapUnitsMinus1, frameMbsOnlyFlag, scalingListCount, i; this.readUByte(); profileIdc = this.readUByte(); // profile_idc 》》》idc简介 profileCompat = this.readBits(5); // constraint_set[0-4]_flag, u(5) this.skipBits(3); // reserved_zero_3bits u(3), levelIdc = this.readUByte(); //level_idc u(8) this.skipUEG(); // seq_parameter_set_id // 有些配置文件有更多我们不需要的可选数据 if(profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { var chromaFormatIdc = this.readUEG(); if(chromaFormatIdc === 3) { this.skipBits(1); // separate_colour_plane_flag对应的函数 } this.skipUEG(); // bit_depth_luma_minus8 对应的函数 this.skipUEG(); // bit_depth_chroma_minus8 对应的函数 this.skipBits(1); // qpprime_y_zero_transform_bypass_flag 对应的函数 if(this.readBoolean()) { // seq_scaling_matrix_present_flag scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; for(i = 0; i < scalingListCount; i++) { if(this.readBoolean()) { // seq_scaling_list_present_flag[ i ] if(i < 6) { this.skipScalingList(16); } else { this.skipScalingList(64); } } } } } this.skipUEG(); var picOrderCntType = this.readUEG(); if(picOrderCntType === 0) { this.readUEG(); //log2最大pic顺序cnt u lsb minus4 } else if(picOrderCntType === 1) { this.skipBits(1); // 标志 this.skipEG(); // 非参考图片的偏移量 this.skipEG(); // 从上到下”字段的偏移量 numRefFramesInPicOrderCntCycle = this.readUEG(); for(i = 0; i < numRefFramesInPicOrderCntCycle; i++) { this.skipEG(); // 参考帧的偏移量[ i ] } } this.skipUEG(); // efu u帧中的最大值 this.skipBits(1); // 间隙数值允许标志 picWidthInMbsMinus1 = this.readUEG(); picHeightInMapUnitsMinus1 = this.readUEG(); frameMbsOnlyFlag = this.readBits(1); if(frameMbsOnlyFlag === 0) { this.skipBits(1); // mb_adaptive_frame_field_flag 对应的函数 } this.skipBits(1); // direct_8x8_inference_flag 对应的函数 if(this.readBoolean()) { frameCropLeftOffset = this.readUEG(); frameCropRightOffset = this.readUEG(); frameCropTopOffset = this.readUEG(); frameCropBottomOffset = this.readUEG(); } if(this.readBoolean()) { if(this.readBoolean()) { var sarRatio = void 0; var aspectRatioIdc = this.readUByte(); switch(aspectRatioIdc) { case 1: sarRatio = [1, 1]; break; case 2: sarRatio = [12, 11]; break; case 3: sarRatio = [10, 11]; break; case 4: sarRatio = [16, 11]; break; case 5: sarRatio = [40, 33]; break; case 6: sarRatio = [24, 11]; break; case 7: sarRatio = [20, 11]; break; case 8: sarRatio = [32, 11]; break; case 9: sarRatio = [80, 33]; break; case 10: sarRatio = [18, 11]; break; case 11: sarRatio = [15, 11]; break; case 12: sarRatio = [64, 33]; break; case 13: sarRatio = [160, 99]; break; case 14: sarRatio = [4, 3]; break; case 15: sarRatio = [3, 2]; break; case 16: sarRatio = [2, 1]; break; case 255: { sarRatio = [this.readUByte() << 8 | this.readUByte(), this.readUByte() << 8 | this.readUByte()]; break; } } if(sarRatio) { sarScale = sarRatio[0] / sarRatio[1]; } } } return { width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale), height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset) }; } }, { key: 'readSliceType', value: function readSliceType() { this.readUByte(); this.readUEG(); return this.readUEG(); } }]); return ExpGolomb; }(); exports.default = ExpGolomb; }, { "../utils/logger": 17 }], 6: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // 云游戏生成模块 var _errors = require('../errors'); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _expGolomb = require('./exp-golomb'); var _expGolomb2 = _interopRequireDefault(_expGolomb); var _eventHandler = require('../event-handler'); var _eventHandler2 = _interopRequireDefault(_eventHandler); var _mp4Remuxer = require('../remux/mp4-remuxer'); var _mp4Remuxer2 = _interopRequireDefault(_mp4Remuxer); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if(!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if(typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if(superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** */ var h264Demuxer = function(_EventHandler) { _inherits(h264Demuxer, _EventHandler); function h264Demuxer(wfs) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; _classCallCheck(this, h264Demuxer); var _this = _possibleConstructorReturn(this, (h264Demuxer.__proto__ || Object.getPrototypeOf(h264Demuxer)).call(this, wfs, _events2.default.H264_DATA_PARSING)); _this.config = _this.wfs.config || config; _this.wfs = wfs; _this.id = 'main'; var typeSupported = { mp4: MediaSource.isTypeSupported('video/mp4') //, // mp2t : wfs.config.enableMP2TPassThrough && MediaSource.isTypeSupported('video/mp2t') }; _this.remuxer = new _mp4Remuxer2.default(_this.wfs, _this.id, _this.config); _this.contiguous = true; _this.timeOffset = 1; _this.sn = 0; _this.TIMESCALE = 90000; _this.timestamp = 0; _this.scaleFactor = _this.TIMESCALE / 1000; _this.H264_TIMEBASE = 2000; _this._avcTrack = { container: 'video/mp4', type: 'video', id: 1, sequenceNumber: 0, //this._avcTrack = {container : 'video/mp4', type: 'video', id :1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0, dropped: 0, count: 0 }; _this.browserType = 0; if(navigator.userAgent.toLowerCase().indexOf('firefox') !== -1) { _this.browserType = 1; } return _this; } _createClass(h264Demuxer, [{ key: 'destroy', value: function destroy() { _eventHandler2.default.prototype.destroy.call(this); } }, { key: 'getTimestampM', value: function getTimestampM() { this.timestamp += this.H264_TIMEBASE; return this.timestamp; } }, { key: 'onH264DataParsing', value: function onH264DataParsing(event) { this._parseAVCTrack(event.data); if(this.browserType === 1) { // Firefox this.remuxer.pushVideo(0, this.sn, this._avcTrack, this.timeOffset, this.contiguous); this.sn += 1; } else { this.remuxer.pushVideo(0, this.sn, this._avcTrack, this.timeOffset, this.contiguous); this.sn += 1; } } }, { key: '_parseAVCTrack', value: function _parseAVCTrack(array) { var _this2 = this; var track = this._avcTrack, samples = track.samples, units = this._parseAVCNALu(array), units2 = [], debug = false, key = false, length = 0, expGolombDecoder, avcSample, push, i; var debugString = ''; var pushAccesUnit = function() { if(units2.length) { if(!this.config.forceKeyFrameOnDiscontinuity || key === true || track.sps && (samples.length || this.contiguous)) { var tss = this.getTimestampM(); avcSample = { units: { units: units2, length: length }, pts: tss, dts: tss, key: key }; samples.push(avcSample); track.len += length; track.nbNalu += units2.length; } else { track.dropped++; } units2 = []; length = 0; } }.bind(this); units.forEach(function(unit) { switch(unit.type) { //NDR case 1: push = true; if(debug) { debugString += 'NDR '; } break; //IDR case 5: push = true; if(debug) { debugString += 'IDR '; } key = true; break; //SEI case 6: unit.data = _this2.discardEPB(unit.data); expGolombDecoder = new _expGolomb2.default(unit.data); // skip frameType expGolombDecoder.readUByte(); break; //SPS case 7: push = false; if(debug) { debugString += 'SPS '; } if(!track.sps) { expGolombDecoder = new _expGolomb2.default(unit.data); var config = expGolombDecoder.readSPS(); track.width = config.width; track.height = config.height; track.sps = [unit.data]; track.duration = 0; var codecarray = unit.data.subarray(1, 4); var codecstring = 'avc1.'; for(i = 0; i < 3; i++) { var h = codecarray[i].toString(16); if(h.length < 2) { h = '0' + h; } codecstring += h; } track.codec = codecstring; _this2.wfs.trigger(_events2.default.BUFFER_RESET, { mimeType: track.codec }); push = true; } break; //PPS case 8: push = false; if(debug) { debugString += 'PPS '; } if(!track.pps) { track.pps = [unit.data]; push = true; } break; case 9: push = false; if(debug) { debugString += 'AUD '; } pushAccesUnit(); break; default: push = false; debugString += 'unknown NAL ' + unit.type + ' '; break; } if(push) { units2.push(unit); length += unit.data.byteLength; } }); if(debug || debugString.length) { logger.log(debugString); } pushAccesUnit(); } }, { key: '_parseAVCNALu', value: function _parseAVCNALu(array) { var i = 0, len = array.byteLength, value, overflow, state = 0; //state = this.avcNaluState; var units = [], unit, unitType, lastUnitStart, lastUnitType; while(i < len) { value = array[i++]; // 绑定3或4字节起始代码 (00 00 01 OR 00 00 00 01) switch(state) { case 0: if(value === 0) { state = 1; } break; case 1: if(value === 0) { state = 2; } else { state = 0; } break; case 2: case 3: if(value === 0) { state = 3; } else if(value === 1 && i < len) { unitType = array[i] & 0x1f; if(lastUnitStart) { unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType }; units.push(unit); } else {} lastUnitStart = i; lastUnitType = unitType; state = 0; } else { state = 0; } break; default: break; } } if(lastUnitStart) { unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state }; units.push(unit); } return units; } /** * 从RBSP中删除仿真保护字节 */ }, { key: 'discardEPB', value: function discardEPB(data) { var length = data.byteLength, EPBPositions = [], i = 1, newLength, newData; // 查找所有“仿真保护字节”` while(i < length - 2) { if(data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { EPBPositions.push(i + 2); i += 2; } else { i++; } } if(EPBPositions.length === 0) { return data; } newLength = length - EPBPositions.length; newData = new Uint8Array(newLength); var sourceIndex = 0; for(i = 0; i < newLength; sourceIndex++, i++) { if(sourceIndex === EPBPositions[0]) { // 跳过此字节 sourceIndex++; // 删除此位置索引 EPBPositions.shift(); } newData[i] = data[sourceIndex]; } return newData; } }]); return h264Demuxer; }(_eventHandler2.default); exports.default = h264Demuxer; }, { "../errors": 7, "../event-handler": 8, "../events": 9, "../remux/mp4-remuxer": 16, "./exp-golomb": 5 }], 7: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var ErrorTypes = exports.ErrorTypes = { // 网络错误的标识符(加载错误/超时…) NETWORK_ERROR: 'networkError', // 媒体错误的标识符(视频/分析/媒体源错误) MEDIA_ERROR: 'mediaError', // 所有其他错误的标识符 OTHER_ERROR: 'otherError' }; var ErrorDetails = exports.ErrorDetails = { // 清单加载错误的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} MANIFEST_LOAD_ERROR: 'manifestLoadError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} MANIFEST_PARSING_ERROR: 'manifestParsingError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} LEVEL_LOAD_ERROR: 'levelLoadError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} LEVEL_SWITCH_ERROR: 'levelSwitchError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} FRAG_LOAD_ERROR: 'fragLoadError', // 片段循环加载错误的标识符-数据:{frag:fragment object} FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError', // 片段加载超时错误的标识符-数据:{frag:fragment object} FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut', // 片段解密错误事件的标识符-数据:分析错误描述 FRAG_DECRYPT_ERROR: 'fragDecryptError', // 片段解密错误事件的标识符-数据:分析错误描述 FRAG_PARSING_ERROR: 'fragParsingError', // 清单加载超时的标识符-数据:{url:faulty url,响应:{code:error code,text:error text} KEY_LOAD_ERROR: 'keyLoadError', // 解密密钥加载超时错误的标识符-数据:{frag:fragment object} KEY_LOAD_TIMEOUT: 'keyLoadTimeOut', // 向MediaSource添加sourceBuffer时发生异常时触发-数据:{err:exception,mimeType:mimeType} BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError', // 缓冲区追加错误的标识符-数据:追加错误描述 BUFFER_APPEND_ERROR: 'bufferAppendError', // 缓冲区追加错误事件的标识符-数据:追加错误描述 BUFFER_APPENDING_ERROR: 'bufferAppendingError', //缓冲区暂停错误事件的标识符 BUFFER_STALLED_ERROR: 'bufferStalledError', // 缓冲区暂停错误事件的标识符 BUFFER_FULL_ERROR: 'bufferFullError', // 缓冲区查找孔事件的标识符 BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', // 发生在内部的内部异常的标识符hls.js公司处理事件时 INTERNAL_EXCEPTION: 'internalException' }; }, {}], 8: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* * *事件处理链中的所有对象都应继承自该类 class 定义 * */ var _events = require('./events'); var _events2 = _interopRequireDefault(_events); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var EventHandler = function() { function EventHandler(wfs) { _classCallCheck(this, EventHandler); this.wfs = wfs; this.onEvent = this.onEvent.bind(this); for(var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { events[_key - 1] = arguments[_key]; } this.handledEvents = events; this.useGenericHandler = true; this.registerListeners(); } _createClass(EventHandler, [{ key: 'destroy', value: function destroy() { this.unregisterListeners(); } }, { key: 'isEventHandler', value: function isEventHandler() { return _typeof(this.handledEvents) === 'object' && this.handledEvents.length && typeof this.onEvent === 'function'; } }, { key: 'registerListeners', value: function registerListeners() { if(this.isEventHandler()) { this.handledEvents.forEach(function(event) { if(event === 'wfsEventGeneric') { //throw new Error('Forbidden event name: ' + event); } this.wfs.on(event, this.onEvent); }.bind(this)); } } }, { key: 'unregisterListeners', value: function unregisterListeners() { if(this.isEventHandler()) { this.handledEvents.forEach(function(event) { this.wfs.off(event, this.onEvent); }.bind(this)); } } /** *参数:事件(字符串)、数据(任意) arguments: event (string), data (any) huangfei */ }, { key: 'onEvent', value: function onEvent(event, data) { this.onEventGeneric(event, data); } }, { key: 'onEventGeneric', value: function onEventGeneric(event, data) { var eventToFunction = function eventToFunction(event, data) { var funcName = 'on' + event.replace('wfs', ''); if(typeof this[funcName] !== 'function') { //throw new Error(`Event ${event} has no generic handler in this ${this.constructor.name} class (tried ${funcName})`); } return this[funcName].bind(this, data); }; try { eventToFunction.call(this, event, data).call(); } catch(err) { console.log('internal error happened while processing ' + event + ':' + err.message); // this.hls.trigger(Event.ERROR, {type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event : event, err : err}); } } }]); return EventHandler; }(); exports.default = EventHandler; }, { "./events": 9 }], 9: [function(require, module, exports) { 'use strict'; module.exports = { MEDIA_ATTACHING: 'wfsMediaAttaching', MEDIA_ATTACHED: 'wfsMediaAttached', FRAG_LOADING: 'wfsFragLoading', BUFFER_CREATED: 'wfsBufferCreated', BUFFER_APPENDING: 'wfsBufferAppending', BUFFER_RESET: 'wfsBufferReset', FRAG_PARSING_DATA: 'wfsFragParsingData', FRAG_PARSING_INIT_SEGMENT: 'wfsFragParsingInitSegment', //------------------------------------------ H264_DATA_PARSING: 'wfsH264DataParsing', //------------------------------------------ WEBSOCKET_ATTACHED: 'wfsWebsocketAttached', WEBSOCKET_ATTACHING: 'wfsWebsocketAttaching', WEBSOCKET_DATA_UPLOADING: 'wfsWebsocketDataUploading', WEBSOCKET_MESSAGE_SENDING: 'wfsWebsocketMessageSending', //------------------------------------------ FILE_HEAD_LOADING: 'wfsFileHeadLoading', FILE_HEAD_LOADED: 'wfsFileHeadLoaded', FILE_DATA_LOADING: 'wfsFileDataLoading', FILE_DATA_LOADED: 'wfsFileDataLoaded', FILE_PARSING_DATA: 'wfsFileParsingData' //------------------------------------------ }; }, {}], 10: [function(require, module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * AAC helper */ var AAC = function() { function AAC() { _classCallCheck(this, AAC); } _createClass(AAC, null, [{ key: "getSilentFrame", value: function getSilentFrame(channelCount) { if(channelCount === 1) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); } else if(channelCount === 2) { return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); } else if(channelCount === 3) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); } else if(channelCount === 4) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); } else if(channelCount === 5) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); } else if(channelCount === 6) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]); } return null; } }]); return AAC; }(); exports.default = AAC; }, {}], 11: [function(require, module, exports) { 'use strict'; //这主要是为了支持es6模块导出 //像我们在node/commonjs中习惯的那样导出函数 module.exports = require('./wfs.js').default; }, { "./wfs.js": 20 }], 12: [function(require, module, exports) { (function(process, global) { 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; // jslint bitwise: true (function() { 'use strict'; var root = (typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' ? window : {}; var NODE_JS = !root.JS_CRC_NO_NODE_JS && (typeof process === 'undefined' ? 'undefined' : _typeof(process)) === 'object' && process.versions && process.versions.node; if(NODE_JS) { root = global; } var COMMON_JS = !root.JS_CRC_NO_COMMON_JS && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object' && module.exports; var AMD = typeof define === 'function' && define.amd; var ARRAY_BUFFER = !root.JS_CRC_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; var HEX_CHARS = '0123456789abcdef'.split(''); var Modules = [{ name: 'crc32', polynom: 0xEDB88320, initValue: -1, bytes: 4 }, { name: 'crc16', polynom: 0xA001, initValue: 0, bytes: 2 }]; var i, j, k, b; for(i = 0; i < Modules.length; ++i) { var m = Modules[i]; m.method = function(m) { return function(message) { return crc(message, m); }; }(m); m.table = []; for(j = 0; j < 256; ++j) { b = j; for(k = 0; k < 8; ++k) { b = b & 1 ? m.polynom ^ b >>> 1 : b >>> 1; } m.table[j] = b >>> 0; } } var crc = function crc(message, module) { var notString = typeof message !== 'string'; if(notString && ARRAY_BUFFER && message instanceof ArrayBuffer) { message = new Uint8Array(message); } var crc = module.initValue, code, i, length = message.length, table = module.table; if(notString) { for(i = 0; i < length; ++i) { crc = table[(crc ^ message[i]) & 0xFF] ^ crc >>> 8; } } else { // 解码计算 小程序和云游戏 0001 二进制 TDDE ?????? for(i = 0; i < length; ++i) { code = message.charCodeAt(i); if(code < 0x80) { crc = table[(crc ^ code) & 0xFF] ^ crc >>> 8; } else if(code < 0x800) { crc = table[(crc ^ (0xc0 | code >> 6)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code & 0x3f)) & 0xFF] ^ crc >>> 8; } else if(code < 0xd800 || code >= 0xe000) { crc = table[(crc ^ (0xe0 | code >> 12)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code >> 6 & 0x3f)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code & 0x3f)) & 0xFF] ^ crc >>> 8; } else { code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++i) & 0x3ff); crc = table[(crc ^ (0xf0 | code >> 18)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code >> 12 & 0x3f)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code >> 6 & 0x3f)) & 0xFF] ^ crc >>> 8; crc = table[(crc ^ (0x80 | code & 0x3f)) & 0xFF] ^ crc >>> 8; } } } crc ^= module.initValue; var hex = ''; if(module.bytes > 2) { hex += HEX_CHARS[crc >> 28 & 0x0F] + HEX_CHARS[crc >> 24 & 0x0F] + HEX_CHARS[crc >> 20 & 0x0F] + HEX_CHARS[crc >> 16 & 0x0F]; } hex += HEX_CHARS[crc >> 12 & 0x0F] + HEX_CHARS[crc >> 8 & 0x0F] + HEX_CHARS[crc >> 4 & 0x0F] + HEX_CHARS[crc & 0x0F]; return hex; }; var exports = {}; for(i = 0; i < Modules.length; ++i) { var m = Modules[i]; exports[m.name] = m.method; } if(COMMON_JS) { module.exports = exports; } else { for(i = 0; i < Modules.length; ++i) { var m = Modules[i]; root[m.name] = m.method; } if(AMD) { define(function() { return exports; }); } } })(); }).call(this, require('_process'), typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) }, { "_process": 2 }], 13: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _eventHandler = require('../event-handler'); var _eventHandler2 = _interopRequireDefault(_eventHandler); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if(!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if(typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if(superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* * 文件加载器 Loader */ var FileLoader = function(_EventHandler) { _inherits(FileLoader, _EventHandler); function FileLoader(wfs) { _classCallCheck(this, FileLoader); var _this = _possibleConstructorReturn(this, (FileLoader.__proto__ || Object.getPrototypeOf(FileLoader)).call(this, wfs, _events2.default.FRAG_LOADING, _events2.default.FILE_HEAD_LOADING, _events2.default.FILE_DATA_LOADING)); _this.loaders = {}; return _this; } _createClass(FileLoader, [{ key: 'destroy', value: function destroy() { for(var loaderName in this.loaders) { var loader = this.loaders[loaderName]; if(loader) { loader.destroy(); } } this.loaders = {}; _eventHandler2.default.prototype.destroy.call(this); } }, { key: 'onFileHeadLoading', value: function onFileHeadLoading(data) { var config = this.wfs.config; var loader = new config.loader(config); var loaderContext = void 0, loaderConfig = void 0, loaderCallbacks = void 0; loaderContext = { url: config.fmp4FileUrl }; loaderConfig = { maxRetry: 0, retryDelay: 0 }; loaderCallbacks = { onSuccess: this.fileloadheadsuccess.bind(this) }; loader.loadHead(loaderContext, loaderConfig, loaderCallbacks); } }, { key: 'fileloadheadsuccess', value: function fileloadheadsuccess(response) { this.wfs.trigger(_events2.default.FILE_HEAD_LOADED, { size: response }); } }, { key: 'onFileDataLoading', value: function onFileDataLoading(data) { var config = this.wfs.config; var loader = new config.loader(config); var loaderContext = void 0, loaderConfig = void 0, loaderCallbacks = void 0; loaderContext = { url: config.fmp4FileUrl, responseType: 'arraybuffer', progressData: false }; var start = data.fileStart, end = data.fileEnd; if(!isNaN(start) && !isNaN(end)) { loaderContext.rangeStart = start; loaderContext.rangeEnd = end; } loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: 0, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; loaderCallbacks = { onSuccess: this.fileloaddatasuccess.bind(this) }; loader.load(loaderContext, loaderConfig, loaderCallbacks); } }, { key: 'fileloaddatasuccess', value: function fileloaddatasuccess(response, stats, context) { this.wfs.trigger(_events2.default.FILE_DATA_LOADED, { payload: response.data, stats: stats }); } }, { key: 'loaderror', value: function loaderror(response, context) { var loader = context.loader; if(loader) { loader.abort(); } this.loaders[context.type] = undefined; } }, { key: 'loadtimeout', value: function loadtimeout(stats, context) { var loader = context.loader; if(loader) { loader.abort(); } this.loaders[context.type] = undefined; } }, { key: 'loadprogress', value: function loadprogress(stats, context, data) { var frag = context.frag; frag.loaded = stats.loaded; } }]); return FileLoader; }(_eventHandler2.default); exports.default = FileLoader; }, { "../event-handler": 8, "../events": 9 }], 14: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _eventHandler = require('../event-handler'); var _eventHandler2 = _interopRequireDefault(_eventHandler); var _h264Demuxer = require('../demux/h264-demuxer'); var _h264Demuxer2 = _interopRequireDefault(_h264Demuxer); var _crc = require('./crc.js'); var _crc2 = _interopRequireDefault(_crc); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if(!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if(typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if(superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* * Websocket Loader ws加载 根据后端需要的对应参数 */ var WebsocketLoader = function(_EventHandler) { _inherits(WebsocketLoader, _EventHandler); function WebsocketLoader(wfs) { _classCallCheck(this, WebsocketLoader); var _this = _possibleConstructorReturn(this, (WebsocketLoader.__proto__ || Object.getPrototypeOf(WebsocketLoader)).call(this, wfs, _events2.default.WEBSOCKET_ATTACHING, _events2.default.WEBSOCKET_DATA_UPLOADING, _events2.default.WEBSOCKET_MESSAGE_SENDING)); _this.buf = null; _this.h264Demuxer = new _h264Demuxer2.default(wfs); _this.mediaType = undefined; _this.channelName = undefined; return _this; } _createClass(WebsocketLoader, [{ key: 'destroy', value: function destroy() { _eventHandler2.default.prototype.destroy.call(this); } }, { key: 'onWebsocketAttaching', value: function onWebsocketAttaching(data) { this.mediaType = data.mediaType; this.channelName = data.channelName; if(data.websocket instanceof WebSocket) { this.client = data.websocket; this.client.onopen = this.initSocketClient.bind(this); this.client.onclose = function(e) { console.log('Websocket Disconnected!'); }; } } }, { key: 'initSocketClient', value: function initSocketClient(client) { this.client.binaryType = 'arraybuffer'; this.client.onmessage = this.receiveSocketMessage.bind(this); } }, { key: 'receiveSocketMessage', value: function receiveSocketMessage(event) { this.buf = new Uint8Array(event.data); var copy = new Uint8Array(this.buf); // console.log(">>>>黄飞",this.mediaType) if(this.buf[0] == 0) { if(this.mediaType === 'FMp4') { this.wfs.trigger(_events2.default.WEBSOCKET_ATTACHED, { payload: copy }); } this.wfs.trigger(_events2.default.H264_DATA_PARSING, { data: copy }); } } }, { key: 'onWebsocketDataUploading', value: function onWebsocketDataUploading(event) { this.client.send(event.data); } }, { key: 'onWebsocketMessageSending', value: function onWebsocketMessageSending(event) { this.client.send(JSON.stringify({ t: event.commandType, c: event.channelName, v: event.commandValue })); } }]); return WebsocketLoader; }(_eventHandler2.default); exports.default = WebsocketLoader; }, { "../demux/h264-demuxer": 6, "../event-handler": 8, "../events": 9, "./crc.js": 12 }], 15: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * 生成MP4框 HDDT 没有用 */ //import Hex from '../utils/hex'; var MP4 = function() { function MP4() { _classCallCheck(this, MP4); } _createClass(MP4, null, [{ key: 'init', value: function init() { MP4.types = { avc1: [], // codingname 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: [] }; var i; for(i in MP4.types) { if(MP4.types.hasOwnProperty(i)) { MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; } } // 为了安卓机不卡和延时的处理 对应的注释 是为了看清类型 var videoHdlr = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' ]); var audioHdlr = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun' 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' ]); MP4.HDLR_TYPES = { 'video': videoHdlr, 'audio': audioHdlr }; var dref = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x01, // entry_count 0x00, 0x00, 0x00, 0x0c, // entry_size 0x75, 0x72, 0x6c, 0x20, // 'url' type 0x00, // version 0 0x00, 0x00, 0x01 // entry_flags ]); var stco = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00 // entry_count ]); MP4.STTS = MP4.STSC = MP4.STCO = stco; MP4.STSZ = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // sample_size 0x00, 0x00, 0x00, 0x00 ]); MP4.VMHD = new Uint8Array([0x00, // version 0x00, 0x00, 0x01, // flags 0x00, 0x00, // graphicsmode 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor ]); MP4.SMHD = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, // balance 0x00, 0x00 // reserved ]); MP4.STSD = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x01 ]); // entry_count var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 这个解码方案 是为了可以支持 高等画质 high 云游戏 var minorVersion = new Uint8Array([0, 0, 0, 1]); MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); } }, { key: 'box', value: function box(type) { var payload = Array.prototype.slice.call(arguments, 1), //box 是分配帧数 size = 8, i = payload.length, len = i, result; // c计算我们需要分配的总大小 while(i--) { size += payload[i].byteLength; } result = new Uint8Array(size); result[0] = size >> 24 & 0xff; result[1] = size >> 16 & 0xff; result[2] = size >> 8 & 0xff; result[3] = size & 0xff; result.set(type, 4); // 将有效负载复制到结果中 for(i = 0, size = 8; i < len; i++) { // 复制负载[i]数组@offset size result.set(payload[i], size); size += payload[i].byteLength; } return result; } }, { key: 'hdlr', value: function hdlr(type) { return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); } }, { key: 'mdat', value: function mdat(data) { // console.log( "mdat==> ",data.length ); return MP4.box(MP4.types.mdat, data); } }, { key: 'mdhd', value: function mdhd(timescale, duration) { duration *= timescale; return MP4.box(MP4.types.mdhd, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x02, // creation_time 创作时间 0x00, 0x00, 0x00, 0x03, // modification_time 修改时间 timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration 0x55, 0xc4, // 'und' language (undetermined) 0x00, 0x00 ])); } }, { key: 'mdia', value: function mdia(track) { return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); } }, { key: 'mfhd', value: function mfhd(sequenceNumber) { return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF ]) // sequence_number 序号 ); } }, { key: 'minf', value: function minf(track) { if(track.type === 'audio') { return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); } else { return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); } } }, { key: 'moof', value: function moof(sn, baseMediaDecodeTime, track) { return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); } /** * @param tracks... (optional) {array} the tracks associated with this movie */ }, { key: 'moov', value: function moov(tracks) { var i = tracks.length, boxes = []; while(i--) { boxes[i] = MP4.trak(tracks[i]); } return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks))); } }, { key: 'mvex', value: function mvex(tracks) { var i = tracks.length, boxes = []; while(i--) { boxes[i] = MP4.trex(tracks[i]); } return MP4.box.apply(null, [MP4.types.mvex].concat(boxes)); } }, { key: 'mvhd', value: function mvhd(timescale, duration) { //duration*=timescale; duration = 0; var bytes = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x01, // creation_time 0x00, 0x00, 0x00, 0x02, // modification_time timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale duration >> 24 & 0xFF, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration 0x00, 0x01, 0x00, 0x00, // 1.0 rate 0x01, 0x00, // 1.0 volume 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined 0xff, 0xff, 0xff, 0xff // next_track_ID ]); return MP4.box(MP4.types.mvhd, bytes); } }, { key: 'sdtp', value: function sdtp(track) { var samples = track.samples || [], bytes = new Uint8Array(4 + samples.length), flags, i; // 将整框标题(4字节)全部保留为零 》》》 比如是这样的 可以打印(4 bytes) // 编写示例表 for(i = 0; i < samples.length; i++) { flags = samples[i].flags; bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy; } return MP4.box(MP4.types.sdtp, bytes); } }, { key: 'stbl', value: function stbl(track) { return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO)); } }, { key: 'avc1', value: function avc1(track) { var sps = [], pps = [], i, data, len; // 组装SPSs for(i = 0; i < track.sps.length; i++) { data = track.sps[i]; len = data.byteLength; sps.push(len >>> 8 & 0xFF); sps.push(len & 0xFF); sps = sps.concat(Array.prototype.slice.call(data)); // SPS } // 装配PPS for(i = 0; i < track.pps.length; i++) { data = track.pps[i]; len = data.byteLength; pps.push(len >>> 8 & 0xFF); pps.push(len & 0xFF); pps = pps.concat(Array.prototype.slice.call(data)); } var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version sps[3], // profile sps[4], // profile compat sps[5], // level 0xfc | 3, // 长度SizeMinusOne,硬编码为4字节 4 bytes 0xE0 | track.sps.length // 3bit保留(111)+numofsequenceparameters集 ].concat(sps).concat([track.pps.length // 对应的数字 numOfPictureParameterSets ]).concat(pps))), // "PPS" width = track.width, height = track.height; //console.log('avcc:' + Hex.hexDump(avcc)); return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // data_reference_index 0x00, 0x00, // pre_defined 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined width >> 8 & 0xFF, width & 0xff, // width height >> 8 & 0xFF, height & 0xff, // height 0x00, 0x48, 0x00, 0x00, // horizresolution 0x00, 0x48, 0x00, 0x00, // vertresolution 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // frame_count 0x12, 0x6a, 0x65, 0x66, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x2f, 0x2f, 0x2f, 0x67, 0x77, 0x66, 0x73, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname 0x00, 0x18, // depth = 24 0x11, 0x11 ]), // pre_defined = -1 avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate 0x00, 0x2d, 0xc6, 0xc0 ])) // avgBitrate ); } }, { key: 'esds', value: function esds(track) { var configlen = track.config.length; return new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x03, // descriptor_type 0x17 + configlen, // length 0x00, 0x01, //es_id 0x00, // stream_priority 0x04, // descriptor_type 0x0f + configlen, // length 0x40, //codec : mpeg4_audio 0x15, // stream_type 0x00, 0x00, 0x00, // buffer_size 0x00, 0x00, 0x00, 0x00, // maxBitrate 0x00, 0x00, 0x00, 0x00, // avgBitrate 0x05 // descriptor_type ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor } }, { key: 'mp4a', value: function mp4a(track) { var audiosamplerate = track.audiosamplerate; // 下面的注释是 对应每个函数和对象 return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // data_reference_index 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, track.channelCount, // channelcount 0x00, 0x10, // sampleSize:16bits 0x00, 0x00, 0x00, 0x00, // reserved2 audiosamplerate >> 8 & 0xFF, audiosamplerate & 0xff, // 0x00, 0x00 ]), MP4.box(MP4.types.esds, MP4.esds(track))); } }, { key: 'stsd', value: function stsd(track) { if(track.type === 'audio') { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); } else { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); } } }, { key: 'tkhd', value: function tkhd(track) { var id = track.id, duration = track.duration * track.timescale, width = track.width, height = track.height; // console.log( "tkhd==> ",track.id, track.duration, track.timescale, width,height ); // 计算width和height 可打印 return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x07, // flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID 0x00, 0x00, 0x00, 0x00, // reserved duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF, // duration 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, // layer 0x00, 0x00, // alternate_group 0x00, 0x00, // non-audio track volume 0x00, 0x00, // reserved 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height ])); } }, { key: 'traf', value: function traf(track, baseMediaDecodeTime) { var sampleDependencyTable = MP4.sdtp(track), id = track.id; // console.log( "traf==> ",id ,baseMediaDecodeTime); // 每一帧的 return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF ]) // track_ID ), MP4.box(MP4.types.tfdt, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags baseMediaDecodeTime >> 24, baseMediaDecodeTime >> 16 & 0XFF, baseMediaDecodeTime >> 8 & 0XFF, baseMediaDecodeTime & 0xFF ]) // baseMediaDecodeTime ), MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd 16 + // tfdt 8 + // traf header 16 + // mfhd 8 + // moof header 8), // mdat header sampleDependencyTable); } }, { key: 'trak', value: function trak(track) { track.duration = track.duration || 0xffffffff; return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); } }, { key: 'trex', value: function trex(track) { var id = track.id; return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID 0x00, 0x00, 0x00, 0x01, //索引 default_sample_description_index 0x00, 0x00, 0x00, 0x00, //持续时间 default_sample_duration 0x00, 0x00, 0x00, 0x00, //大小 default_sample_size 0x00, 0x01, 0x00, 0x01 // 帧数default_sample_flags ])); } }, { key: 'trun', value: function trun(track, offset) { var samples = track.samples || [], len = samples.length, arraylen = 12 + 16 * len, array = new Uint8Array(arraylen), i, sample, duration, size, flags, cts; //sample = samples[0]; // console.log( "trun==> ",sample.duration, sample.cts ,sample.size,len ); offset += 8 + arraylen; array.set([0x00, // version 0 0x00, 0x0f, 0x01, // flags len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset ], 0); for(i = 0; i < len; i++) { sample = samples[i]; duration = sample.duration; size = sample.size; flags = sample.flags; cts = sample.cts; array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset ], 12 + 16 * i); } return MP4.box(MP4.types.trun, array); } }, { key: 'initSegment', value: function initSegment(tracks) { if(!MP4.types) { MP4.init(); } var movie = MP4.moov(tracks), result; result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength); result.set(MP4.FTYP); result.set(movie, MP4.FTYP.byteLength); return result; } }]); return MP4; }(); exports.default = MP4; }, {}], 16: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** * fMP4 remuxer */ var _aac = require('../helper/aac'); var _aac2 = _interopRequireDefault(_aac); var _events = require('../events'); var _events2 = _interopRequireDefault(_events); var _logger = require('../utils/logger'); var _mp4Generator = require('../remux/mp4-generator'); var _mp4Generator2 = _interopRequireDefault(_mp4Generator); var _errors = require('../errors'); require('../utils/polyfill'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var MP4Remuxer = function() { function MP4Remuxer(observer, id, config) { _classCallCheck(this, MP4Remuxer); this.observer = observer; this.id = id; this.config = config; this.ISGenerated = false; this.PES2MP4SCALEFACTOR = 4; this.PES_TIMESCALE = 90000; this.MP4_TIMESCALE = this.PES_TIMESCALE / this.PES2MP4SCALEFACTOR; this.nextAvcDts = 90300; this.H264_TIMEBASE = 2000; } _createClass(MP4Remuxer, [{ key: 'destroy', value: function destroy() {} }, { key: 'insertDiscontinuity', value: function insertDiscontinuity() { this._initPTS = this._initDTS = undefined; } }, { key: 'switchLevel', value: function switchLevel() { this.ISGenerated = false; } }, { key: 'pushVideo', value: function pushVideo(level, sn, videoTrack, timeOffset, contiguous) { this.level = level; this.sn = sn; var videoData = void 0; // generate Init Segment if needed if(!this.ISGenerated) { this.generateVideoIS(videoTrack, timeOffset); } if(this.ISGenerated) { // if (videoTrack.samples.length) { this.remuxVideo_2(videoTrack, timeOffset, contiguous); // } } } }, { key: 'remuxVideo_2', value: function remuxVideo_2(track, timeOffset, contiguous, audioTrackLength) { var offset = 8, pesTimeScale = this.PES_TIMESCALE, pes2mp4ScaleFactor = this.PES2MP4SCALEFACTOR, mp4SampleDuration, mdat, moof, firstPTS, firstDTS, nextDTS, inputSamples = track.samples, outputSamples = []; /*连接视频数据并就地构造mdat需要另外8个字节来填充长度和mpdat类型)*/ mdat = new Uint8Array(track.len + 4 * track.nbNalu + 8); var view = new DataView(mdat.buffer); view.setUint32(0, mdat.byteLength); mdat.set(_mp4Generator2.default.types.mdat, 4); var sampleDuration = 0; var ptsnorm = void 0, dtsnorm = void 0, mp4Sample = void 0, lastDTS = void 0; for(var i = 0; i < inputSamples.length; i++) { var avcSample = inputSamples[i], mp4SampleLength = 0, compositionTimeOffset = void 0; // 将NALU比特流转换为MP4格式(在NALU前面加上大小字段) 交接的人 请了解比特流的流程 》》 huangfei while(avcSample.units.units.length) { var unit = avcSample.units.units.shift(); view.setUint32(offset, unit.data.byteLength); offset += 4; mdat.set(unit.data, offset); offset += unit.data.byteLength; mp4SampleLength += 4 + unit.data.byteLength; } var pts = avcSample.pts - this._initPTS; var dts = avcSample.dts - this._initDTS; dts = Math.min(pts, dts); if(lastDTS !== undefined) { ptsnorm = this._PTSNormalize(pts, lastDTS); dtsnorm = this._PTSNormalize(dts, lastDTS); sampleDuration = dtsnorm - lastDTS; if(sampleDuration <= 0) { _logger.logger.log('invalid sample duration at PTS/DTS: ' + avcSample.pts + '/' + avcSample.dts + '|dts norm: ' + dtsnorm + '|lastDTS: ' + lastDTS + ':' + sampleDuration); sampleDuration = 1; } } else { var nextAvcDts = this.nextAvcDts, delta; ptsnorm = this._PTSNormalize(pts, nextAvcDts); dtsnorm = this._PTSNormalize(dts, nextAvcDts); if(nextAvcDts) { delta = Math.round(dtsnorm - nextAvcDts); if( /*contiguous ||*/ Math.abs(delta) < 600) { if(delta) { if(delta > 1) { _logger.logger.log('AVC:' + delta + ' ms hole between fragments detected,filling it'); } else if(delta < -1) { _logger.logger.log('AVC:' + -delta + ' ms overlapping between fragments detected'); } dtsnorm = nextAvcDts; ptsnorm = Math.max(ptsnorm - delta, dtsnorm); _logger.logger.log('Video/PTS/DTS adjusted: ' + ptsnorm + '/' + dtsnorm + ',delta:' + delta); } } } this.firstPTS = Math.max(0, ptsnorm); this.firstDTS = Math.max(0, dtsnorm); sampleDuration = 0.03; } outputSamples.push({ size: mp4SampleLength, duration: this.H264_TIMEBASE, cts: 0, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: avcSample.key ? 2 : 1, isNonSync: avcSample.key ? 0 : 1 } }); lastDTS = dtsnorm; } var lastSampleDuration = 0; if(outputSamples.length >= 2) { lastSampleDuration = outputSamples[outputSamples.length - 2].duration; outputSamples[0].duration = lastSampleDuration; } this.nextAvcDts = dtsnorm + lastSampleDuration; var dropped = track.dropped; track.len = 0; track.nbNalu = 0; track.dropped = 0; if(outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { var flags = outputSamples[0].flags; flags.dependsOn = 2; flags.isNonSync = 0; } track.samples = outputSamples; moof = _mp4Generator2.default.moof(track.sequenceNumber++, dtsnorm, track); track.samples = []; var data = { id: this.id, level: this.level, sn: this.sn, data1: moof, data2: mdat, startPTS: ptsnorm, endPTS: ptsnorm, startDTS: dtsnorm, endDTS: dtsnorm, type: 'video', nb: outputSamples.length, dropped: dropped }; this.observer.trigger(_events2.default.FRAG_PARSING_DATA, data); return data; } }, { key: 'generateVideoIS', value: function generateVideoIS(videoTrack, timeOffset) { var observer = this.observer, videoSamples = videoTrack.samples, pesTimeScale = this.PES_TIMESCALE, tracks = {}, data = { id: this.id, level: this.level, sn: this.sn, tracks: tracks, unique: false }, computePTSDTS = this._initPTS === undefined, initPTS, initDTS; if(computePTSDTS) { initPTS = initDTS = Infinity; } if(videoTrack.sps && videoTrack.pps && videoSamples.length) { videoTrack.timescale = 90000; //this.MP4_TIMESCALE; tracks.video = { container: 'video/mp4', codec: videoTrack.codec, initSegment: _mp4Generator2.default.initSegment([videoTrack]), metadata: { width: videoTrack.width, height: videoTrack.height } }; if(computePTSDTS) { initPTS = Math.min(initPTS, videoSamples[0].pts - this.H264_TIMEBASE); initDTS = Math.min(initDTS, videoSamples[0].dts - this.H264_TIMEBASE); } } if(Object.keys(tracks).length) { observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, data); this.ISGenerated = true; if(computePTSDTS) { this._initPTS = initPTS; this._initDTS = initDTS; } } else { console.log("generateVideoIS ERROR==> ", _errors.ErrorTypes.MEDIA_ERROR); } } }, { key: 'remux', value: function remux(level, sn, audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous) { this.level = level; this.sn = sn; // 如果需要,生成Init段 if(!this.ISGenerated) { this.generateIS(audioTrack, videoTrack, timeOffset); } if(this.ISGenerated) { //在视频之前有目的地重复播放音频,以便重复播放视频可以使用nextAacPts,即 //以remuxAudio计算。 //logger.log日志(“nb AAC样品:”+音轨.样品.长度); huangfei //请了解代码之前,打开注释 打印对应的》》》块 》》》 logger.log() if(audioTrack.samples.length) { var audioData = this.remuxAudio(audioTrack, timeOffset, contiguous); //logger.log('nb AVC samples:' + videoTrack.samples.length); if(videoTrack.samples.length) { var audioTrackLength = void 0; if(audioData) { audioTrackLength = audioData.endPTS - audioData.startPTS; } this.remuxVideo(videoTrack, timeOffset, contiguous, audioTrackLength); } } else { var videoData = void 0; //logger.log('nb AVC samples:' + videoTrack.samples.length); if(videoTrack.samples.length) { videoData = this.remuxVideo(videoTrack, timeOffset, contiguous); } if(videoData && audioTrack.codec) { this.remuxEmptyAudio(audioTrack, timeOffset, contiguous, videoData); } } } //logger.log('nb ID3 samples:>>>>>' + audioTrack.samples.length); if(id3Track.samples.length) { this.remuxID3(id3Track, timeOffset); } //logger.log('nb ID3 samples:' + audioTrack.samples.length); if(textTrack.samples.length) { this.remuxText(textTrack, timeOffset); } //notify end of parsing this.observer.trigger(_events2.default.FRAG_PARSED, { id: this.id, level: this.level, sn: this.sn }); } }, { key: 'generateIS', value: function generateIS(audioTrack, videoTrack, timeOffset) { var observer = this.observer, audioSamples = audioTrack.samples, videoSamples = videoTrack.samples, pesTimeScale = this.PES_TIMESCALE, tracks = {}, data = { id: this.id, level: this.level, sn: this.sn, tracks: tracks, unique: false }, computePTSDTS = this._initPTS === undefined, initPTS, initDTS; if(computePTSDTS) { initPTS = initDTS = Infinity; } if(audioTrack.config && audioSamples.length) { audioTrack.timescale = audioTrack.audiosamplerate; //MP4持续时间(以秒为单位的跟踪持续时间乘以时间刻度)编码为32位 //我们知道每个AAC样本包含1024帧。。。。 //为了避免32位计数器长时间溢出,我们使用较小的时间刻度(timescale/gcd) //我们只需要确保AAC采样持续时间仍然是一个整数(将是1024/gcd) if(audioTrack.timescale * audioTrack.duration > Math.pow(2, 32)) { var greatestCommonDivisor = function greatestCommonDivisor(a, b) { if(!b) { return a; } return greatestCommonDivisor(b, a % b); }; audioTrack.timescale = audioTrack.audiosamplerate / greatestCommonDivisor(audioTrack.audiosamplerate, 1024); } _logger.logger.log('audio mp4 timescale :' + audioTrack.timescale); tracks.audio = { container: 'audio/mp4', codec: audioTrack.codec, initSegment: _mp4Generator2.default.initSegment([audioTrack]), metadata: { channelCount: audioTrack.channelCount } }; if(computePTSDTS) { // 请记住这个反对的背景的第一点。音频,PTS+D initPTS = initDTS = audioSamples[0].pts - pesTimeScale * timeOffset; } } if(videoTrack.sps && videoTrack.pps && videoSamples.length) { videoTrack.timescale = this.MP4_TIMESCALE; tracks.video = { container: 'video/mp4', codec: videoTrack.codec, initSegment: _mp4Generator2.default.initSegment([videoTrack]), metadata: { width: videoTrack.width, height: videoTrack.height } }; if(computePTSDTS) { initPTS = Math.min(initPTS, videoSamples[0].pts - pesTimeScale * timeOffset); initDTS = Math.min(initDTS, videoSamples[0].dts - pesTimeScale * timeOffset); } } if(Object.keys(tracks).length) { observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, data); this.ISGenerated = true; if(computePTSDTS) { this._initPTS = initPTS; this._initDTS = initDTS; } } else { observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, id: this.id, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found' }); } } }, { key: 'remuxVideo', value: function remuxVideo(track, timeOffset, contiguous, audioTrackLength) { var offset = 8, pesTimeScale = this.PES_TIMESCALE, pes2mp4ScaleFactor = this.PES2MP4SCALEFACTOR, mp4SampleDuration, mdat, moof, firstPTS, firstDTS, nextDTS, lastPTS, lastDTS, inputSamples = track.samples, outputSamples = []; //PTS编码为33位,可以从-2^32循环到2^32 //PTSNormalize会使PTS/DTS值单调,我们使用最后已知的DTS值作为参考值 var nextAvcDts = void 0; if(contiguous) { // if parsed fragment is contiguous with last one, let's use last DTS value as reference nextAvcDts = this.nextAvcDts; } else { // if not contiguous, let's use target timeOffset nextAvcDts = timeOffset * pesTimeScale; } //计算第一个DTS和最后一个DTS,根据参考值对它们进行规范化 reference value var sample = inputSamples[0]; firstDTS = Math.max(this._PTSNormalize(sample.dts, nextAvcDts) - this._initDTS, 0); firstPTS = Math.max(this._PTSNormalize(sample.pts, nextAvcDts) - this._initDTS, 0); // 检查跨连续片段的时间戳连续性(这是为了消除片段间间隙/孔) var delta = Math.round((firstDTS - nextAvcDts) / 90); // if fragment are contiguous, detect hole/overlapping between fragments if(contiguous) { if(delta) { if(delta > 1) { _logger.logger.log('AVC:' + delta + ' ms hole between fragments detected,filling it'); } else if(delta < -1) { _logger.logger.log('AVC:' + -delta + ' ms overlapping between fragments detected'); } // remove hole/gap : set DTS to next expected DTS firstDTS = nextAvcDts; inputSamples[0].dts = firstDTS + this._initDTS; // offset PTS as well, ensure that PTS is smaller or equal than new DTS firstPTS = Math.max(firstPTS - delta, nextAvcDts); inputSamples[0].pts = firstPTS + this._initDTS; _logger.logger.log('Video/PTS/DTS adjusted: ' + firstPTS + '/' + firstDTS + ',delta:' + delta); } } nextDTS = firstDTS; // 计算lastps/lastDTS sample = inputSamples[inputSamples.length - 1]; lastDTS = Math.max(this._PTSNormalize(sample.dts, nextAvcDts) - this._initDTS, 0); lastPTS = Math.max(this._PTSNormalize(sample.pts, nextAvcDts) - this._initDTS, 0); lastPTS = Math.max(lastPTS, lastDTS); var vendor = navigator.vendor, userAgent = navigator.userAgent, isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS'); //在Safari上,让我们对所有样本发出相同的采样持续时间信号 //样本持续时间(如trun MP4框所预期的那样)应为样本DTS之间的增量 //将此持续时间设置为连续DTS之间的平均增量。 if(isSafari) { mp4SampleDuration = Math.round((lastDTS - firstDTS) / (pes2mp4ScaleFactor * (inputSamples.length - 1))); } //现在规范化所有PTS/DTS。。。 for(var i = 0; i < inputSamples.length; i++) { var _sample = inputSamples[i]; if(isSafari) { // 使用样本之间的恒定解码偏移量(mp4SampleDuration)计算样本DTS _sample.dts = firstDTS + i * pes2mp4ScaleFactor * mp4SampleDuration; } else { // 确保样本单调DTS _sample.dts = Math.max(this._PTSNormalize(_sample.dts, nextAvcDts) - this._initDTS, firstDTS); // 确保dts是比例因子的倍数,以避免舍入问题 对接的人,请注意》》》倍数 _sample.dts = Math.round(_sample.dts / pes2mp4ScaleFactor) * pes2mp4ScaleFactor; } //我们针对nextavcdt规范化PTS,还对initDTS进行减法(有些流不启动@PTS O) //我们确保计算值大于或等于样本DTS _sample.pts = Math.max(this._PTSNormalize(_sample.pts, nextAvcDts) - this._initDTS, _sample.dts); // 确保pts是比例因子的倍数,以避免舍入问题 _sample.pts = Math.round(_sample.pts / pes2mp4ScaleFactor) * pes2mp4ScaleFactor; } /*连接视频数据并就地构造mdat(需要另外8个字节来填充长度和mpdat类型) need 8 */ mdat = new Uint8Array(track.len + 4 * track.nbNalu + 8); var view = new DataView(mdat.buffer); view.setUint32(0, mdat.byteLength); mdat.set(_mp4Generator2.default.types.mdat, 4); for(var _i = 0; _i < inputSamples.length; _i++) { var avcSample = inputSamples[_i], mp4SampleLength = 0, compositionTimeOffset = void 0; // 将NALU比特流转换为MP4格式(在NALU前面加上大小字段) == (prepend NALU with size field) while(avcSample.units.units.length) { var unit = avcSample.units.units.shift(); view.setUint32(offset, unit.data.byteLength); offset += 4; mdat.set(unit.data, offset); offset += unit.data.byteLength; mp4SampleLength += 4 + unit.data.byteLength; } if(!isSafari) { // 预期样本持续时间是连续样本的解码时间戳差异 TDDE 解码时间 if(_i < inputSamples.length - 1) { mp4SampleDuration = inputSamples[_i + 1].dts - avcSample.dts; } else { var config = this.config, lastFrameDuration = avcSample.dts - inputSamples[_i > 0 ? _i - 1 : _i].dts; if(config.stretchShortVideoTrack) { //这里是为了拿到帧数的大小 TDDE //在某些情况下,段的音频跟踪持续时间可能超过视频跟踪持续时间。 //既然我们已经恢复了音频,而且我们知道音频轨道有多长,我们希望 //查看到下一段的增量是否大于maxBufferHole的最小值,以及 //使段之间的任何潜在间隔最小化的最后一帧的持续时间。 var maxBufferHole = config.maxBufferHole, maxSeekHole = config.maxSeekHole, gapTolerance = Math.floor(Math.min(maxBufferHole, maxSeekHole) * pesTimeScale), deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * pesTimeScale : this.nextAacPts) - avcSample.pts; if(deltaToFrameEnd > gapTolerance) { //我们从deltaToFrameEnd中减去lastFrameDuration以尝试阻止任何视频 //重叠。maxBufferHole/maxSeekHole无论如何都应该是>>lastFrameDuration。 mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; if(mp4SampleDuration < 0) { mp4SampleDuration = lastFrameDuration; } _logger.logger.log('It is approximately ' + deltaToFrameEnd / 90 + ' ms to the next segment; using duration ' + mp4SampleDuration / 90 + ' ms for the last video frame.'); } else { mp4SampleDuration = lastFrameDuration; } } else { mp4SampleDuration = lastFrameDuration; } } mp4SampleDuration /= pes2mp4ScaleFactor; compositionTimeOffset = Math.round((avcSample.pts - avcSample.dts) / pes2mp4ScaleFactor); } else { compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / (pes2mp4ScaleFactor * mp4SampleDuration))); } outputSamples.push({ size: mp4SampleLength, // 持续时间不变 duration: mp4SampleDuration, cts: compositionTimeOffset, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: avcSample.key ? 2 : 1, isNonSync: avcSample.key ? 0 : 1 } }); } //下一个AVC样本DTS应等于上一个样本DTS+上一个样本持续时间(以PES时间刻度为单位) == next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) this.nextAvcDts = lastDTS + mp4SampleDuration * pes2mp4ScaleFactor; var dropped = track.dropped; track.len = 0; track.nbNalu = 0; track.dropped = 0; if(outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { var flags = outputSamples[0].flags; flags.dependsOn = 2; flags.isNonSync = 0; } track.samples = outputSamples; moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track); track.samples = []; var data = { id: this.id, level: this.level, sn: this.sn, data1: moof, data2: mdat, startPTS: firstPTS / pesTimeScale, endPTS: (lastPTS + pes2mp4ScaleFactor * mp4SampleDuration) / pesTimeScale, startDTS: firstPTS / pesTimeScale, endDTS: (lastPTS + pes2mp4ScaleFactor * mp4SampleDuration) / pesTimeScale, // startDTS: firstDTS / pesTimeScale, // endDTS: this.nextAvcDts / pesTimeScale, type: 'video', nb: outputSamples.length, dropped: dropped }; this.observer.trigger(_events2.default.FRAG_PARSING_DATA, data); return data; } }, { key: 'remuxAudio', value: function remuxAudio(track, timeOffset, contiguous) { var pesTimeScale = this.PES_TIMESCALE, mp4timeScale = track.timescale, pes2mp4ScaleFactor = pesTimeScale / mp4timeScale, expectedSampleDuration = track.timescale * 1024 / track.audiosamplerate; var view, offset = 8, aacSample, mp4Sample, unit, mdat, moof, firstPTS, firstDTS, lastDTS, pts, dts, ptsnorm, dtsnorm, samples = [], samples0 = []; track.samples.sort(function(a, b) { return a.pts - b.pts; }); samples0 = track.samples; var nextAacPts = contiguous ? this.nextAacPts : timeOffset * pesTimeScale; //如果音频曲目缺少示例,则帧似乎在 //生成的mp4段,导致同步问题,并在音频段的末尾留下间隙。 //为了防止这种情况发生,我们在有间隙的地方注入帧。 //如果可能,我们注入一个无声的帧;如果不可能,我们复制最后一个 var firstPtsNorm = this._PTSNormalize(samples0[0].pts - this._initPTS, nextAacPts), pesFrameDuration = expectedSampleDuration * pes2mp4ScaleFactor; var nextPtsNorm = firstPtsNorm + pesFrameDuration; for(var i = 1; i < samples0.length;) { // 首先,让我们看看这个离我们预期的距离有多远 var sample = samples0[i], ptsNorm = this._PTSNormalize(sample.pts - this._initPTS, nextAacPts), delta = ptsNorm - nextPtsNorm; //如果重叠超过半个持续时间,则丢弃此样本 if(delta < -0.5 * pesFrameDuration) { _logger.logger.log('Dropping frame due to ' + Math.abs(delta / 90) + ' ms overlap.'); samples0.splice(i, 1); track.len -= sample.unit.length; // 别碰nextPtsNorm或我 } // 否则,如果我们离我们应该在的地方超过半帧,请插入丢失的帧 TDDE else if(delta > 0.5 * pesFrameDuration) { var missing = Math.round(delta / pesFrameDuration); _logger.logger.log('Injecting ' + missing + ' frame' + (missing > 1 ? 's' : '') + ' of missing audio due to ' + Math.round(delta / 90) + ' ms gap.'); for(var j = 0; j < missing; j++) { var newStamp = samples0[i - 1].pts + pesFrameDuration, fillFrame = _aac2.default.getSilentFrame(track.channelCount); if(!fillFrame) { _logger.logger.log('Unable to get silent frame for given audio codec; duplicating last frame instead.'); fillFrame = sample.unit.slice(0); } samples0.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp }); track.len += fillFrame.length; i += 1; } // 将样本调整到下一个预期点 nextPtsNorm += (missing + 1) * pesFrameDuration; sample.pts = samples0[i - 1].pts + pesFrameDuration; i += 1; } // 否则,我们在半帧内,所以调整点 else { if(Math.abs(delta) > 0.1 * pesFrameDuration) { _logger.logger.log('Invalid frame delta ' + (ptsNorm - nextPtsNorm + pesFrameDuration) + ' at PTS ' + Math.round(ptsNorm / 90) + ' (should be ' + pesFrameDuration + ').'); } nextPtsNorm += pesFrameDuration; sample.pts = samples0[i - 1].pts + pesFrameDuration; i += 1; } } while(samples0.length) { aacSample = samples0.shift(); unit = aacSample.unit; pts = aacSample.pts - this._initDTS; dts = aacSample.dts - this._initDTS; //logger.log(`Audio/PTS:${Math.round(pts/90)}`); // 如果不是第一个样本 if(lastDTS !== undefined) { ptsnorm = this._PTSNormalize(pts, lastDTS); dtsnorm = this._PTSNormalize(dts, lastDTS); mp4Sample.duration = (dtsnorm - lastDTS) / pes2mp4ScaleFactor; } else { ptsnorm = this._PTSNormalize(pts, nextAacPts); dtsnorm = this._PTSNormalize(dts, nextAacPts); var _delta = Math.round(1000 * (ptsnorm - nextAacPts) / pesTimeScale); // 如果碎片是连续的,则检测碎片之间的 孔/重叠 detect hole/overlapping between fragments if(contiguous) { // log delta if(_delta) { if(_delta > 0) { _logger.logger.log(_delta + ' ms hole between AAC samples detected,filling it'); //如果我们有帧重叠,重叠超过半帧持续时间 overlapping for more than half a frame duraion } else if(_delta < -12) { // 放置重叠的音频帧。。。浏览器会处理的 _logger.logger.log(-_delta + ' ms overlapping between AAC samples detected, drop frame'); track.len -= unit.byteLength; continue; } // set PTS/DTS to expected PTS/DTS ptsnorm = dtsnorm = nextAacPts; } } // 记住我们的aac样本的第一个点,确保值为正 firstPTS = Math.max(0, ptsnorm); firstDTS = Math.max(0, dtsnorm); if(track.len > 0) { /*连接音频数据并就地构造mdat (需要8个字节来填充长度和mdat类型)*/ mdat = new Uint8Array(track.len + 8); view = new DataView(mdat.buffer); view.setUint32(0, mdat.byteLength); mdat.set(_mp4Generator2.default.types.mdat, 4); } else { // no audio samples return; } } mdat.set(unit, offset); offset += unit.byteLength; //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${aacSample.pts}/${aacSample.dts}/${this._initDTS}/${ptsnorm}/${dtsnorm}/${(aacSample.pts/4294967296).toFixed(3)}'); mp4Sample = { size: unit.byteLength, cts: 0, duration: 0, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: 1 } }; samples.push(mp4Sample); lastDTS = dtsnorm; } var lastSampleDuration = 0; var nbSamples = samples.length; //set last sample duration as being identical to previous sample if(nbSamples >= 2) { lastSampleDuration = samples[nbSamples - 2].duration; mp4Sample.duration = lastSampleDuration; } if(nbSamples) { //下一个aac样本点应等于上一个样本点+持续时间 this.nextAacPts = ptsnorm + pes2mp4ScaleFactor * lastSampleDuration; //logger.log('Audio/PTS/PTSend:' + aacSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0)); track.len = 0; track.samples = samples; moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstDTS / pes2mp4ScaleFactor, track); track.samples = []; var audioData = { id: this.id, level: this.level, sn: this.sn, data1: moof, data2: mdat, startPTS: firstPTS / pesTimeScale, endPTS: this.nextAacPts / pesTimeScale, startDTS: firstDTS / pesTimeScale, endDTS: (dtsnorm + pes2mp4ScaleFactor * lastSampleDuration) / pesTimeScale, type: 'audio', nb: nbSamples }; this.observer.trigger(_events2.default.FRAG_PARSING_DATA, audioData); return audioData; } return null; } }, { key: 'remuxEmptyAudio', value: function remuxEmptyAudio(track, timeOffset, contiguous, videoData) { var pesTimeScale = this.PES_TIMESCALE, mp4timeScale = track.timescale ? track.timescale : track.audiosamplerate, pes2mp4ScaleFactor = pesTimeScale / mp4timeScale, // 与视频的时间戳同步 startDTS = videoData.startDTS * pesTimeScale + this._initDTS, endDTS = videoData.endDTS * pesTimeScale + this._initDTS, //一个样本的持续时间值 sampleDuration = 1024, frameDuration = pes2mp4ScaleFactor * sampleDuration, //此段持续时间的样本计数 nbSamples = Math.ceil((endDTS - startDTS) / frameDuration), //无声 silentFrame = _aac2.default.getSilentFrame(track.channelCount); // 如果我们不能产生一个无声的画面。。。 if(!silentFrame) { _logger.logger.trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!'); return; } var samples = []; for(var i = 0; i < nbSamples; i++) { var stamp = startDTS + i * frameDuration; samples.push({ unit: silentFrame.slice(0), pts: stamp, dts: stamp }); track.len += silentFrame.length; } track.samples = samples; this.remuxAudio(track, timeOffset, contiguous); } }, { key: 'remuxID3', value: function remuxID3(track, timeOffset) { var length = track.samples.length, sample; // 消耗样本 if(length) { for(var index = 0; index < length; index++) { sample = track.samples[index]; //将id3 pts,dts设置为相对时间 //使用this.\u initPTS和this.\u initDTS计算相对时间 sample.pts = (sample.pts - this._initPTS) / this.PES_TIMESCALE; sample.dts = (sample.dts - this._initDTS) / this.PES_TIMESCALE; } this.observer.trigger(_events2.default.FRAG_PARSING_METADATA, { id: this.id, level: this.level, sn: this.sn, samples: track.samples }); } track.samples = []; timeOffset = timeOffset; } }, { key: 'remuxText', value: function remuxText(track, timeOffset) { track.samples.sort(function(a, b) { return a.pts - b.pts; }); var length = track.samples.length, sample; // 消耗样本 if(length) { for(var index = 0; index < length; index++) { sample = track.samples[index]; sample.pts = (sample.pts - this._initPTS) / this.PES_TIMESCALE; } this.observer.trigger(_events2.default.FRAG_PARSING_USERDATA, { id: this.id, level: this.level, sn: this.sn, samples: track.samples }); } track.samples = []; timeOffset = timeOffset; } }, { key: '_PTSNormalize', value: function _PTSNormalize(value, reference) { var offset; if(reference === undefined) { return value; } if(reference < value) { // - 2^33 offset = -8589934592; } else { // + 2^33 offset = 8589934592; } /*PTS是33位(从0到2^33-1) 如果值和参考值之间的差值大于振幅的一半(2^32),则意味着 发生PTS循环。填补空白*/ while(Math.abs(value - reference) > 4294967296) { value += offset; } return value; } }, { key: 'passthrough', get: function get() { return false; } }]); return MP4Remuxer; }(); exports.default = MP4Remuxer; }, { "../errors": 7, "../events": 9, "../helper/aac": 10, "../remux/mp4-generator": 15, "../utils/logger": 17, "../utils/polyfill": 18 }], 17: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; function noop() {} var fakeLogger = { trace: noop, debug: noop, log: noop, warn: noop, info: noop, error: noop }; var exportedLogger = fakeLogger; //let lastCallTime; // function formatMsgWithTimeInfo(type, msg) { // const now = Date.now(); // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; // lastCallTime = now; // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; // return msg; // } function formatMsg(type, msg) { msg = '[' + type + '] > ' + msg; return msg; } function consolePrintFn(type) { var func = window.console[type]; if(func) { return function() { for(var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if(args[0]) { args[0] = formatMsg(type, args[0]); } func.apply(window.console, args); }; } return noop; } function exportLoggerFunctions(debugConfig) { for(var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { functions[_key2 - 1] = arguments[_key2]; } functions.forEach(function(type) { exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type); }); } var enableLogs = exports.enableLogs = function enableLogs(debugConfig) { if(debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') { exportLoggerFunctions(debugConfig, // Remove out from list here to hard-disable a log-level //'trace', 'debug', 'log', 'info', 'warn', 'error'); // Some browsers don't allow to use bind on console object anyway // fallback to default if needed try { exportedLogger.log(); } catch(e) { exportedLogger = fakeLogger; } } else { exportedLogger = fakeLogger; } }; var logger = exports.logger = exportedLogger; }, {}], 18: [function(require, module, exports) { 'use strict'; if(typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) { ArrayBuffer.prototype.slice = function(start, end) { var that = new Uint8Array(this); if(end === undefined) { end = that.length; } var result = new ArrayBuffer(end - start); var resultArray = new Uint8Array(result); for(var i = 0; i < resultArray.length; i++) { resultArray[i] = that[i + start]; } return result; }; } }, {}], 19: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * 基于XHR的记录器 logger */ var XhrLoader = function() { function XhrLoader(config) { _classCallCheck(this, XhrLoader); if(config && config.xhrSetup) { this.xhrSetup = config.xhrSetup; } } _createClass(XhrLoader, [{ key: 'destroy', value: function destroy() { this.abort(); this.loader = null; } }, { key: 'abort', value: function abort() { var loader = this.loader; if(loader && loader.readyState !== 4) { this.stats.aborted = true; loader.abort(); } window.clearTimeout(this.requestTimeout); this.requestTimeout = null; window.clearTimeout(this.retryTimeout); this.retryTimeout = null; } }, { key: 'loadHead', value: function loadHead(context, config, callbacks) { this.context = context; this.config = config; this.callbacks = callbacks; this.stats = { trequest: performance.now(), retry: 0 }; this.retryDelay = config.retryDelay; var xhr = new XMLHttpRequest(); xhr.open('head', context.url); xhr.onload = function() { callbacks.onSuccess(xhr.getResponseHeader('content-length')); }; xhr.send(); } }, { key: 'load', value: function load(context, config, callbacks) { this.context = context; this.config = config; this.callbacks = callbacks; this.stats = { trequest: performance.now(), retry: 0 }; this.retryDelay = config.retryDelay; this.loadInternal(); } }, { key: 'loadInternal', value: function loadInternal() { var xhr, context = this.context; if(typeof XDomainRequest !== 'undefined') { xhr = this.loader = new XDomainRequest(); } else { xhr = this.loader = new XMLHttpRequest(); } xhr.onloadend = this.loadend.bind(this); xhr.onprogress = this.loadprogress.bind(this); xhr.open('GET', context.url, true); if(context.rangeEnd) { xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); } xhr.responseType = context.responseType; var stats = this.stats; stats.tfirst = 0; stats.loaded = 0; if(this.xhrSetup) { this.xhrSetup(xhr, context.url); } //在执行请求之前设置超时 this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout); xhr.send(); } }, { key: 'loadend', value: function loadend(event) { var xhr = event.currentTarget, status = xhr.status, stats = this.stats, context = this.context, config = this.config; // 如果xhr已中止,则不要继续 if(stats.aborted) { return; } // 无论如何,清除当前XHR超时 window.clearTimeout(this.requestTimeout); // 200到299之间的http状态都是成功的 if(status >= 200 && status < 300) { stats.tload = Math.max(stats.tfirst, performance.now()); var data = void 0, len = void 0; if(context.responseType === 'arraybuffer') { data = xhr.response; len = data.byteLength; } else { data = xhr.responseText; len = data.length; } stats.loaded = stats.total = len; var response = { url: xhr.responseURL, data: data }; this.callbacks.onSuccess(response, stats, context); } else { // 如果达到最大重试次数,或者如果http状态介于400到499之间(此错误无法恢复,重试无效),则返回错误 TDDE if(stats.retry >= config.maxRetry || status >= 400 && status < 499) { // logger.error(`${status} while loading ${context.url}` ); this.callbacks.onError({ code: status, text: xhr.statusText }, context); } else { this.destroy(); // 计划重试 this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay); // 设置指数退避 backoff this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay); stats.retry++; } } } }, { key: 'loadtimeout', value: function loadtimeout() { // logger.warn(`timeout while loading ${this.context.url}` ); this.callbacks.onTimeout(this.stats, this.context); } }, { key: 'loadprogress', value: function loadprogress(event) { var stats = this.stats; if(stats.tfirst === 0) { stats.tfirst = Math.max(performance.now(), stats.trequest); } stats.loaded = event.loaded; if(event.lengthComputable) { stats.total = event.total; } var onProgress = this.callbacks.onProgress; if(onProgress) { // last args is to provide on progress data onProgress(stats, this.context, null); } } }]); return XhrLoader; }(); exports.default = XhrLoader; }, {}], 20: [function(require, module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function() { function defineProperties(target, props) { for(var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if(protoProps) defineProperties(Constructor.prototype, protoProps); if(staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('./events'); var _events2 = _interopRequireDefault(_events); var _flowController = require('./controller/flow-controller'); var _flowController2 = _interopRequireDefault(_flowController); var _bufferController = require('./controller/buffer-controller'); var _bufferController2 = _interopRequireDefault(_bufferController); var _events3 = require('events'); var _events4 = _interopRequireDefault(_events3); var _xhrLoader = require('./utils/xhr-loader'); var _xhrLoader2 = _interopRequireDefault(_xhrLoader); var _fileLoader = require('./loader/file-loader'); var _fileLoader2 = _interopRequireDefault(_fileLoader); var _websocketLoader = require('./loader/websocket-loader'); var _websocketLoader2 = _interopRequireDefault(_websocketLoader); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if(!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Wfs = function() { _createClass(Wfs, null, [{ key: 'isSupported', value: function isSupported() { return window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function' && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42c01f,mp4a.40.2"'); } }, { key: 'version', get: function get() { return '' + 'v.0.0.0.1'; } }, { key: 'Events', get: function get() { return _events2.default; } }, { key: 'DefaultConfig', get: function get() { if(!Wfs.defaultConfig) { Wfs.defaultConfig = { autoStartLoad: true, startPosition: -1, debug: false, fLoader: undefined, loader: _xhrLoader2.default, //loader: FetchLoader, fmp4FileUrl: 'xxxx.mp4', fragLoadingTimeOut: 20000, fragLoadingMaxRetry: 6, fragLoadingRetryDelay: 1000, fragLoadingMaxRetryTimeout: 64000, fragLoadingLoopThreshold: 3, forceKeyFrameOnDiscontinuity: true, appendErrorMaxRetry: 3 }; } return Wfs.defaultConfig; }, set: function set(defaultConfig) { Wfs.defaultConfig = defaultConfig; } }]); function Wfs() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Wfs); var defaultConfig = Wfs.DefaultConfig; for(var prop in defaultConfig) { if(prop in config) { continue; } config[prop] = defaultConfig[prop]; } this.config = config; // observer setup var observer = this.observer = new _events4.default(); observer.trigger = function trigger(event) { for(var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { data[_key - 1] = arguments[_key]; } observer.emit.apply(observer, [event, event].concat(data)); }; observer.off = function off(event) { for(var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { data[_key2 - 1] = arguments[_key2]; } observer.removeListener.apply(observer, [event].concat(data)); }; this.on = observer.on.bind(observer); this.off = observer.off.bind(observer); this.trigger = observer.trigger.bind(observer); this.flowController = new _flowController2.default(this); this.bufferController = new _bufferController2.default(this); // this.fileLoader = new FileLoader(this); this.websocketLoader = new _websocketLoader2.default(this); this.mediaType = undefined; } _createClass(Wfs, [{ key: 'destroy', value: function destroy() { this.flowController.destroy(); this.bufferController.destroy(); // this.fileLoader.destroy(); this.websocketLoader.destroy(); } }, { key: 'attachMedia', value: function attachMedia(media) { var channelName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'chX'; var mediaType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'H264Raw'; var websocketName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'play2'; // 'H264Raw' 'FMp4' this.mediaType = mediaType; this.media = media; this.trigger(_events2.default.MEDIA_ATTACHING, { media: media, channelName: channelName, mediaType: mediaType, websocketName: websocketName }); } }, { key: 'attachWebsocket', value: function attachWebsocket(websocket, channelName) { console.log("111>>>",websocket) this.trigger(_events2.default.WEBSOCKET_ATTACHING, { websocket: websocket, mediaType: this.mediaType, channelName: channelName }); } }]); return Wfs; }(); exports.default = Wfs; }, { "./controller/buffer-controller": 3, "./controller/flow-controller": 4, "./events": 9, "./loader/file-loader": 13, "./loader/websocket-loader": 14, "./utils/xhr-loader": 19, "events": 1 }] }, {}, [11])(11) });