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

    const LICENSE_SERVER_MANIFEST_CONFIGURATIONS = {
        attributes: ['Laurl', 'laurl'],
        prefixes: ['clearkey', 'dashif']
    };

    /**
     * @class
     * @ignore
     */
    class CommonEncryption {
        /**
         * Find and return the ContentProtection element in the given array
         * that indicates support for MPEG Common Encryption
         *
         * @param {Array} cpArray array of content protection elements
         * @returns {Object|null} the Common Encryption content protection element or
         * null if one was not found
         */
        static findCencContentProtection(cpArray) {
            let retVal = null;
            for (let i = 0; i < cpArray.length; ++i) {
                let cp = cpArray[i];
                if (cp.schemeIdUri.toLowerCase() === 'urn:mpeg:dash:mp4protection:2011' &&
                    (cp.value.toLowerCase() === 'cenc' || cp.value.toLowerCase() === 'cbcs'))
                    retVal = cp;
            }
            return retVal;
        }

        /**
         * Returns just the data portion of a single PSSH
         *
         * @param {ArrayBuffer} pssh - the PSSH
         * @return {ArrayBuffer} data portion of the PSSH
         */
        static getPSSHData(pssh) {
            let offset = 8; // Box size and type fields
            let view = new DataView(pssh);

            // Read version
            let version = view.getUint8(offset);

            offset += 20; // Version (1), flags (3), system ID (16)

            if (version > 0) {
                offset += 4 + (16 * view.getUint32(offset)); // Key ID count (4) and All key IDs (16*count)
            }

            offset += 4; // Data size
            return pssh.slice(offset);
        }

        /**
         * Returns the PSSH associated with the given key system from the concatenated
         * list of PSSH boxes in the given initData
         *
         * @param {KeySystem} keySystem the desired
         * key system
         * @param {ArrayBuffer} initData 'cenc' initialization data.  Concatenated list of PSSH.
         * @returns {ArrayBuffer|null} The PSSH box data corresponding to the given key system, null if not found
         * or null if a valid association could not be found.
         */
        static getPSSHForKeySystem(keySystem, initData) {
            let psshList = CommonEncryption.parsePSSHList(initData);
            if (keySystem && psshList.hasOwnProperty(keySystem.uuid.toLowerCase())) {
                return psshList[keySystem.uuid.toLowerCase()];
            }
            return null;
        }

        /**
         * Parse a standard common encryption PSSH which contains a simple
         * base64-encoding of the init data
         *
         * @param {Object} cpData the ContentProtection element
         * @param {BASE64} BASE64 reference
         * @returns {ArrayBuffer|null} the init data or null if not found
         */
        static parseInitDataFromContentProtection(cpData, BASE64) {
            if ('pssh' in cpData) {

                // Remove whitespaces and newlines from pssh text
                cpData.pssh.__text = cpData.pssh.__text.replace(/\r?\n|\r/g, '').replace(/\s+/g, '');

                return BASE64.decodeArray(cpData.pssh.__text).buffer;
            }
            return null;
        }

        /**
         * Parses list of PSSH boxes into keysystem-specific PSSH data
         *
         * @param {ArrayBuffer} data - the concatenated list of PSSH boxes as provided by
         * CDM as initialization data when CommonEncryption content is detected
         * @returns {Object|Array} an object that has a property named according to each of
         * the detected key system UUIDs (e.g. 00000000-0000-0000-0000-0000000000)
         * and a ArrayBuffer (the entire PSSH box) as the property value
         */
        static parsePSSHList(data) {

            if (data === null || data === undefined)
                return [];

            let dv = new DataView(data.buffer || data); // data.buffer first for Uint8Array support
            let done = false;
            let pssh = {};

            // TODO: Need to check every data read for end of buffer
            let byteCursor = 0;
            while (!done) {

                let size,
                    nextBox,
                    version,
                    systemID;
                let boxStart = byteCursor;

                if (byteCursor >= dv.buffer.byteLength)
                    break;

                /* Box size */
                size = dv.getUint32(byteCursor);
                nextBox = byteCursor + size;
                byteCursor += 4;

                /* Verify PSSH */
                if (dv.getUint32(byteCursor) !== 0x70737368) {
                    byteCursor = nextBox;
                    continue;
                }
                byteCursor += 4;

                /* Version must be 0 or 1 */
                version = dv.getUint8(byteCursor);
                if (version !== 0 && version !== 1) {
                    byteCursor = nextBox;
                    continue;
                }
                byteCursor++;

                byteCursor += 3; /* skip flags */

                // 16-byte UUID/SystemID
                systemID = '';
                let i, val;
                for (i = 0; i < 4; i++) {
                    val = dv.getUint8(byteCursor + i).toString(16);
                    systemID += (val.length === 1) ? '0' + val : val;
                }
                byteCursor += 4;
                systemID += '-';
                for (i = 0; i < 2; i++) {
                    val = dv.getUint8(byteCursor + i).toString(16);
                    systemID += (val.length === 1) ? '0' + val : val;
                }
                byteCursor += 2;
                systemID += '-';
                for (i = 0; i < 2; i++) {
                    val = dv.getUint8(byteCursor + i).toString(16);
                    systemID += (val.length === 1) ? '0' + val : val;
                }
                byteCursor += 2;
                systemID += '-';
                for (i = 0; i < 2; i++) {
                    val = dv.getUint8(byteCursor + i).toString(16);
                    systemID += (val.length === 1) ? '0' + val : val;
                }
                byteCursor += 2;
                systemID += '-';
                for (i = 0; i < 6; i++) {
                    val = dv.getUint8(byteCursor + i).toString(16);
                    systemID += (val.length === 1) ? '0' + val : val;
                }
                byteCursor += 6;

                systemID = systemID.toLowerCase();

                /* PSSH Data Size */
                byteCursor += 4;

                /* PSSH Data */
                pssh[systemID] = dv.buffer.slice(boxStart, nextBox);
                byteCursor = nextBox;
            }

            return pssh;
        }

        static getLicenseServerUrlFromMediaInfo(mediaInfo, schemeIdUri) {
            try {

                if (!mediaInfo || mediaInfo.length === 0) {
                    return null;
                }

                let i = 0;
                let licenseServer = null;

                while (i < mediaInfo.length && !licenseServer) {
                    const info = mediaInfo[i];

                    if (info && info.contentProtection && info.contentProtection.length > 0) {
                        const targetProtectionData = info.contentProtection.filter((cp) => {
                            return cp.schemeIdUri && cp.schemeIdUri === schemeIdUri;
                        });

                        if (targetProtectionData && targetProtectionData.length > 0) {
                            let j = 0;
                            while (j < targetProtectionData.length && !licenseServer) {
                                const ckData = targetProtectionData[j];
                                let k = 0;
                                while (k < LICENSE_SERVER_MANIFEST_CONFIGURATIONS.attributes.length && !licenseServer) {
                                    let l = 0;
                                    const attribute = LICENSE_SERVER_MANIFEST_CONFIGURATIONS.attributes[k];
                                    while (l < LICENSE_SERVER_MANIFEST_CONFIGURATIONS.prefixes.length && !licenseServer) {
                                        const prefix = LICENSE_SERVER_MANIFEST_CONFIGURATIONS.prefixes[l];
                                        if (ckData[attribute] && ckData[attribute].__prefix && ckData[attribute].__prefix === prefix && ckData[attribute].__text) {
                                            licenseServer = ckData[attribute].__text;
                                        }
                                        l += 1;
                                    }
                                    k += 1;
                                }
                                j += 1;
                            }
                        }
                    }
                    i += 1;
                }
                return licenseServer;
            } catch (e) {
                return null;
            }
        }
    }

    export default CommonEncryption;

#### Search results