@l5i/dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
841 lines (704 loc) • 30.7 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 StreamProcessor from './StreamProcessor';
import EventController from './controllers/EventController';
import FragmentController from './controllers/FragmentController';
import ThumbnailController from './thumbnail/ThumbnailController';
import EventBus from '../core/EventBus';
import Events from '../core/events/Events';
import Debug from '../core/Debug';
import Errors from '../core/errors/Errors';
import FactoryMaker from '../core/FactoryMaker';
import DashJSError from './vo/DashJSError';
function Stream(config) {
config = config || {};
const context = this.context;
const eventBus = EventBus(context).getInstance();
const manifestModel = config.manifestModel;
const dashManifestModel = config.dashManifestModel;
const mediaPlayerModel = config.mediaPlayerModel;
const manifestUpdater = config.manifestUpdater;
const adapter = config.adapter;
const capabilities = config.capabilities;
const errHandler = config.errHandler;
const timelineConverter = config.timelineConverter;
const metricsModel = config.metricsModel;
const abrController = config.abrController;
const playbackController = config.playbackController;
const mediaController = config.mediaController;
const textController = config.textController;
const videoModel = config.videoModel;
let instance,
logger,
streamProcessors,
isStreamActivated,
isMediaInitialized,
streamInfo,
updateError,
isUpdating,
protectionController,
fragmentController,
thumbnailController,
eventController,
preloaded,
trackChangedEvent;
const codecCompatibilityTable = [
{
'codec': 'avc1',
'compatibleCodecs': ['avc3']
},
{
'codec': 'avc3',
'compatibleCodecs': ['avc1']
}
];
function setup() {
logger = Debug(context).getInstance().getLogger(instance);
resetInitialSettings();
fragmentController = FragmentController(context).create({
mediaPlayerModel: mediaPlayerModel,
metricsModel: metricsModel,
errHandler: errHandler
});
registerEvents();
}
function registerEvents() {
eventBus.on(Events.BUFFERING_COMPLETED, onBufferingCompleted, instance);
eventBus.on(Events.DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance);
}
function unRegisterEvents() {
eventBus.off(Events.DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance);
eventBus.off(Events.BUFFERING_COMPLETED, onBufferingCompleted, instance);
}
function registerProtectionEvents() {
if (protectionController) {
eventBus.on(Events.KEY_ERROR, onProtectionError, instance);
eventBus.on(Events.SERVER_CERTIFICATE_UPDATED, onProtectionError, instance);
eventBus.on(Events.LICENSE_REQUEST_COMPLETE, onProtectionError, instance);
eventBus.on(Events.KEY_SYSTEM_SELECTED, onProtectionError, instance);
eventBus.on(Events.KEY_SESSION_CREATED, onProtectionError, instance);
eventBus.on(Events.KEY_STATUSES_CHANGED, onProtectionError, instance);
}
}
function unRegisterProtectionEvents() {
if (protectionController) {
eventBus.off(Events.KEY_ERROR, onProtectionError, instance);
eventBus.off(Events.SERVER_CERTIFICATE_UPDATED, onProtectionError, instance);
eventBus.off(Events.LICENSE_REQUEST_COMPLETE, onProtectionError, instance);
eventBus.off(Events.KEY_SYSTEM_SELECTED, onProtectionError, instance);
eventBus.off(Events.KEY_SESSION_CREATED, onProtectionError, instance);
eventBus.off(Events.KEY_STATUSES_CHANGED, onProtectionError, instance);
}
}
function initialize(StreamInfo, ProtectionController) {
streamInfo = StreamInfo;
protectionController = ProtectionController;
registerProtectionEvents();
}
/**
* Activates Stream by re-initializing some of its components
* @param {MediaSource} mediaSource
* @memberof Stream#
* @param {SourceBuffer} previousBuffers
*/
function activate(mediaSource, previousBuffers) {
if (!isStreamActivated) {
let result;
eventBus.on(Events.CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance);
if (!getPreloaded()) {
result = initializeMedia(mediaSource, previousBuffers);
} else {
initializeAfterPreload();
result = previousBuffers;
}
isStreamActivated = true;
return result;
}
return previousBuffers;
}
/**
* Partially resets some of the Stream elements
* @memberof Stream#
* @param {boolean} keepBuffers
*/
function deactivate(keepBuffers) {
let ln = streamProcessors ? streamProcessors.length : 0;
const errored = false;
for (let i = 0; i < ln; i++) {
let fragmentModel = streamProcessors[i].getFragmentModel();
fragmentModel.removeExecutedRequestsBeforeTime(getStartTime() + getDuration());
streamProcessors[i].reset(errored, keepBuffers);
}
streamProcessors = [];
isStreamActivated = false;
isMediaInitialized = false;
setPreloaded(false);
eventBus.off(Events.CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance);
}
function isActive() {
return isStreamActivated;
}
function setMediaSource(mediaSource) {
for (let i = 0; i < streamProcessors.length;) {
if (isMediaSupported(streamProcessors[i].getMediaInfo())) {
streamProcessors[i].setMediaSource(mediaSource);
i++;
} else {
streamProcessors[i].reset();
streamProcessors.splice(i,1);
}
}
for (let i = 0; i < streamProcessors.length; i++) {
//Adding of new tracks to a stream processor isn't guaranteed by the spec after the METADATA_LOADED state
//so do this after the buffers are created above.
streamProcessors[i].dischargePreBuffer();
}
if (streamProcessors.length === 0) {
let msg = 'No streams to play.';
errHandler.manifestError(msg, 'nostreams', manifestModel.getValue());
errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE, msg + 'nostreams', manifestModel.getValue()));
logger.fatal(msg);
}
}
function resetInitialSettings() {
deactivate();
streamInfo = null;
updateError = {};
isUpdating = false;
}
function reset() {
if (playbackController) {
playbackController.pause();
}
if (fragmentController) {
fragmentController.reset();
fragmentController = null;
}
resetInitialSettings();
unRegisterEvents();
unRegisterProtectionEvents();
setPreloaded(false);
}
function getDuration() {
return streamInfo ? streamInfo.duration : NaN;
}
function getStartTime() {
return streamInfo ? streamInfo.start : NaN;
}
function getId() {
return streamInfo ? streamInfo.id : null;
}
function getStreamInfo() {
return streamInfo;
}
function getFragmentController() {
return fragmentController;
}
function getThumbnailController() {
return thumbnailController;
}
function checkConfig() {
if (!abrController || !abrController.hasOwnProperty('getBitrateList') || !adapter || !adapter.hasOwnProperty('getAllMediaInfoForType') || !adapter.hasOwnProperty('getEventsFor')) {
throw new Error('Missing config parameter(s)');
}
}
/**
* @param {string} type
* @returns {Array}
* @memberof Stream#
*/
function getBitrateListFor(type) {
checkConfig();
if (type === Constants.IMAGE) {
if (!thumbnailController) {
return [];
}
return thumbnailController.getBitrateList();
}
const mediaInfo = getMediaInfo(type);
return abrController.getBitrateList(mediaInfo);
}
function startEventController() {
if (eventController) {
eventController.start();
}
}
function stopEventController() {
if (eventController) {
eventController.stop();
}
}
function onProtectionError(event) {
if (event.error) {
errHandler.mediaKeySessionError(event.error.message);
errHandler.error(event.error);
logger.fatal(event.error.message);
reset();
}
}
function isMediaSupported(mediaInfo) {
const type = mediaInfo.type;
let codec,
msg;
if (type === Constants.MUXED && mediaInfo) {
msg = 'Multiplexed representations are intentionally not supported, as they are not compliant with the DASH-AVC/264 guidelines';
logger.fatal(msg);
errHandler.manifestError(msg, 'multiplexedrep', manifestModel.getValue());
errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_MULTIPLEXED_CODE, msg, manifestModel.getValue()));
return false;
}
if (type === Constants.TEXT || type === Constants.FRAGMENTED_TEXT || type === Constants.EMBEDDED_TEXT || type === Constants.IMAGE) {
return true;
}
codec = mediaInfo.codec;
logger.debug(type + ' codec: ' + codec);
if (!!mediaInfo.contentProtection && !capabilities.supportsEncryptedMedia()) {
errHandler.capabilityError('encryptedmedia');
errHandler.error(new DashJSError(Errors.CAPABILITY_MEDIAKEYS_ERROR_CODE, Errors.CAPABILITY_MEDIAKEYS_ERROR_MESSAGE));
} else if (!capabilities.supportsCodec(codec)) {
msg = type + 'Codec (' + codec + ') is not supported.';
logger.error(msg);
return false;
}
return true;
}
function onCurrentTrackChanged(e) {
if (e.newMediaInfo.streamInfo.id !== streamInfo.id) return;
let processor = getProcessorForMediaInfo(e.newMediaInfo);
if (!processor) return;
let currentTime = playbackController.getTime();
logger.info('Stream - Process track changed at current time ' + currentTime);
let mediaInfo = e.newMediaInfo;
let manifest = manifestModel.getValue();
logger.debug('Stream - Update stream controller');
if (manifest.refreshManifestOnSwitchTrack) {
logger.debug('Stream - Refreshing manifest for switch track');
trackChangedEvent = e;
manifestUpdater.refreshManifest();
} else {
processor.selectMediaInfo(mediaInfo);
if (mediaInfo.type !== Constants.FRAGMENTED_TEXT) {
abrController.updateTopQualityIndex(mediaInfo);
processor.switchTrackAsked();
processor.getFragmentModel().abortRequests();
} else {
processor.getScheduleController().setSeekTarget(NaN);
adapter.setIndexHandlerTime(processor, currentTime);
adapter.resetIndexHandler(processor);
}
}
}
function createStreamProcessor(mediaInfo, allMediaForType, mediaSource, optionalSettings) {
let streamProcessor = StreamProcessor(context).create({
type: mediaInfo.type,
mimeType: mediaInfo.mimeType,
timelineConverter: timelineConverter,
adapter: adapter,
manifestModel: manifestModel,
dashManifestModel: dashManifestModel,
mediaPlayerModel: mediaPlayerModel,
metricsModel: metricsModel,
dashMetrics: config.dashMetrics,
baseURLController: config.baseURLController,
stream: instance,
abrController: abrController,
domStorage: config.domStorage,
playbackController: playbackController,
mediaController: mediaController,
streamController: config.streamController,
textController: textController,
errHandler: errHandler
});
streamProcessor.initialize(mediaSource);
abrController.updateTopQualityIndex(mediaInfo);
if (optionalSettings) {
streamProcessor.setBuffer(optionalSettings.buffer);
streamProcessor.getIndexHandler().setCurrentTime(optionalSettings.currentTime);
streamProcessors[optionalSettings.replaceIdx] = streamProcessor;
} else {
streamProcessors.push(streamProcessor);
}
if (optionalSettings && optionalSettings.ignoreMediaInfo) {
return;
}
if ((mediaInfo.type === Constants.TEXT || mediaInfo.type === Constants.FRAGMENTED_TEXT)) {
let idx;
for (let i = 0; i < allMediaForType.length; i++) {
if (allMediaForType[i].index === mediaInfo.index) {
idx = i;
}
streamProcessor.addMediaInfo(allMediaForType[i]); //creates text tracks for all adaptations in one stream processor
}
streamProcessor.selectMediaInfo(allMediaForType[idx]); //sets the initial media info
} else {
streamProcessor.addMediaInfo(mediaInfo, true);
}
}
function initializeMediaForType(type, mediaSource) {
const allMediaForType = adapter.getAllMediaInfoForType(streamInfo, type);
let mediaInfo = null;
let initialMediaInfo;
if (!allMediaForType || allMediaForType.length === 0) {
logger.info('No ' + type + ' data.');
return;
}
for (let i = 0, ln = allMediaForType.length; i < ln; i++) {
mediaInfo = allMediaForType[i];
if (type === Constants.EMBEDDED_TEXT) {
textController.addEmbeddedTrack(mediaInfo);
} else {
if (!isMediaSupported(mediaInfo)) continue;
mediaController.addTrack(mediaInfo);
}
}
if (type === Constants.EMBEDDED_TEXT || mediaController.getTracksFor(type, streamInfo).length === 0) {
return;
}
if (type === Constants.IMAGE) {
thumbnailController = ThumbnailController(context).create({
dashManifestModel: dashManifestModel,
adapter: adapter,
baseURLController: config.baseURLController,
stream: instance
});
return;
}
if (type !== Constants.FRAGMENTED_TEXT || (type === Constants.FRAGMENTED_TEXT && textController.getTextDefaultEnabled())) {
mediaController.checkInitialMediaSettingsForType(type, streamInfo);
initialMediaInfo = mediaController.getCurrentTrackFor(type, streamInfo);
}
if (type === Constants.FRAGMENTED_TEXT && !textController.getTextDefaultEnabled()) {
initialMediaInfo = mediaController.getTracksFor(type, streamInfo)[0];
}
// TODO : How to tell index handler live/duration?
// TODO : Pass to controller and then pass to each method on handler?
createStreamProcessor(initialMediaInfo, allMediaForType, mediaSource);
}
function initializeEventController () {
//if initializeMedia is called from a switch period, eventController could have been already created.
if (!eventController) {
eventController = EventController(context).create();
eventController.setConfig({
manifestUpdater: manifestUpdater,
playbackController: playbackController
});
addInlineEvents();
}
}
function addInlineEvents () {
const events = adapter.getEventsFor(streamInfo);
eventController.addInlineEvents(events);
}
function addInbandEvents (events) {
if (eventController) {
eventController.addInbandEvents(events);
}
}
function initializeMedia(mediaSource, previousBuffers) {
checkConfig();
let element = videoModel.getElement();
initializeEventController();
isUpdating = true;
filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
if (element === null || (element && (/^VIDEO$/i).test(element.nodeName))) {
initializeMediaForType(Constants.VIDEO, mediaSource);
}
initializeMediaForType(Constants.AUDIO, mediaSource);
initializeMediaForType(Constants.TEXT, mediaSource);
initializeMediaForType(Constants.FRAGMENTED_TEXT, mediaSource);
initializeMediaForType(Constants.EMBEDDED_TEXT, mediaSource);
initializeMediaForType(Constants.MUXED, mediaSource);
initializeMediaForType(Constants.IMAGE, mediaSource);
//TODO. Consider initialization of TextSourceBuffer here if embeddedText, but no sideloadedText.
const buffers = createBuffers(previousBuffers);
isMediaInitialized = true;
isUpdating = false;
if (streamProcessors.length === 0) {
const msg = 'No streams to play.';
errHandler.manifestError(msg, 'nostreams', manifestModel.getValue());
errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE, msg, manifestModel.getValue()));
logger.fatal(msg);
} else {
checkIfInitializationCompleted();
}
return buffers;
}
function initializeAfterPreload() {
isUpdating = true;
checkConfig();
filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
isMediaInitialized = true;
isUpdating = false;
if (streamProcessors.length === 0) {
let msg = 'No streams to play.';
errHandler.manifestError(msg, 'nostreams', manifestModel.getValue());
logger.debug(msg);
} else {
checkIfInitializationCompleted();
}
}
function filterCodecs(type) {
const realAdaptation = dashManifestModel.getAdaptationForType(manifestModel.getValue(), streamInfo.index, type, streamInfo);
if (!realAdaptation || !Array.isArray(realAdaptation.Representation_asArray)) return;
// Filter codecs that are not supported
realAdaptation.Representation_asArray = realAdaptation.Representation_asArray.filter((_, i) => {
// keep at least codec from lowest representation
if (i === 0) return true;
const codec = dashManifestModel.getCodec(realAdaptation, i, true);
if (!capabilities.supportsCodec(codec)) {
logger.error('[Stream] codec not supported: ' + codec);
return false;
}
return true;
});
}
function checkIfInitializationCompleted() {
const ln = streamProcessors.length;
const hasError = !!updateError.audio || !!updateError.video;
let error = hasError ? new DashJSError(Errors.DATA_UPDATE_FAILED_ERROR_CODE, Errors.DATA_UPDATE_FAILED_ERROR_MESSAGE) : null;
for (let i = 0; i < ln; i++) {
if (streamProcessors[i].isUpdating() || isUpdating) {
return;
}
}
if (!isMediaInitialized) {
return;
}
if (protectionController) {
// Need to check if streamProcessors exists because streamProcessors
// could be cleared in case an error is detected while initializing DRM keysystem
for (let i = 0; i < ln && streamProcessors[i]; i++) {
if (streamProcessors[i].getType() === Constants.AUDIO ||
streamProcessors[i].getType() === Constants.VIDEO ||
streamProcessors[i].getType() === Constants.FRAGMENTED_TEXT) {
protectionController.initializeForMedia(streamProcessors[i].getMediaInfo());
}
}
}
eventBus.trigger(Events.STREAM_INITIALIZED, {
streamInfo: streamInfo,
error: error
});
}
function getMediaInfo(type) {
const ln = streamProcessors.length;
let streamProcessor = null;
for (let i = 0; i < ln; i++) {
streamProcessor = streamProcessors[i];
if (streamProcessor.getType() === type) {
return streamProcessor.getMediaInfo();
}
}
return null;
}
function createBuffers(previousBuffers) {
const buffers = {};
for (let i = 0, ln = streamProcessors.length; i < ln; i++) {
buffers[streamProcessors[i].getType()] = streamProcessors[i].createBuffer(previousBuffers).getBuffer();
}
return buffers;
}
function onBufferingCompleted(e) {
if (e.streamInfo !== streamInfo) {
return;
}
let processors = getProcessors();
const ln = processors.length;
if (ln === 0) {
logger.warn('onBufferingCompleted - can\'t trigger STREAM_BUFFERING_COMPLETED because no streamProcessor is defined');
return;
}
// if there is at least one buffer controller that has not completed buffering yet do nothing
for (let i = 0; i < ln; i++) {
//if audio or video buffer is not buffering completed state, do not send STREAM_BUFFERING_COMPLETED
if (!processors[i].isBufferingCompleted() && (processors[i].getType() === Constants.AUDIO || processors[i].getType() === Constants.VIDEO)) {
logger.warn('onBufferingCompleted - can\'t trigger STREAM_BUFFERING_COMPLETED because streamProcessor ' + processors[i].getType() + ' is not buffering completed');
return;
}
}
logger.debug('onBufferingCompleted - trigger STREAM_BUFFERING_COMPLETED');
eventBus.trigger(Events.STREAM_BUFFERING_COMPLETED, {
streamInfo: streamInfo
});
}
function onDataUpdateCompleted(e) {
let sp = e.sender.getStreamProcessor();
if (sp.getStreamInfo() !== streamInfo) {
return;
}
updateError[sp.getType()] = e.error;
checkIfInitializationCompleted();
}
function getProcessorForMediaInfo(mediaInfo) {
if (!mediaInfo) {
return null;
}
let processors = getProcessors();
return processors.filter(function (processor) {
return (processor.getType() === mediaInfo.type);
})[0];
}
function getProcessors() {
const ln = streamProcessors.length;
let arr = [];
let type,
streamProcessor;
for (let i = 0; i < ln; i++) {
streamProcessor = streamProcessors[i];
type = streamProcessor.getType();
if (type === Constants.AUDIO || type === Constants.VIDEO || type === Constants.FRAGMENTED_TEXT || type === Constants.TEXT) {
arr.push(streamProcessor);
}
}
return arr;
}
function updateData(updatedStreamInfo) {
logger.info('Manifest updated... updating data system wide.');
isStreamActivated = false;
isUpdating = true;
streamInfo = updatedStreamInfo;
if (eventController) {
addInlineEvents();
}
filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
for (let i = 0, ln = streamProcessors.length; i < ln; i++) {
let streamProcessor = streamProcessors[i];
let mediaInfo = adapter.getMediaInfoForType(streamInfo, streamProcessor.getType());
abrController.updateTopQualityIndex(mediaInfo);
streamProcessor.addMediaInfo(mediaInfo, true);
}
if (trackChangedEvent) {
let mediaInfo = trackChangedEvent.newMediaInfo;
if (mediaInfo.type !== 'fragmentedText') {
let processor = getProcessorForMediaInfo(trackChangedEvent.oldMediaInfo);
if (!processor) return;
processor.switchTrackAsked();
trackChangedEvent = undefined;
}
}
isUpdating = false;
checkIfInitializationCompleted();
}
function isCompatibleWithStream(stream) {
return compareCodecs(stream, Constants.VIDEO) && compareCodecs(stream, Constants.AUDIO);
}
function compareCodecs(stream, type) {
if (!stream) {
return false;
}
const newStreamInfo = stream.getStreamInfo();
const currentStreamInfo = getStreamInfo();
if (!newStreamInfo || !currentStreamInfo) {
return false;
}
const newAdaptation = dashManifestModel.getAdaptationForType(manifestModel.getValue(), newStreamInfo.index, type, newStreamInfo);
const currentAdaptation = dashManifestModel.getAdaptationForType(manifestModel.getValue(), currentStreamInfo.index, type, currentStreamInfo);
if (!newAdaptation || !currentAdaptation) {
// If there is no adaptation for neither the old or the new stream they're compatible
return !newAdaptation && !currentAdaptation;
}
const sameMimeType = newAdaptation && currentAdaptation && newAdaptation.mimeType === currentAdaptation.mimeType;
const oldCodecs = currentAdaptation.Representation_asArray.map((representation) => {
return representation.codecs;
});
const newCodecs = newAdaptation.Representation_asArray.map((representation) => {
return representation.codecs;
});
const codecMatch = newCodecs.some((newCodec) => {
return oldCodecs.indexOf(newCodec) > -1;
});
const partialCodecMatch = newCodecs.some((newCodec) => oldCodecs.some((oldCodec) => codecRootCompatibleWithCodec(oldCodec, newCodec)));
return codecMatch || (partialCodecMatch && sameMimeType);
}
// Check if the root of the old codec is the same as the new one, or if it's declared as compatible in the compat table
function codecRootCompatibleWithCodec(codec1, codec2) {
const codecRoot = codec1.split('.')[0];
const compatTableCodec = codecCompatibilityTable.find((compat) => compat.codec === codecRoot);
const rootCompatible = codec2.indexOf(codecRoot) === 0;
if (compatTableCodec) {
return rootCompatible || compatTableCodec.compatibleCodecs.some((compatibleCodec) => codec2.indexOf(compatibleCodec) === 0);
}
return rootCompatible;
}
function setPreloaded(value) {
preloaded = value;
}
function getPreloaded() {
return preloaded;
}
function preload(mediaSource, previousBuffers) {
initializeEventController();
initializeMediaForType(Constants.VIDEO, mediaSource);
initializeMediaForType(Constants.AUDIO, mediaSource);
initializeMediaForType(Constants.TEXT, mediaSource);
initializeMediaForType(Constants.FRAGMENTED_TEXT, mediaSource);
initializeMediaForType(Constants.EMBEDDED_TEXT, mediaSource);
initializeMediaForType(Constants.MUXED, mediaSource);
initializeMediaForType(Constants.IMAGE, mediaSource);
createBuffers(previousBuffers);
eventBus.on(Events.CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance);
for (let i = 0; i < streamProcessors.length && streamProcessors[i]; i++) {
streamProcessors[i].getScheduleController().start();
}
setPreloaded(true);
}
instance = {
initialize: initialize,
activate: activate,
deactivate: deactivate,
isActive: isActive,
getDuration: getDuration,
getStartTime: getStartTime,
getId: getId,
getStreamInfo: getStreamInfo,
preload: preload,
getFragmentController: getFragmentController,
getThumbnailController: getThumbnailController,
getBitrateListFor: getBitrateListFor,
startEventController: startEventController,
stopEventController: stopEventController,
updateData: updateData,
reset: reset,
getProcessors: getProcessors,
setMediaSource: setMediaSource,
isCompatibleWithStream: isCompatibleWithStream,
getPreloaded: getPreloaded,
addInbandEvents: addInbandEvents
};
setup();
return instance;
}
Stream.__dashjs_factory_name = 'Stream';
export default FactoryMaker.getClassFactory(Stream);