@l5i/dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
249 lines (216 loc) • 9.67 kB
JavaScript
/**
* 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.
*/
import Constants from './constants/Constants';
import XlinkController from './controllers/XlinkController';
import HTTPLoader from './net/HTTPLoader';
import URLUtils from './utils/URLUtils';
import TextRequest from './vo/TextRequest';
import DashJSError from './vo/DashJSError';
import {HTTPRequest} from './vo/metrics/HTTPRequest';
import EventBus from '../core/EventBus';
import Events from '../core/events/Events';
import Errors from '../core/errors/Errors';
import FactoryMaker from '../core/FactoryMaker';
import DashParser from '../dash/parser/DashParser';
import Debug from '../core/Debug';
function ManifestLoader(config) {
config = config || {};
const context = this.context;
const eventBus = EventBus(context).getInstance();
const urlUtils = URLUtils(context).getInstance();
let instance,
logger,
httpLoader,
xlinkController,
parser;
let mssHandler = config.mssHandler;
let errHandler = config.errHandler;
function setup() {
logger = Debug(context).getInstance().getLogger(instance);
eventBus.on(Events.XLINK_READY, onXlinkReady, instance);
httpLoader = HTTPLoader(context).create({
errHandler: errHandler,
metricsModel: config.metricsModel,
mediaPlayerModel: config.mediaPlayerModel,
requestModifier: config.requestModifier
});
xlinkController = XlinkController(context).create({
errHandler: errHandler,
metricsModel: config.metricsModel,
mediaPlayerModel: config.mediaPlayerModel,
requestModifier: config.requestModifier
});
parser = null;
}
function onXlinkReady(event) {
eventBus.trigger(
Events.INTERNAL_MANIFEST_LOADED, {
manifest: event.manifest
}
);
}
function createParser(data) {
let parser = null;
// Analyze manifest content to detect protocol and select appropriate parser
if (data.indexOf('SmoothStreamingMedia') > -1) {
//do some business to transform it into a Dash Manifest
if (mssHandler) {
parser = mssHandler.createMssParser();
mssHandler.registerEvents();
}
return parser;
} else if (data.indexOf('MPD') > -1) {
return DashParser(context).create();
} else {
return parser;
}
}
function load(url) {
const request = new TextRequest(url, HTTPRequest.MPD_TYPE);
httpLoader.load({
request: request,
success: function (data, textStatus, responseURL) {
// Manage situations in which success is called after calling reset
if (!xlinkController) return;
let actualUrl,
baseUri,
manifest;
// Handle redirects for the MPD - as per RFC3986 Section 5.1.3
// also handily resolves relative MPD URLs to absolute
if (responseURL && responseURL !== url) {
baseUri = urlUtils.parseBaseUrl(responseURL);
actualUrl = responseURL;
} else {
// usually this case will be caught and resolved by
// responseURL above but it is not available for IE11 and Edge/12 and Edge/13
// baseUri must be absolute for BaseURL resolution later
if (urlUtils.isRelative(url)) {
url = urlUtils.resolve(url, window.location.href);
}
baseUri = urlUtils.parseBaseUrl(url);
}
// Create parser according to manifest type
if (parser === null) {
parser = createParser(data);
}
if (parser === null) {
eventBus.trigger(
Events.INTERNAL_MANIFEST_LOADED, {
manifest: null,
error: new DashJSError(
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE,
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_MESSAGE + `${url}`
)
}
);
return;
}
// init xlinkcontroller with matchers and iron object from created parser
xlinkController.setMatchers(parser.getMatchers());
xlinkController.setIron(parser.getIron());
try {
manifest = parser.parse(data);
} catch (e) {
eventBus.trigger(
Events.INTERNAL_MANIFEST_LOADED, {
manifest: null,
error: new DashJSError(
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE,
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_MESSAGE + `${url}`
)
}
);
return;
}
if (manifest) {
manifest.url = actualUrl || url;
// URL from which the MPD was originally retrieved (MPD updates will not change this value)
if (!manifest.originalUrl) {
manifest.originalUrl = manifest.url;
}
// In the following, we only use the first Location entry even if many are available
// Compare with ManifestUpdater/DashManifestModel
if (manifest.hasOwnProperty(Constants.LOCATION)) {
baseUri = urlUtils.parseBaseUrl(manifest.Location_asArray[0]);
logger.debug('BaseURI set by Location to: ' + baseUri);
}
manifest.baseUri = baseUri;
manifest.loadedTime = new Date();
xlinkController.resolveManifestOnLoad(manifest);
} else {
eventBus.trigger(
Events.INTERNAL_MANIFEST_LOADED, {
manifest: null,
error: new DashJSError(
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE,
Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_MESSAGE + `${url}`
)
}
);
}
},
error: function (request, statusText, errorText) {
eventBus.trigger(
Events.INTERNAL_MANIFEST_LOADED, {
manifest: null,
error: new DashJSError(
Errors.MANIFEST_LOADER_LOADING_FAILURE_ERROR_CODE,
Errors.MANIFEST_LOADER_LOADING_FAILURE_ERROR_MESSAGE + `${url}, ${errorText}`
)
}
);
}
});
}
function reset() {
eventBus.off(Events.XLINK_READY, onXlinkReady, instance);
if (xlinkController) {
xlinkController.reset();
xlinkController = null;
}
if (httpLoader) {
httpLoader.abort();
httpLoader = null;
}
if (mssHandler) {
mssHandler.reset();
}
}
instance = {
load: load,
reset: reset
};
setup();
return instance;
}
ManifestLoader.__dashjs_factory_name = 'ManifestLoader';
const factory = FactoryMaker.getClassFactory(ManifestLoader);
export default factory;