UNPKG

dashjs

Version:

A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.

228 lines (196 loc) 9.9 kB
/** * 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 DataChunk from '../streaming/vo/DataChunk'; import FragmentRequest from '../streaming/vo/FragmentRequest'; import MssFragmentInfoController from './MssFragmentInfoController'; import MssFragmentProcessor from './MssFragmentProcessor'; import MssParser from './parser/MssParser'; import MssErrors from './errors/MssErrors'; import DashJSError from '../streaming/vo/DashJSError'; function MssHandler(config) { config = config || {}; let context = this.context; let eventBus = config.eventBus; const events = config.events; const constants = config.constants; const initSegmentType = config.initSegmentType; let dashMetrics = config.dashMetrics; let playbackController = config.playbackController; let protectionController = config.protectionController; let mssFragmentProcessor = MssFragmentProcessor(context).create({ dashMetrics: dashMetrics, playbackController: playbackController, protectionController: protectionController, eventBus: eventBus, constants: constants, ISOBoxer: config.ISOBoxer, debug: config.debug, errHandler: config.errHandler }); let mssParser, instance; function setup() {} function onInitializationRequested(e) { let streamProcessor = e.sender.getStreamProcessor(); let request = new FragmentRequest(); let representationController = streamProcessor.getRepresentationController(); let representation = representationController.getCurrentRepresentation(); request.mediaType = representation.adaptation.type; request.type = initSegmentType; request.range = representation.range; request.quality = representation.index; request.mediaInfo = streamProcessor.getMediaInfo(); request.representationId = representation.id; const chunk = createDataChunk(request, streamProcessor.getStreamInfo().id, e.type !== events.FRAGMENT_LOADING_PROGRESS); try { // Generate initialization segment (moov) chunk.bytes = mssFragmentProcessor.generateMoov(representation); } catch (e) { config.errHandler.error(new DashJSError(e.code, e.message, e.data)); } eventBus.trigger(events.INIT_FRAGMENT_LOADED, { chunk: chunk, fragmentModel: streamProcessor.getFragmentModel() }); // Change the sender value to stop event to be propagated e.sender = null; } function createDataChunk(request, streamId, endFragment) { const chunk = new DataChunk(); chunk.streamId = streamId; chunk.mediaInfo = request.mediaInfo; chunk.segmentType = request.type; chunk.start = request.startTime; chunk.duration = request.duration; chunk.end = chunk.start + chunk.duration; chunk.index = request.index; chunk.quality = request.quality; chunk.representationId = request.representationId; chunk.endFragment = endFragment; return chunk; } function startFragmentInfoControllers() { let streamController = playbackController.getStreamController(); if (!streamController) { return; } // Create MssFragmentInfoControllers for each StreamProcessor of active stream (only for audio, video or fragmentedText) let processors = streamController.getActiveStreamProcessors(); processors.forEach(function (processor) { if (processor.getType() === constants.VIDEO || processor.getType() === constants.AUDIO || processor.getType() === constants.FRAGMENTED_TEXT) { // Check MssFragmentInfoController already registered to StreamProcessor let i; let alreadyRegistered = false; let externalControllers = processor.getExternalControllers(); for (i = 0; i < externalControllers.length; i++) { if (externalControllers[i].controllerType && externalControllers[i].controllerType === 'MssFragmentInfoController') { alreadyRegistered = true; } } if (!alreadyRegistered) { let fragmentInfoController = MssFragmentInfoController(context).create({ streamProcessor: processor, eventBus: eventBus, dashMetrics: dashMetrics, playbackController: playbackController, baseURLController: config.baseURLController, ISOBoxer: config.ISOBoxer, debug: config.debug }); fragmentInfoController.initialize(); fragmentInfoController.start(); } } }); } function onSegmentMediaLoaded(e) { if (e.error) { return; } // Process moof to transcode it from MSS to DASH let streamProcessor = e.sender.getStreamProcessor(); mssFragmentProcessor.processFragment(e, streamProcessor); // Start MssFragmentInfoControllers in case of start-over streams let streamInfo = streamProcessor.getStreamInfo(); if (!streamInfo.manifestInfo.isDynamic && streamInfo.manifestInfo.DVRWindowSize !== Infinity) { startFragmentInfoControllers(); } } function onPlaybackPaused() { if (playbackController.getIsDynamic() && playbackController.getTime() !== 0) { startFragmentInfoControllers(); } } function onPlaybackSeekAsked() { if (playbackController.getIsDynamic() && playbackController.getTime() !== 0) { startFragmentInfoControllers(); } } function onTTMLPreProcess(ttmlSubtitles) { if (!ttmlSubtitles || !ttmlSubtitles.data) { return; } ttmlSubtitles.data = ttmlSubtitles.data.replace(/http:\/\/www.w3.org\/2006\/10\/ttaf1/gi, 'http://www.w3.org/ns/ttml'); } function registerEvents() { eventBus.on(events.INIT_REQUESTED, onInitializationRequested, instance, dashjs.FactoryMaker.getSingletonFactoryByName(eventBus.getClassName()).EVENT_PRIORITY_HIGH); /* jshint ignore:line */ eventBus.on(events.PLAYBACK_PAUSED, onPlaybackPaused, instance, dashjs.FactoryMaker.getSingletonFactoryByName(eventBus.getClassName()).EVENT_PRIORITY_HIGH); /* jshint ignore:line */ eventBus.on(events.PLAYBACK_SEEK_ASKED, onPlaybackSeekAsked, instance, dashjs.FactoryMaker.getSingletonFactoryByName(eventBus.getClassName()).EVENT_PRIORITY_HIGH); /* jshint ignore:line */ eventBus.on(events.FRAGMENT_LOADING_COMPLETED, onSegmentMediaLoaded, instance, dashjs.FactoryMaker.getSingletonFactoryByName(eventBus.getClassName()).EVENT_PRIORITY_HIGH); /* jshint ignore:line */ eventBus.on(events.TTML_TO_PARSE, onTTMLPreProcess, instance); } function reset() { eventBus.off(events.INIT_REQUESTED, onInitializationRequested, this); eventBus.off(events.PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.off(events.PLAYBACK_SEEK_ASKED, onPlaybackSeekAsked, this); eventBus.off(events.FRAGMENT_LOADING_COMPLETED, onSegmentMediaLoaded, this); eventBus.off(events.TTML_TO_PARSE, onTTMLPreProcess, this); } function createMssParser() { mssParser = MssParser(context).create(config); return mssParser; } instance = { reset: reset, createMssParser: createMssParser, registerEvents: registerEvents }; setup(); return instance; } MssHandler.__dashjs_factory_name = 'MssHandler'; const factory = dashjs.FactoryMaker.getClassFactory(MssHandler); /* jshint ignore:line */ factory.errors = MssErrors; dashjs.FactoryMaker.updateClassFactory(MssHandler.__dashjs_factory_name, factory); /* jshint ignore:line */ export default factory; /* jshint ignore:line */