123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- import { optionalSelf } from './global';
- import { changeEndianness } from './keysystem-util';
- import { base64Decode } from './numeric-encoding-utils';
- import type { DRMSystemOptions, EMEControllerConfig } from '../config';
- /**
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess
- */
- export const enum KeySystems {
- CLEARKEY = 'org.w3.clearkey',
- FAIRPLAY = 'com.apple.fps',
- PLAYREADY = 'com.microsoft.playready',
- WIDEVINE = 'com.widevine.alpha',
- }
- // Playlist #EXT-X-KEY KEYFORMAT values
- export const enum KeySystemFormats {
- CLEARKEY = 'org.w3.clearkey',
- FAIRPLAY = 'com.apple.streamingkeydelivery',
- PLAYREADY = 'com.microsoft.playready',
- WIDEVINE = 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed',
- }
- export function keySystemFormatToKeySystemDomain(
- format: KeySystemFormats,
- ): KeySystems | undefined {
- switch (format) {
- case KeySystemFormats.FAIRPLAY:
- return KeySystems.FAIRPLAY;
- case KeySystemFormats.PLAYREADY:
- return KeySystems.PLAYREADY;
- case KeySystemFormats.WIDEVINE:
- return KeySystems.WIDEVINE;
- case KeySystemFormats.CLEARKEY:
- return KeySystems.CLEARKEY;
- }
- }
- // System IDs for which we can extract a key ID from "encrypted" event PSSH
- export const enum KeySystemIds {
- CENC = '1077efecc0b24d02ace33c1e52e2fb4b',
- CLEARKEY = 'e2719d58a985b3c9781ab030af78d30e',
- FAIRPLAY = '94ce86fb07ff4f43adb893d2fa968ca2',
- PLAYREADY = '9a04f07998404286ab92e65be0885f95',
- WIDEVINE = 'edef8ba979d64acea3c827dcd51d21ed',
- }
- export function keySystemIdToKeySystemDomain(
- systemId: KeySystemIds,
- ): KeySystems | undefined {
- if (systemId === KeySystemIds.WIDEVINE) {
- return KeySystems.WIDEVINE;
- } else if (systemId === KeySystemIds.PLAYREADY) {
- return KeySystems.PLAYREADY;
- } else if (
- systemId === KeySystemIds.CENC ||
- systemId === KeySystemIds.CLEARKEY
- ) {
- return KeySystems.CLEARKEY;
- }
- }
- export function keySystemDomainToKeySystemFormat(
- keySystem: KeySystems,
- ): KeySystemFormats | undefined {
- switch (keySystem) {
- case KeySystems.FAIRPLAY:
- return KeySystemFormats.FAIRPLAY;
- case KeySystems.PLAYREADY:
- return KeySystemFormats.PLAYREADY;
- case KeySystems.WIDEVINE:
- return KeySystemFormats.WIDEVINE;
- case KeySystems.CLEARKEY:
- return KeySystemFormats.CLEARKEY;
- }
- }
- export function getKeySystemsForConfig(
- config: EMEControllerConfig,
- ): KeySystems[] {
- const { drmSystems, widevineLicenseUrl } = config;
- const keySystemsToAttempt: KeySystems[] = drmSystems
- ? [
- KeySystems.FAIRPLAY,
- KeySystems.WIDEVINE,
- KeySystems.PLAYREADY,
- KeySystems.CLEARKEY,
- ].filter((keySystem) => !!drmSystems[keySystem])
- : [];
- if (!keySystemsToAttempt[KeySystems.WIDEVINE] && widevineLicenseUrl) {
- keySystemsToAttempt.push(KeySystems.WIDEVINE);
- }
- return keySystemsToAttempt;
- }
- export type MediaKeyFunc = (
- keySystem: KeySystems,
- supportedConfigurations: MediaKeySystemConfiguration[],
- ) => Promise<MediaKeySystemAccess>;
- export const requestMediaKeySystemAccess = (function (): MediaKeyFunc | null {
- if (optionalSelf?.navigator?.requestMediaKeySystemAccess) {
- return self.navigator.requestMediaKeySystemAccess.bind(self.navigator);
- } else {
- return null;
- }
- })();
- /**
- * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration
- */
- export function getSupportedMediaKeySystemConfigurations(
- keySystem: KeySystems,
- audioCodecs: string[],
- videoCodecs: string[],
- drmSystemOptions: DRMSystemOptions,
- ): MediaKeySystemConfiguration[] {
- let initDataTypes: string[];
- switch (keySystem) {
- case KeySystems.FAIRPLAY:
- initDataTypes = ['cenc', 'sinf'];
- break;
- case KeySystems.WIDEVINE:
- case KeySystems.PLAYREADY:
- initDataTypes = ['cenc'];
- break;
- case KeySystems.CLEARKEY:
- initDataTypes = ['cenc', 'keyids'];
- break;
- default:
- throw new Error(`Unknown key-system: ${keySystem}`);
- }
- return createMediaKeySystemConfigurations(
- initDataTypes,
- audioCodecs,
- videoCodecs,
- drmSystemOptions,
- );
- }
- function createMediaKeySystemConfigurations(
- initDataTypes: string[],
- audioCodecs: string[],
- videoCodecs: string[],
- drmSystemOptions: DRMSystemOptions,
- ): MediaKeySystemConfiguration[] {
- const baseConfig: MediaKeySystemConfiguration = {
- initDataTypes: initDataTypes,
- persistentState: drmSystemOptions.persistentState || 'optional',
- distinctiveIdentifier: drmSystemOptions.distinctiveIdentifier || 'optional',
- sessionTypes: drmSystemOptions.sessionTypes || [
- drmSystemOptions.sessionType || 'temporary',
- ],
- audioCapabilities: audioCodecs.map((codec) => ({
- contentType: `audio/mp4; codecs=${codec}`,
- robustness: drmSystemOptions.audioRobustness || '',
- encryptionScheme: drmSystemOptions.audioEncryptionScheme || null,
- })),
- videoCapabilities: videoCodecs.map((codec) => ({
- contentType: `video/mp4; codecs=${codec}`,
- robustness: drmSystemOptions.videoRobustness || '',
- encryptionScheme: drmSystemOptions.videoEncryptionScheme || null,
- })),
- };
- return [baseConfig];
- }
- export function isPersistentSessionType(
- drmSystemOptions: DRMSystemOptions,
- ): boolean {
- return (
- drmSystemOptions.sessionType === 'persistent-license' ||
- !!drmSystemOptions.sessionTypes?.some(
- (type) => type === 'persistent-license',
- )
- );
- }
- export function parsePlayReadyWRM(keyBytes: Uint8Array<ArrayBuffer>) {
- const keyBytesUtf16 = new Uint16Array(
- keyBytes.buffer,
- keyBytes.byteOffset,
- keyBytes.byteLength / 2,
- );
- const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16));
- // Parse Playready WRMHeader XML
- const xmlKeyBytes = keyByteStr.substring(
- keyByteStr.indexOf('<'),
- keyByteStr.length,
- );
- const parser = new DOMParser();
- const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml');
- const keyData = xmlDoc.getElementsByTagName('KID')[0];
- if (keyData) {
- const keyId = keyData.childNodes[0]
- ? keyData.childNodes[0].nodeValue
- : keyData.getAttribute('VALUE');
- if (keyId) {
- const keyIdArray = base64Decode(keyId).subarray(0, 16);
- // KID value in PRO is a base64-encoded little endian GUID interpretation of UUID
- // KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID
- changeEndianness(keyIdArray);
- return keyIdArray;
- }
- }
- return null;
- }
|