# Source: streaming/protection/models/ProtectionModel_3Feb2014.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.
     */

    /**
     * Implementation of the EME APIs as of the 3 Feb 2014 state of the specification.
     *
     * Implemented by Internet Explorer 11 (Windows 8.1)
     *
     * @implements ProtectionModel
     * @class
     */

    import ProtectionKeyController from '../controllers/ProtectionKeyController';
    import NeedKey from '../vo/NeedKey';
    import DashJSError from '../../vo/DashJSError';
    import ProtectionErrors from '../errors/ProtectionErrors';
    import KeyMessage from '../vo/KeyMessage';
    import KeySystemConfiguration from '../vo/KeySystemConfiguration';
    import KeySystemAccess from '../vo/KeySystemAccess';

    function ProtectionModel_3Feb2014(config) {

        config = config || {};
        const context = this.context;
        const eventBus = config.eventBus;//Need to pass in here so we can use same instance since this is optional module
        const events = config.events;
        const debug = config.debug;
        const api = config.api;

        let instance,
            logger,
            videoElement,
            keySystem,
            mediaKeys,
            keySystemAccess,
            sessions,
            eventHandler,
            protectionKeyController;

        function setup() {
            logger = debug.getLogger(instance);
            videoElement = null;
            keySystem = null;
            mediaKeys = null;
            keySystemAccess = null;
            sessions = [];
            protectionKeyController = ProtectionKeyController(context).getInstance();
            eventHandler = createEventHandler();
        }

        function reset() {
            try {
                for (let i = 0; i < sessions.length; i++) {
                    closeKeySession(sessions[i]);
                }
                if (videoElement) {
                    videoElement.removeEventListener(api.needkey, eventHandler);
                }
                eventBus.trigger(events.TEARDOWN_COMPLETE);
            } catch (error) {
                eventBus.trigger(events.TEARDOWN_COMPLETE, { error: 'Error tearing down key sessions and MediaKeys! -- ' + error.message });
            }
        }

        function getAllInitData() {
            const retVal = [];
            for (let i = 0; i < sessions.length; i++) {
                retVal.push(sessions[i].initData);
            }
            return retVal;
        }

        function getSessions() {
            return sessions;
        }

        function requestKeySystemAccess(ksConfigurations) {
            return new Promise((resolve, reject) => {
                // Try key systems in order, first one with supported key system configuration
                // is used
                let found = false;
                for (let ksIdx = 0; ksIdx < ksConfigurations.length; ksIdx++) {
                    const systemString = ksConfigurations[ksIdx].ks.systemString;
                    const configs = ksConfigurations[ksIdx].configs;
                    let supportedAudio = null;
                    let supportedVideo = null;

                    // Try key system configs in order, first one with supported audio/video
                    // is used
                    for (let configIdx = 0; configIdx < configs.length; configIdx++) {
                        const audios = configs[configIdx].audioCapabilities;
                        const videos = configs[configIdx].videoCapabilities;

                        // Look for supported audio container/codecs
                        if (audios && audios.length !== 0) {
                            supportedAudio = []; // Indicates that we have a requested audio config
                            for (let audioIdx = 0; audioIdx < audios.length; audioIdx++) {
                                if (window[api.MediaKeys].isTypeSupported(systemString, audios[audioIdx].contentType)) {
                                    supportedAudio.push(audios[audioIdx]);
                                }
                            }
                        }

                        // Look for supported video container/codecs
                        if (videos && videos.length !== 0) {
                            supportedVideo = []; // Indicates that we have a requested video config
                            for (let videoIdx = 0; videoIdx < videos.length; videoIdx++) {
                                if (window[api.MediaKeys].isTypeSupported(systemString, videos[videoIdx].contentType)) {
                                    supportedVideo.push(videos[videoIdx]);
                                }
                            }
                        }

                        // No supported audio or video in this configuration OR we have
                        // requested audio or video configuration that is not supported
                        if ((!supportedAudio && !supportedVideo) ||
                            (supportedAudio && supportedAudio.length === 0) ||
                            (supportedVideo && supportedVideo.length === 0)) {
                            continue;
                        }

                        // This configuration is supported
                        found = true;
                        const ksConfig = new KeySystemConfiguration(supportedAudio, supportedVideo);
                        const ks = protectionKeyController.getKeySystemBySystemString(systemString);
                        const keySystemAccess = new KeySystemAccess(ks, ksConfig);
                        eventBus.trigger(events.KEY_SYSTEM_ACCESS_COMPLETE, { data: keySystemAccess });
                        resolve({ data: keySystemAccess });
                        break;
                    }
                }
                if (!found) {
                    const errorMessage = 'Key system access denied! -- No valid audio/video content configurations detected!';
                    eventBus.trigger(events.KEY_SYSTEM_ACCESS_COMPLETE, { error: errorMessage });
                    reject({ error: errorMessage });
                }
            })
        }

        function selectKeySystem(ksAccess) {
            return new Promise((resolve, reject) => {
                try {
                    mediaKeys = ksAccess.mediaKeys = new window[api.MediaKeys](ksAccess.keySystem.systemString);
                    keySystem = ksAccess.keySystem;
                    keySystemAccess = ksAccess;
                    if (videoElement) {
                        setMediaKeys();
                    }
                    resolve(keySystem);
                } catch (error) {
                    reject({ error: 'Error selecting keys system (' + keySystem.systemString + ')! Could not create MediaKeys -- TODO' });
                }
            })
        }

        function setMediaElement(mediaElement) {
            if (videoElement === mediaElement)
                return;

            // Replacing the previous element
            if (videoElement) {
                videoElement.removeEventListener(api.needkey, eventHandler);
            }

            videoElement = mediaElement;

            // Only if we are not detaching from the existing element
            if (videoElement) {
                videoElement.addEventListener(api.needkey, eventHandler);
                if (mediaKeys) {
                    setMediaKeys();
                }
            }
        }

        function createKeySession(ksInfo) {
            if (!keySystem || !mediaKeys || !keySystemAccess) {
                throw new Error('Can not create sessions until you have selected a key system');
            }

            // Use the first video capability for the contentType.
            // TODO:  Not sure if there is a way to concatenate all capability data into a RFC6386-compatible format

            // If player is trying to playback Audio only stream - don't error out.
            let capabilities = null;

            if (keySystemAccess.ksConfiguration.videoCapabilities && keySystemAccess.ksConfiguration.videoCapabilities.length > 0) {
                capabilities = keySystemAccess.ksConfiguration.videoCapabilities[0];
            }

            if (capabilities === null && keySystemAccess.ksConfiguration.audioCapabilities && keySystemAccess.ksConfiguration.audioCapabilities.length > 0) {
                capabilities = keySystemAccess.ksConfiguration.audioCapabilities[0];
            }

            if (capabilities === null) {
                throw new Error('Can not create sessions for unknown content types.');
            }

            const contentType = capabilities.contentType;
            const session = mediaKeys.createSession(contentType, new Uint8Array(ksInfo.initData), ksInfo.cdmData ? new Uint8Array(ksInfo.cdmData) : null);
            const sessionToken = createSessionToken(session, ksInfo);

            // Add all event listeners
            session.addEventListener(api.error, sessionToken);
            session.addEventListener(api.message, sessionToken);
            session.addEventListener(api.ready, sessionToken);
            session.addEventListener(api.close, sessionToken);

            // Add to our session list
            sessions.push(sessionToken);
            logger.debug('DRM: Session created.  SessionID = ' + sessionToken.getSessionId());
            eventBus.trigger(events.KEY_SESSION_CREATED, { data: sessionToken });
        }

        function updateKeySession(sessionToken, message) {
            const session = sessionToken.session;

            if (!protectionKeyController.isClearKey(keySystem)) {
                // Send our request to the key session
                session.update(new Uint8Array(message));
            } else {
                // For clearkey, message is a ClearKeyKeySet
                session.update(new Uint8Array(message.toJWK()));
            }
            eventBus.trigger(events.KEY_SESSION_UPDATED);
        }

        /**
         * Close the given session and release all associated keys.  Following
         * this call, the sessionToken becomes invalid
         *
         * @param {Object} sessionToken - the session token
         */
        function closeKeySession(sessionToken) {
            const session = sessionToken.session;

            // Remove event listeners
            session.removeEventListener(api.error, sessionToken);
            session.removeEventListener(api.message, sessionToken);
            session.removeEventListener(api.ready, sessionToken);
            session.removeEventListener(api.close, sessionToken);

            // Remove from our session list
            for (let i = 0; i < sessions.length; i++) {
                if (sessions[i] === sessionToken) {
                    sessions.splice(i, 1);
                    break;
                }
            }

            // Send our request to the key session
            session[api.release]();
        }

        function setServerCertificate(/*serverCertificate*/) { /* Not supported */
        }

        function loadKeySession(/*ksInfo*/) { /* Not supported */
        }

        function removeKeySession(/*sessionToken*/) { /* Not supported */
        }

        function createEventHandler() {
            return {
                handleEvent: function (event) {
                    switch (event.type) {

                        case api.needkey:
                            if (event.initData) {
                                const initData = ArrayBuffer.isView(event.initData) ? event.initData.buffer : event.initData;
                                eventBus.trigger(events.NEED_KEY, { key: new NeedKey(initData, 'cenc') });
                            }
                            break;
                    }
                }
            };
        }

        // IE11 does not let you set MediaKeys until it has entered a certain
        // readyState, so we need this logic to ensure we don't set the keys
        // too early
        function setMediaKeys() {
            let boundDoSetKeys = null;
            const doSetKeys = function () {
                videoElement.removeEventListener('loadedmetadata', boundDoSetKeys);
                videoElement[api.setMediaKeys](mediaKeys);
                eventBus.trigger(events.VIDEO_ELEMENT_SELECTED);
            };
            if (videoElement.readyState >= 1) {
                doSetKeys();
            } else {
                boundDoSetKeys = doSetKeys.bind(this);
                videoElement.addEventListener('loadedmetadata', boundDoSetKeys);
            }

        }

        // Function to create our session token objects which manage the EME
        // MediaKeySession and session-specific event handler
        function createSessionToken(keySession, ksInfo) {
            return {
                // Implements SessionToken
                session: keySession,
                keyId: ksInfo.keyId,
                initData: ksInfo.initData,

                getKeyId: function () {
                    return this.keyId;
                },

                getSessionId: function () {
                    return this.session.sessionId;
                },

                getExpirationTime: function () {
                    return NaN;
                },

                getSessionType: function () {
                    return 'temporary';
                },

                // This is our main event handler for all desired MediaKeySession events
                // These events are translated into our API-independent versions of the
                // same events
                handleEvent: function (event) {
                    switch (event.type) {
                        case api.error:
                            let errorStr = 'KeyError'; // TODO: Make better string from event
                            eventBus.trigger(events.KEY_ERROR, { error: new DashJSError(ProtectionErrors.MEDIA_KEYERR_CODE, errorStr, this) });
                            break;
                        case api.message:
                            let message = ArrayBuffer.isView(event.message) ? event.message.buffer : event.message;
                            eventBus.trigger(events.INTERNAL_KEY_MESSAGE, { data: new KeyMessage(this, message, event.destinationURL) });
                            break;
                        case api.ready:
                            logger.debug('DRM: Key added.');
                            eventBus.trigger(events.KEY_ADDED);
                            break;

                        case api.close:
                            logger.debug('DRM: Session closed.  SessionID = ' + this.getSessionId());
                            eventBus.trigger(events.KEY_SESSION_CLOSED, { data: this.getSessionId() });
                            break;
                    }
                }
            };
        }

        instance = {
            getAllInitData,
            getSessions,
            requestKeySystemAccess,
            selectKeySystem,
            setMediaElement,
            createKeySession,
            updateKeySession,
            closeKeySession,
            setServerCertificate,
            loadKeySession,
            removeKeySession,
            stop: reset,
            reset
        };

        setup();

        return instance;
    }

    ProtectionModel_3Feb2014.__dashjs_factory_name = 'ProtectionModel_3Feb2014';
    export default dashjs.FactoryMaker.getClassFactory(ProtectionModel_3Feb2014); /* jshint ignore:line */

#### Search results