dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
1,209 lines (982 loc) • 142 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @class
* @ignore
*/
'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'); } }
var ErrorsBase = (function () {
function ErrorsBase() {
_classCallCheck(this, ErrorsBase);
}
_createClass(ErrorsBase, [{
key: 'extend',
value: function extend(errors, config) {
if (!errors) return;
var override = config ? config.override : false;
var publicOnly = config ? config.publicOnly : false;
for (var err in errors) {
if (!errors.hasOwnProperty(err) || this[err] && !override) continue;
if (publicOnly && errors[err].indexOf('public_') === -1) continue;
this[err] = errors[err];
}
}
}]);
return ErrorsBase;
})();
exports['default'] = ErrorsBase;
module.exports = exports['default'];
},{}],2:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @class
* @ignore
*/
'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'); } }
var EventsBase = (function () {
function EventsBase() {
_classCallCheck(this, EventsBase);
}
_createClass(EventsBase, [{
key: 'extend',
value: function extend(events, config) {
if (!events) return;
var override = config ? config.override : false;
var publicOnly = config ? config.publicOnly : false;
for (var evt in events) {
if (!events.hasOwnProperty(evt) || this[evt] && !override) continue;
if (publicOnly && events[evt].indexOf('public_') === -1) continue;
this[evt] = events[evt];
}
}
}]);
return EventsBase;
})();
exports['default'] = EventsBase;
module.exports = exports['default'];
},{}],3:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
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 _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 _coreEventsEventsBase = _dereq_(2);
var _coreEventsEventsBase2 = _interopRequireDefault(_coreEventsEventsBase);
var MssEvents = (function (_EventsBase) {
_inherits(MssEvents, _EventsBase);
function MssEvents() {
_classCallCheck(this, MssEvents);
_get(Object.getPrototypeOf(MssEvents.prototype), 'constructor', this).call(this);
this.FRAGMENT_INFO_LOADING_COMPLETED = 'fragmentInfoLoadingCompleted';
}
return MssEvents;
})(_coreEventsEventsBase2['default']);
var mssEvents = new MssEvents();
exports['default'] = mssEvents;
module.exports = exports['default'];
},{"2":2}],4:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _MssEvents = _dereq_(3);
var _MssEvents2 = _interopRequireDefault(_MssEvents);
var _MssFragmentMoofProcessor = _dereq_(5);
var _MssFragmentMoofProcessor2 = _interopRequireDefault(_MssFragmentMoofProcessor);
var _streamingVoFragmentRequest = _dereq_(15);
var _streamingVoFragmentRequest2 = _interopRequireDefault(_streamingVoFragmentRequest);
function MssFragmentInfoController(config) {
config = config || {};
var context = this.context;
var instance = undefined,
logger = undefined,
fragmentModel = undefined,
started = undefined,
type = undefined,
bufferTimeout = undefined,
startTime = undefined,
startFragmentTime = undefined,
index = undefined;
var streamProcessor = config.streamProcessor;
var eventBus = config.eventBus;
var dashMetrics = config.dashMetrics;
var playbackController = config.playbackController;
var ISOBoxer = config.ISOBoxer;
var baseURLController = config.baseURLController;
var debug = config.debug;
var controllerType = 'MssFragmentInfoController';
function setup() {
logger = debug.getLogger(instance);
}
function initialize() {
started = false;
startTime = null;
startFragmentTime = null;
// Register to StreamProcessor as external controller
streamProcessor.registerExternalController(instance);
type = streamProcessor.getType();
fragmentModel = streamProcessor.getFragmentModel();
}
function doStart() {
if (started === true) {
return;
}
logger.debug('Do start');
eventBus.on(_MssEvents2['default'].FRAGMENT_INFO_LOADING_COMPLETED, onFragmentInfoLoadedCompleted, instance);
started = true;
startTime = new Date().getTime();
index = 0;
loadNextFragmentInfo();
}
function doStop() {
if (!started) {
return;
}
logger.debug('Do stop');
eventBus.off(_MssEvents2['default'].FRAGMENT_INFO_LOADING_COMPLETED, onFragmentInfoLoadedCompleted, instance);
// Stop buffering process
clearTimeout(bufferTimeout);
started = false;
startTime = null;
startFragmentTime = null;
}
function reset() {
doStop();
streamProcessor.unregisterExternalController(instance);
}
function loadNextFragmentInfo() {
// Check if running state
if (!started) {
return;
}
// Get last segment from SegmentTimeline
var representation = getCurrentRepresentation();
var manifest = representation.adaptation.period.mpd.manifest;
var adaptation = manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index];
var segments = adaptation.SegmentTemplate.SegmentTimeline.S_asArray;
var segment = segments[segments.length - 1];
logger.debug('Last fragment time: ' + segment.t / adaptation.SegmentTemplate.timescale);
// Generate segment request
var request = getRequestForSegment(adaptation, representation, segment);
// Send segment request
requestFragment.call(this, request);
}
function delayLoadNextFragmentInfo(delay) {
clearTimeout(bufferTimeout);
bufferTimeout = setTimeout(function () {
bufferTimeout = null;
loadNextFragmentInfo();
}, delay * 1000);
}
function getRequestForSegment(adaptation, representation, segment) {
var timescale = adaptation.SegmentTemplate.timescale;
var request = new _streamingVoFragmentRequest2['default']();
request.mediaType = type;
request.type = 'FragmentInfoSegment';
// request.range = segment.mediaRange;
request.startTime = segment.t / timescale;
request.duration = segment.d / timescale;
request.timescale = timescale;
// request.availabilityStartTime = segment.availabilityStartTime;
// request.availabilityEndTime = segment.availabilityEndTime;
// request.wallStartTime = segment.wallStartTime;
request.quality = representation.index;
request.index = index++;
request.mediaInfo = streamProcessor.getMediaInfo();
request.adaptationIndex = representation.adaptation.index;
request.representationId = representation.id;
request.url = baseURLController.resolve(representation.path).url + adaptation.SegmentTemplate.media;
request.url = request.url.replace('$Bandwidth$', representation.bandwidth);
request.url = request.url.replace('$Time$', segment.tManifest ? segment.tManifest : segment.t);
request.url = request.url.replace('/Fragments(', '/FragmentInfo(');
return request;
}
function getCurrentRepresentation() {
var representationController = streamProcessor.getRepresentationController();
var representation = representationController.getCurrentRepresentation();
return representation;
}
function requestFragment(request) {
logger.debug('Load fragment for time: ' + request.startTime);
if (streamProcessor.getFragmentModel().isFragmentLoadedOrPending(request)) {
// We may have reached end of timeline in case of start-over streams
logger.debug('No more fragments');
return;
}
fragmentModel.executeRequest(request);
}
function onFragmentInfoLoadedCompleted(e) {
if (e.streamProcessor !== streamProcessor) {
return;
}
var request = e.fragmentInfo.request;
if (!e.fragmentInfo.response) {
logger.error('Load error', request.url);
return;
}
var deltaFragmentTime = undefined,
deltaTime = undefined;
logger.debug('FragmentInfo loaded: ', request.url);
if (!startFragmentTime) {
startFragmentTime = request.startTime;
}
try {
// Process FramgentInfo in order to update segment timeline (DVR window)
var mssFragmentMoofProcessor = (0, _MssFragmentMoofProcessor2['default'])(context).create({
dashMetrics: dashMetrics,
playbackController: playbackController,
ISOBoxer: ISOBoxer,
eventBus: eventBus,
debug: debug
});
mssFragmentMoofProcessor.updateSegmentList(e.fragmentInfo, streamProcessor);
deltaTime = (new Date().getTime() - startTime) / 1000;
deltaFragmentTime = request.startTime + request.duration - startFragmentTime;
delayLoadNextFragmentInfo(Math.max(0, deltaFragmentTime - deltaTime));
} catch (e) {
logger.fatal('Internal error while processing fragment info segment ');
}
}
function getType() {
return type;
}
instance = {
initialize: initialize,
controllerType: controllerType,
start: doStart,
getType: getType,
reset: reset
};
setup();
return instance;
}
MssFragmentInfoController.__dashjs_factory_name = 'MssFragmentInfoController';
exports['default'] = dashjs.FactoryMaker.getClassFactory(MssFragmentInfoController);
/* jshint ignore:line */
module.exports = exports['default'];
},{"15":15,"3":3,"5":5}],5:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _streamingVoDashJSError = _dereq_(13);
var _streamingVoDashJSError2 = _interopRequireDefault(_streamingVoDashJSError);
var _errorsMssErrors = _dereq_(9);
var _errorsMssErrors2 = _interopRequireDefault(_errorsMssErrors);
var _streamingMediaPlayerEvents = _dereq_(12);
var _streamingMediaPlayerEvents2 = _interopRequireDefault(_streamingMediaPlayerEvents);
/**
* @module MssFragmentMoofProcessor
* @ignore
* @param {Object} config object
*/
function MssFragmentMoofProcessor(config) {
config = config || {};
var instance = undefined,
type = undefined,
logger = undefined;
var dashMetrics = config.dashMetrics;
var playbackController = config.playbackController;
var errorHandler = config.errHandler;
var eventBus = config.eventBus;
var ISOBoxer = config.ISOBoxer;
var debug = config.debug;
function setup() {
logger = debug.getLogger(instance);
type = '';
}
function processTfrf(request, tfrf, tfdt, streamProcessor) {
var representationController = streamProcessor.getRepresentationController();
var representation = representationController.getCurrentRepresentation();
var indexHandler = streamProcessor.getIndexHandler();
var manifest = representation.adaptation.period.mpd.manifest;
var adaptation = manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index];
var timescale = adaptation.SegmentTemplate.timescale;
type = streamProcessor.getType();
if (manifest.type !== 'dynamic' && !manifest.timeShiftBufferDepth) {
return;
}
if (!tfrf) {
errorHandler.error(new _streamingVoDashJSError2['default'](_errorsMssErrors2['default'].MSS_NO_TFRF_CODE, _errorsMssErrors2['default'].MSS_NO_TFRF_MESSAGE));
return;
}
// Get adaptation's segment timeline (always a SegmentTimeline in Smooth Streaming use case)
var segments = adaptation.SegmentTemplate.SegmentTimeline.S;
var entries = tfrf.entry;
var entry = undefined,
segmentTime = undefined,
range = undefined;
var segment = null;
var t = 0;
var availabilityStartTime = null;
if (entries.length === 0) {
return;
}
// Consider only first tfrf entry (to avoid pre-condition failure on fragment info requests)
entry = entries[0];
// In case of start-over streams, check if we have reached end of original manifest duration (set in timeShiftBufferDepth)
// => then do not update anymore timeline
if (manifest.type === 'static') {
// Get first segment time
segmentTime = segments[0].tManifest ? parseFloat(segments[0].tManifest) : segments[0].t;
if (entry.fragment_absolute_time > segmentTime + manifest.timeShiftBufferDepth * timescale) {
return;
}
}
logger.debug('entry - t = ', entry.fragment_absolute_time / timescale);
// Get last segment time
segmentTime = segments[segments.length - 1].tManifest ? parseFloat(segments[segments.length - 1].tManifest) : segments[segments.length - 1].t;
logger.debug('Last segment - t = ', segmentTime / timescale);
// Check if we have to append new segment to timeline
if (entry.fragment_absolute_time <= segmentTime) {
// Update DVR window range
// => set range end to end time of current segment
range = {
start: segments[0].t / timescale,
end: tfdt.baseMediaDecodeTime / timescale + request.duration
};
updateDVR(request.mediaType, range, streamProcessor.getStreamInfo().manifestInfo);
return;
}
logger.debug('Add new segment - t = ', entry.fragment_absolute_time / timescale);
segment = {};
segment.t = entry.fragment_absolute_time;
segment.d = entry.fragment_duration;
// If timestamps starts at 0 relative to 1st segment (dynamic to static) then update segment time
if (segments[0].tManifest) {
segment.t -= parseFloat(segments[0].tManifest) - segments[0].t;
segment.tManifest = entry.fragment_absolute_time;
}
segments.push(segment);
// In case of static start-over streams, update content duration
if (manifest.type === 'static') {
if (type === 'video') {
segment = segments[segments.length - 1];
var end = (segment.t + segment.d) / timescale;
if (end > representation.adaptation.period.duration) {
eventBus.trigger(_streamingMediaPlayerEvents2['default'].MANIFEST_VALIDITY_CHANGED, { sender: this, newDuration: end });
}
}
return;
}
// In case of live streams, update segment timeline according to DVR window
else if (manifest.timeShiftBufferDepth && manifest.timeShiftBufferDepth > 0) {
// Get timestamp of the last segment
segment = segments[segments.length - 1];
t = segment.t;
// Determine the segments' availability start time
availabilityStartTime = Math.round((t - manifest.timeShiftBufferDepth * timescale) / timescale);
// Remove segments prior to availability start time
segment = segments[0];
while (Math.round(segment.t / timescale) < availabilityStartTime) {
logger.debug('Remove segment - t = ' + segment.t / timescale);
segments.splice(0, 1);
segment = segments[0];
}
// Update DVR window range => set range end to end time of current segment
range = {
start: segments[0].t / timescale,
end: tfdt.baseMediaDecodeTime / timescale + request.duration
};
updateDVR(type, range, streamProcessor.getStreamInfo().manifestInfo);
}
indexHandler.updateRepresentation(representation, true);
}
function updateDVR(type, range, manifestInfo) {
var dvrInfos = dashMetrics.getCurrentDVRInfo(type);
if (!dvrInfos || range.end > dvrInfos.range.end) {
logger.debug('Update DVR Infos [' + range.start + ' - ' + range.end + ']');
dashMetrics.addDVRInfo(type, playbackController.getTime(), manifestInfo, range);
}
}
// This function returns the offset of the 1st byte of a child box within a container box
function getBoxOffset(parent, type) {
var offset = 8;
var i = 0;
for (i = 0; i < parent.boxes.length; i++) {
if (parent.boxes[i].type === type) {
return offset;
}
offset += parent.boxes[i].size;
}
return offset;
}
function convertFragment(e, sp) {
var i = undefined;
// e.request contains request description object
// e.response contains fragment bytes
var isoFile = ISOBoxer.parseBuffer(e.response);
// Update track_Id in tfhd box
var tfhd = isoFile.fetch('tfhd');
tfhd.track_ID = e.request.mediaInfo.index + 1;
// Add tfdt box
var tfdt = isoFile.fetch('tfdt');
var traf = isoFile.fetch('traf');
if (tfdt === null) {
tfdt = ISOBoxer.createFullBox('tfdt', traf, tfhd);
tfdt.version = 1;
tfdt.flags = 0;
tfdt.baseMediaDecodeTime = Math.floor(e.request.startTime * e.request.timescale);
}
var trun = isoFile.fetch('trun');
// Process tfxd boxes
// This box provide absolute timestamp but we take the segment start time for tfdt
var tfxd = isoFile.fetch('tfxd');
if (tfxd) {
tfxd._parent.boxes.splice(tfxd._parent.boxes.indexOf(tfxd), 1);
tfxd = null;
}
var tfrf = isoFile.fetch('tfrf');
processTfrf(e.request, tfrf, tfdt, sp);
if (tfrf) {
tfrf._parent.boxes.splice(tfrf._parent.boxes.indexOf(tfrf), 1);
tfrf = null;
}
// If protected content in PIFF1.1 format (sepiff box = Sample Encryption PIFF)
// => convert sepiff box it into a senc box
// => create saio and saiz boxes (if not already present)
var sepiff = isoFile.fetch('sepiff');
if (sepiff !== null) {
sepiff.type = 'senc';
sepiff.usertype = undefined;
var _saio = isoFile.fetch('saio');
if (_saio === null) {
// Create Sample Auxiliary Information Offsets Box box (saio)
_saio = ISOBoxer.createFullBox('saio', traf);
_saio.version = 0;
_saio.flags = 0;
_saio.entry_count = 1;
_saio.offset = [0];
var saiz = ISOBoxer.createFullBox('saiz', traf);
saiz.version = 0;
saiz.flags = 0;
saiz.sample_count = sepiff.sample_count;
saiz.default_sample_info_size = 0;
saiz.sample_info_size = [];
if (sepiff.flags & 0x02) {
// Sub-sample encryption => set sample_info_size for each sample
for (i = 0; i < sepiff.sample_count; i += 1) {
// 10 = 8 (InitializationVector field size) + 2 (subsample_count field size)
// 6 = 2 (BytesOfClearData field size) + 4 (BytesOfEncryptedData field size)
saiz.sample_info_size[i] = 10 + 6 * sepiff.entry[i].NumberOfEntries;
}
} else {
// No sub-sample encryption => set default sample_info_size = InitializationVector field size (8)
saiz.default_sample_info_size = 8;
}
}
}
tfhd.flags &= 0xFFFFFE; // set tfhd.base-data-offset-present to false
tfhd.flags |= 0x020000; // set tfhd.default-base-is-moof to true
trun.flags |= 0x000001; // set trun.data-offset-present to true
// Update trun.data_offset field that corresponds to first data byte (inside mdat box)
var moof = isoFile.fetch('moof');
var length = moof.getLength();
trun.data_offset = length + 8;
// Update saio box offset field according to new senc box offset
var saio = isoFile.fetch('saio');
if (saio !== null) {
var trafPosInMoof = getBoxOffset(moof, 'traf');
var sencPosInTraf = getBoxOffset(traf, 'senc');
// Set offset from begin fragment to the first IV field in senc box
saio.offset[0] = trafPosInMoof + sencPosInTraf + 16; // 16 = box header (12) + sample_count field size (4)
}
// Write transformed/processed fragment into request reponse data
e.response = isoFile.write();
}
function updateSegmentList(e, sp) {
// e.request contains request description object
// e.response contains fragment bytes
if (!e.response) {
throw new Error('e.response parameter is missing');
}
var isoFile = ISOBoxer.parseBuffer(e.response);
// Update track_Id in tfhd box
var tfhd = isoFile.fetch('tfhd');
tfhd.track_ID = e.request.mediaInfo.index + 1;
// Add tfdt box
var tfdt = isoFile.fetch('tfdt');
var traf = isoFile.fetch('traf');
if (tfdt === null) {
tfdt = ISOBoxer.createFullBox('tfdt', traf, tfhd);
tfdt.version = 1;
tfdt.flags = 0;
tfdt.baseMediaDecodeTime = Math.floor(e.request.startTime * e.request.timescale);
}
var tfrf = isoFile.fetch('tfrf');
processTfrf(e.request, tfrf, tfdt, sp);
if (tfrf) {
tfrf._parent.boxes.splice(tfrf._parent.boxes.indexOf(tfrf), 1);
tfrf = null;
}
}
function getType() {
return type;
}
instance = {
convertFragment: convertFragment,
updateSegmentList: updateSegmentList,
getType: getType
};
setup();
return instance;
}
MssFragmentMoofProcessor.__dashjs_factory_name = 'MssFragmentMoofProcessor';
exports['default'] = dashjs.FactoryMaker.getClassFactory(MssFragmentMoofProcessor);
/* jshint ignore:line */
module.exports = exports['default'];
},{"12":12,"13":13,"9":9}],6:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _errorsMssErrors = _dereq_(9);
var _errorsMssErrors2 = _interopRequireDefault(_errorsMssErrors);
/**
* @module MssFragmentMoovProcessor
* @ignore
* @param {Object} config object
*/
function MssFragmentMoovProcessor(config) {
config = config || {};
var NALUTYPE_SPS = 7;
var NALUTYPE_PPS = 8;
var constants = config.constants;
var ISOBoxer = config.ISOBoxer;
var protectionController = config.protectionController;
var instance = undefined,
period = undefined,
adaptationSet = undefined,
representation = undefined,
contentProtection = undefined,
timescale = undefined,
trackId = undefined;
function createFtypBox(isoFile) {
var ftyp = ISOBoxer.createBox('ftyp', isoFile);
ftyp.major_brand = 'iso6';
ftyp.minor_version = 1; // is an informative integer for the minor version of the major brand
ftyp.compatible_brands = []; //is a list, to the end of the box, of brands isom, iso6 and msdh
ftyp.compatible_brands[0] = 'isom'; // => decimal ASCII value for isom
ftyp.compatible_brands[1] = 'iso6'; // => decimal ASCII value for iso6
ftyp.compatible_brands[2] = 'msdh'; // => decimal ASCII value for msdh
return ftyp;
}
function createMoovBox(isoFile) {
// moov box
var moov = ISOBoxer.createBox('moov', isoFile);
// moov/mvhd
createMvhdBox(moov);
// moov/trak
var trak = ISOBoxer.createBox('trak', moov);
// moov/trak/tkhd
createTkhdBox(trak);
// moov/trak/mdia
var mdia = ISOBoxer.createBox('mdia', trak);
// moov/trak/mdia/mdhd
createMdhdBox(mdia);
// moov/trak/mdia/hdlr
createHdlrBox(mdia);
// moov/trak/mdia/minf
var minf = ISOBoxer.createBox('minf', mdia);
switch (adaptationSet.type) {
case constants.VIDEO:
// moov/trak/mdia/minf/vmhd
createVmhdBox(minf);
break;
case constants.AUDIO:
// moov/trak/mdia/minf/smhd
createSmhdBox(minf);
break;
default:
break;
}
// moov/trak/mdia/minf/dinf
var dinf = ISOBoxer.createBox('dinf', minf);
// moov/trak/mdia/minf/dinf/dref
createDrefBox(dinf);
// moov/trak/mdia/minf/stbl
var stbl = ISOBoxer.createBox('stbl', minf);
// Create empty stts, stsc, stco and stsz boxes
// Use data field as for codem-isoboxer unknown boxes for setting fields value
// moov/trak/mdia/minf/stbl/stts
var stts = ISOBoxer.createFullBox('stts', stbl);
stts._data = [0, 0, 0, 0, 0, 0, 0, 0]; // version = 0, flags = 0, entry_count = 0
// moov/trak/mdia/minf/stbl/stsc
var stsc = ISOBoxer.createFullBox('stsc', stbl);
stsc._data = [0, 0, 0, 0, 0, 0, 0, 0]; // version = 0, flags = 0, entry_count = 0
// moov/trak/mdia/minf/stbl/stco
var stco = ISOBoxer.createFullBox('stco', stbl);
stco._data = [0, 0, 0, 0, 0, 0, 0, 0]; // version = 0, flags = 0, entry_count = 0
// moov/trak/mdia/minf/stbl/stsz
var stsz = ISOBoxer.createFullBox('stsz', stbl);
stsz._data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // version = 0, flags = 0, sample_size = 0, sample_count = 0
// moov/trak/mdia/minf/stbl/stsd
createStsdBox(stbl);
// moov/mvex
var mvex = ISOBoxer.createBox('mvex', moov);
// moov/mvex/trex
createTrexBox(mvex);
if (contentProtection && protectionController) {
var supportedKS = protectionController.getSupportedKeySystemsFromContentProtection(contentProtection);
createProtectionSystemSpecificHeaderBox(moov, supportedKS);
}
}
function createMvhdBox(moov) {
var mvhd = ISOBoxer.createFullBox('mvhd', moov);
mvhd.version = 1; // version = 1 in order to have 64bits duration value
mvhd.creation_time = 0; // the creation time of the presentation => ignore (set to 0)
mvhd.modification_time = 0; // the most recent time the presentation was modified => ignore (set to 0)
mvhd.timescale = timescale; // the time-scale for the entire presentation => 10000000 for MSS
mvhd.duration = Math.round(period.duration * timescale); // the length of the presentation (in the indicated timescale) => take duration of period
mvhd.rate = 1.0; // 16.16 number, '1.0' = normal playback
mvhd.volume = 1.0; // 8.8 number, '1.0' = full volume
mvhd.reserved1 = 0;
mvhd.reserved2 = [0x0, 0x0];
mvhd.matrix = [1, 0, 0, // provides a transformation matrix for the video;
0, 1, 0, // (u,v,w) are restricted here to (0,0,1)
0, 0, 16384];
mvhd.pre_defined = [0, 0, 0, 0, 0, 0];
mvhd.next_track_ID = trackId + 1; // indicates a value to use for the track ID of the next track to be added to this presentation
return mvhd;
}
function createTkhdBox(trak) {
var tkhd = ISOBoxer.createFullBox('tkhd', trak);
tkhd.version = 1; // version = 1 in order to have 64bits duration value
tkhd.flags = 0x1 | // Track_enabled (0x000001): Indicates that the track is enabled
0x2 | // Track_in_movie (0x000002): Indicates that the track is used in the presentation
0x4; // Track_in_preview (0x000004): Indicates that the track is used when previewing the presentation
tkhd.creation_time = 0; // the creation time of the presentation => ignore (set to 0)
tkhd.modification_time = 0; // the most recent time the presentation was modified => ignore (set to 0)
tkhd.track_ID = trackId; // uniquely identifies this track over the entire life-time of this presentation
tkhd.reserved1 = 0;
tkhd.duration = Math.round(period.duration * timescale); // the duration of this track (in the timescale indicated in the Movie Header Box) => take duration of period
tkhd.reserved2 = [0x0, 0x0];
tkhd.layer = 0; // specifies the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer => 0 since only one video track
tkhd.alternate_group = 0; // specifies a group or collection of tracks => ignore
tkhd.volume = 1.0; // '1.0' = full volume
tkhd.reserved3 = 0;
tkhd.matrix = [1, 0, 0, // provides a transformation matrix for the video;
0, 1, 0, // (u,v,w) are restricted here to (0,0,1)
0, 0, 16384];
tkhd.width = representation.width; // visual presentation width
tkhd.height = representation.height; // visual presentation height
return tkhd;
}
function createMdhdBox(mdia) {
var mdhd = ISOBoxer.createFullBox('mdhd', mdia);
mdhd.version = 1; // version = 1 in order to have 64bits duration value
mdhd.creation_time = 0; // the creation time of the presentation => ignore (set to 0)
mdhd.modification_time = 0; // the most recent time the presentation was modified => ignore (set to 0)
mdhd.timescale = timescale; // the time-scale for the entire presentation
mdhd.duration = Math.round(period.duration * timescale); // the duration of this media (in the scale of the timescale). If the duration cannot be determined then duration is set to all 1s.
mdhd.language = adaptationSet.lang || 'und'; // declares the language code for this media (see getLanguageCode())
mdhd.pre_defined = 0;
return mdhd;
}
function createHdlrBox(mdia) {
var hdlr = ISOBoxer.createFullBox('hdlr', mdia);
hdlr.pre_defined = 0;
switch (adaptationSet.type) {
case constants.VIDEO:
hdlr.handler_type = 'vide';
break;
case constants.AUDIO:
hdlr.handler_type = 'soun';
break;
default:
hdlr.handler_type = 'meta';
break;
}
hdlr.name = representation.id;
hdlr.reserved = [0, 0, 0];
return hdlr;
}
function createVmhdBox(minf) {
var vmhd = ISOBoxer.createFullBox('vmhd', minf);
vmhd.flags = 1;
vmhd.graphicsmode = 0; // specifies a composition mode for this video track, from the following enumerated set, which may be extended by derived specifications: copy = 0 copy over the existing image
vmhd.opcolor = [0, 0, 0]; // is a set of 3 colour values (red, green, blue) available for use by graphics modes
return vmhd;
}
function createSmhdBox(minf) {
var smhd = ISOBoxer.createFullBox('smhd', minf);
smhd.flags = 1;
smhd.balance = 0; // is a fixed-point 8.8 number that places mono audio tracks in a stereo space; 0 is centre (the normal value); full left is -1.0 and full right is 1.0.
smhd.reserved = 0;
return smhd;
}
function createDrefBox(dinf) {
var dref = ISOBoxer.createFullBox('dref', dinf);
dref.entry_count = 1;
dref.entries = [];
var url = ISOBoxer.createFullBox('url ', dref, false);
url.location = '';
url.flags = 1;
dref.entries.push(url);
return dref;
}
function createStsdBox(stbl) {
var stsd = ISOBoxer.createFullBox('stsd', stbl);
stsd.entries = [];
switch (adaptationSet.type) {
case constants.VIDEO:
case constants.AUDIO:
stsd.entries.push(createSampleEntry(stsd));
break;
default:
break;
}
stsd.entry_count = stsd.entries.length; // is an integer that counts the actual entries
return stsd;
}
function createSampleEntry(stsd) {
var codec = representation.codecs.substring(0, representation.codecs.indexOf('.'));
switch (codec) {
case 'avc1':
return createAVCVisualSampleEntry(stsd, codec);
case 'mp4a':
return createMP4AudioSampleEntry(stsd, codec);
default:
throw {
code: _errorsMssErrors2['default'].MSS_UNSUPPORTED_CODEC_CODE,
message: _errorsMssErrors2['default'].MSS_UNSUPPORTED_CODEC_MESSAGE,
data: {
codec: codec
}
};
}
}
function createAVCVisualSampleEntry(stsd, codec) {
var avc1 = undefined;
if (contentProtection) {
avc1 = ISOBoxer.createBox('encv', stsd, false);
} else {
avc1 = ISOBoxer.createBox('avc1', stsd, false);
}
// SampleEntry fields
avc1.reserved1 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
avc1.data_reference_index = 1;
// VisualSampleEntry fields
avc1.pre_defined1 = 0;
avc1.reserved2 = 0;
avc1.pre_defined2 = [0, 0, 0];
avc1.height = representation.height;
avc1.width = representation.width;
avc1.horizresolution = 72; // 72 dpi
avc1.vertresolution = 72; // 72 dpi
avc1.reserved3 = 0;
avc1.frame_count = 1; // 1 compressed video frame per sample
avc1.compressorname = [0x0A, 0x41, 0x56, 0x43, 0x20, 0x43, 0x6F, 0x64, // = 'AVC Coding';
0x69, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
avc1.depth = 0x0018; // 0x0018 – images are in colour with no alpha.
avc1.pre_defined3 = 65535;
avc1.config = createAVC1ConfigurationRecord();
if (contentProtection) {
// Create and add Protection Scheme Info Box
var sinf = ISOBoxer.createBox('sinf', avc1);
// Create and add Original Format Box => indicate codec type of the encrypted content
createOriginalFormatBox(sinf, codec);
// Create and add Scheme Type box
createSchemeTypeBox(sinf);
// Create and add Scheme Information Box
createSchemeInformationBox(sinf);
}
return avc1;
}
function createAVC1ConfigurationRecord() {
var avcC = null;
var avcCLength = 15; // length = 15 by default (0 SPS and 0 PPS)