streaming_mediaplayer_ManifestApi.js

/**
 * 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 EventBus from '../../core/EventBus.js';
import Events from '../../core/events/Events.js';
import FactoryMaker from '../../core/FactoryMaker.js';
import {
    MEDIA_PLAYER_NOT_INITIALIZED_ERROR,
    SOURCE_NOT_ATTACHED_ERROR,
    STREAMING_NOT_INITIALIZED_ERROR
} from './MediaPlayerApiErrors.js';

function ManifestApi() {
    const context = this.context;
    const eventBus = EventBus(context).getInstance();
    let instance,
        state,
        mediaPlayer;

    function setConfig(config) {
        if (!config) {
            return;
        }
        if (config.state) {
            state = config.state;
        }
        if (config.mediaPlayer) {
            mediaPlayer = config.mediaPlayer;
        }
    }

    /**
     * This method returns the list of all available streams from a given manifest
     * @param {Object} manifest
     * @returns {Array} list of {@link StreamInfo}
     * @memberof module:MediaPlayer
     * @throws {@link module:MediaPlayer~STREAMING_NOT_INITIALIZED_ERROR STREAMING_NOT_INITIALIZED_ERROR} if called before initializePlayback function
     * @instance
     */
    function getStreamsFromManifest(manifest) {
        if (!state.streamingInitialized) {
            throw STREAMING_NOT_INITIALIZED_ERROR;
        }
        return state.adapter.getStreamsInfo(manifest);
    }

    /**
     * This method returns the list of all available tracks for a given media type and streamInfo from a given manifest
     * @param {MediaType} type
     * @param {Object} manifest
     * @param {Object} streamInfo
     * @returns {Array}  list of {@link MediaInfo}
     * @memberof module:MediaPlayer
     * @throws {@link module:MediaPlayer~STREAMING_NOT_INITIALIZED_ERROR STREAMING_NOT_INITIALIZED_ERROR} if called before initializePlayback function
     * @instance
     */
    function getTracksForTypeFromManifest(type, manifest, streamInfo) {
        if (!state.streamingInitialized) {
            throw STREAMING_NOT_INITIALIZED_ERROR;
        }

        streamInfo = streamInfo || state.adapter.getStreamsInfo(manifest, 1)[0];

        return streamInfo ? state.adapter.getAllMediaInfoForType(streamInfo, type, manifest) : [];
    }

    /**
     * Returns the source string or manifest that was attached by calling attachSource()
     * @returns {string | manifest}
     * @memberof module:MediaPlayer
     * @throws {@link module:MediaPlayer~SOURCE_NOT_ATTACHED_ERROR SOURCE_NOT_ATTACHED_ERROR} if called before attachSource function
     * @instance
     */
    function getSource() {
        if (!state.source) {
            throw SOURCE_NOT_ATTACHED_ERROR;
        }
        return state.source;
    }

    /**
     * Sets the source to a new manifest URL or object without reloading
     * Useful for updating CDN tokens
     * @param {string | object} urlOrManifest
     */
    function updateSource(urlOrManifest) {
        state.source = urlOrManifest
        state.streamController.load(state.source);
    }

    /**
     *  Reload the manifest that the player is currently using.
     *
     *  @memberof module:MediaPlayer
     *  @param {function} callback - A Callback function provided when retrieving manifests
     *  @instance
     */
    function refreshManifest(callback) {
        if (!state.mediaPlayerInitialized) {
            throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR;
        }

        if (!mediaPlayer.isReady()) {
            return callback(null, SOURCE_NOT_ATTACHED_ERROR);
        }

        let self = this;

        if (typeof callback === 'function') {
            const handler = function (e) {
                eventBus.off(Events.INTERNAL_MANIFEST_LOADED, handler, self);

                if (e.error) {
                    callback(null, e.error);
                    return;
                }

                callback(e.manifest);
            };

            eventBus.on(Events.INTERNAL_MANIFEST_LOADED, handler, self);
        }

        state.streamController.refreshManifest();
    }

    /**
     * This method returns the active stream
     *
     * @throws {@link module:MediaPlayer~STREAMING_NOT_INITIALIZED_ERROR STREAMING_NOT_INITIALIZED_ERROR} if called before initializePlayback function
     * @memberof module:MediaPlayer
     * @instance
     */
    function getActiveStream() {
        if (!state.streamingInitialized) {
            throw STREAMING_NOT_INITIALIZED_ERROR;
        }
        let streamInfo = state.streamController.getActiveStreamInfo();
        return streamInfo ? state.streamController.getStreamById(streamInfo.id) : null;
    }

    /**
     * Returns the DashAdapter.js Module.
     *
     * @see {@link module:DashAdapter}
     * @returns {Object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getDashAdapter() {
        return state.adapter;
    }

    /**
     * Triggers a request to the content steering server to update the steering information.
     * @return {Promise<any>}
     */
    function triggerSteeringRequest() {
        if (state.contentSteeringController) {
            return state.contentSteeringController.loadSteeringData();
        }
    }

    /**
     * Returns the current response data of the content steering server
     * @return {object}
     */
    function getCurrentSteeringResponseData() {
        if (state.contentSteeringController) {
            return state.contentSteeringController.getCurrentSteeringResponseData();
        }
    }

    /**
     * Returns the current manifest
     * @returns {object}
     */
    function getManifest() {
        return state.manifestModel.getValue();
    }

    /**
     * Returns all BaseURLs that are available including synthesized elements (e.g by content steering)
     * @returns {BaseURL[]}
     */
    function getAvailableBaseUrls() {
        const manifest = state.manifestModel.getValue();

        if (!manifest) {
            return [];
        }

        return state.baseURLController.getBaseUrls(manifest);
    }

    /**
     * Returns the available location elements including synthesized elements (e.g by content steering)
     * @returns {MpdLocation[]}
     */
    function getAvailableLocations() {
        const manifest = state.manifestModel.getValue();

        if (!manifest) {
            return [];
        }

        const manifestLocations = state.adapter.getLocation(manifest);
        const synthesizedElements = state.contentSteeringController.getSynthesizedLocationElements(manifestLocations);

        return manifestLocations.concat(synthesizedElements);
    }

    instance = {
        getActiveStream,
        getAvailableBaseUrls,
        getAvailableLocations,
        getCurrentSteeringResponseData,
        getDashAdapter,
        getManifest,
        getSource,
        getStreamsFromManifest,
        getTracksForTypeFromManifest,
        refreshManifest,
        setConfig,
        triggerSteeringRequest,
        updateSource,
    };

    return instance;
}

ManifestApi.__dashjs_factory_name = 'ManifestApi';
export default FactoryMaker.getClassFactory(ManifestApi);