UNPKG

rx-player

Version:
162 lines (152 loc) 5.86 kB
/** * Copyright 2015 CANAL+ Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import log from "../../log"; import { getPsshSystemID } from "../../parsers/containers/isobmff"; import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal"; import { be4toi } from "../../utils/byte_parsing"; import isNullOrUndefined from "../../utils/is_null_or_undefined"; import type { IMediaEncryptedEvent } from "../browser_compatibility_types"; import { PSSH_TO_INTEGER } from "./constants"; /** Data recuperated from parsing the payload of an `encrypted` event. */ export interface IEncryptedEventData { /** * Initialization data type. * String describing the format of the initialization data sent through this * event. * https://www.w3.org/TR/eme-initdata-registry/ * * `undefined` if not known. */ type: string | undefined; /** Every initialization data for that type. */ values: Array<{ /** * Hex encoded system id, which identifies the key system. * https://dashif.org/identifiers/content_protection/ * * If `undefined`, we don't know the system id for that initialization data. * In that case, the initialization data might even be a concatenation of * the initialization data from multiple system ids. */ systemId: string | undefined; /** * The initialization data itself for that type and systemId. * For example, with ISOBMFF "cenc" initialization data, this will be the * whole PSSH box. */ data: Uint8Array; }>; forceSessionRecreation?: boolean | undefined; } /** * Take in input initialization data from an encrypted event and generate the * corresponding array of initialization data values from it. * * At the moment, this function only handles initialization data which have the * "cenc" initialization data type. * It will just return a single value with an `undefined` `systemId` for all * other types of data. * @param {Uint8Array} initData - Raw initialization data * @returns {Array.<Object>} */ function getInitializationDataValues( initData: Uint8Array, ): Array<{ systemId: string | undefined; data: Uint8Array }> { const result: Array<{ systemId: string | undefined; data: Uint8Array }> = []; let offset = 0; while (offset < initData.length) { if ( initData.length < offset + 8 || be4toi(initData, offset + 4) !== PSSH_TO_INTEGER ) { log.warn("DRM", "Unrecognized initialization data. Use as is."); return [{ systemId: undefined, data: initData }]; } const len = be4toi(new Uint8Array(initData), offset); if (offset + len > initData.length) { log.warn("DRM", "Unrecognized initialization data. Use as is."); return [{ systemId: undefined, data: initData }]; } const currentPSSH = initData.subarray(offset, offset + len); const systemId = getPsshSystemID(currentPSSH, 8); const currentItem = { systemId, data: currentPSSH }; if (isPSSHAlreadyEncountered(result, currentItem)) { // As we observed on some browsers (IE and Edge), the initialization data on // some segments have sometimes duplicated PSSH when sent through an encrypted // event (but not when the corresponding segment has been pushed to the // SourceBuffer). // We prefer filtering them out, to avoid further issues. log.warn("DRM", "Duplicated PSSH found in initialization data, removing it."); } else { result.push(currentItem); } offset += len; } if (offset !== initData.length) { log.warn("DRM", "Unrecognized initialization data. Use as is."); return [{ systemId: undefined, data: initData }]; } return result; } /** * Returns `true` if the given PSSH has already been stored in the * `encounteredPSSHs` cache given. * Returns `false` otherwise. * @param {Array.<Object>} encounteredPSSHs * @param {Uint8Array} pssh * @returns {boolean} */ function isPSSHAlreadyEncountered( encounteredPSSHs: Array<{ systemId: string | undefined; data: Uint8Array }>, pssh: { systemId: string | undefined; data: Uint8Array }, ): boolean { for (let i = 0; i < encounteredPSSHs.length; i++) { const item = encounteredPSSHs[i]; if ( pssh.systemId === undefined || item.systemId === undefined || pssh.systemId === item.systemId ) { if (areArraysOfNumbersEqual(pssh.data, item.data)) { return true; } } } return false; } /** * Take out the two things we need on an encryptedEvent: * - the initialization Data * - the initialization Data type * * @param {MediaEncryptedEvent} encryptedEvent - Payload received with an * "encrypted" event. * @returns {Object} - Initialization data and Initialization data type. * @throws {EncryptedMediaError} - Throws if no initialization data is * encountered in the given event. */ export default function getInitData( encryptedEvent: IMediaEncryptedEvent, ): IEncryptedEventData | null { const { initData, initDataType, forceSessionRecreation } = encryptedEvent; if (isNullOrUndefined(initData)) { log.warn("DRM", "No init data found on media encrypted event."); return null; } const initDataBytes = new Uint8Array(initData); const values = getInitializationDataValues(initDataBytes); return { type: initDataType, values, forceSessionRecreation }; }