src/utils/mediakeys-helper.ts
import type { DRMSystemOptions, EMEControllerConfig } from '../config';
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess
*/
export 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 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 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 (
typeof self !== 'undefined' &&
self.navigator &&
self.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 || 'not-allowed',
distinctiveIdentifier:
drmSystemOptions.distinctiveIdentifier || 'not-allowed',
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];
}