���� JFIF �� � ( %"1"%)+...383,7(-.-
![]() Server : Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.20 System : Linux st2.domain.com 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 User : apache ( 48) PHP Version : 7.4.20 Disable Function : NONE Directory : /var/www/html/netphim/layout/default/node_modules/hls.js/src/loader/ |
import { ErrorTypes, ErrorDetails } from '../errors'; import { LoaderStats, LoaderResponse, LoaderConfiguration, LoaderCallbacks, Loader, KeyLoaderContext, PlaylistLevelType, } from '../types/loader'; import { LoadError } from './fragment-loader'; import type { HlsConfig } from '../config'; import type { Fragment } from '../loader/fragment'; import type { ComponentAPI } from '../types/component-api'; import type { KeyLoadedData } from '../types/events'; import type { LevelKey } from './level-key'; import type EMEController from '../controller/eme-controller'; import type { MediaKeySessionContext } from '../controller/eme-controller'; import type { KeySystemFormats } from '../utils/mediakeys-helper'; export interface KeyLoaderInfo { decryptdata: LevelKey; keyLoadPromise: Promise<KeyLoadedData> | null; loader: Loader<KeyLoaderContext> | null; mediaKeySessionContext: MediaKeySessionContext | null; } export default class KeyLoader implements ComponentAPI { private readonly config: HlsConfig; public keyUriToKeyInfo: { [keyuri: string]: KeyLoaderInfo } = {}; public emeController: EMEController | null = null; constructor(config: HlsConfig) { this.config = config; } abort(type?: PlaylistLevelType) { for (const uri in this.keyUriToKeyInfo) { const loader = this.keyUriToKeyInfo[uri].loader; if (loader) { if (type && type !== loader.context.frag.type) { return; } loader.abort(); } } } detach() { for (const uri in this.keyUriToKeyInfo) { const keyInfo = this.keyUriToKeyInfo[uri]; // Remove cached EME keys on detach if ( keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption ) { delete this.keyUriToKeyInfo[uri]; } } } destroy() { this.detach(); for (const uri in this.keyUriToKeyInfo) { const loader = this.keyUriToKeyInfo[uri].loader; if (loader) { loader.destroy(); } } this.keyUriToKeyInfo = {}; } createKeyLoadError( frag: Fragment, details: ErrorDetails = ErrorDetails.KEY_LOAD_ERROR, error: Error, networkDetails?: any, response?: { url: string; data: undefined; code: number; text: string } ): LoadError { return new LoadError({ type: ErrorTypes.NETWORK_ERROR, details, fatal: false, frag, response, error, networkDetails, }); } loadClear( loadingFrag: Fragment, encryptedFragments: Fragment[] ): void | Promise<void> { if (this.emeController && this.config.emeEnabled) { // access key-system with nearest key on start (loaidng frag is unencrypted) const { sn, cc } = loadingFrag; for (let i = 0; i < encryptedFragments.length; i++) { const frag = encryptedFragments[i]; if ( cc <= frag.cc && (sn === 'initSegment' || frag.sn === 'initSegment' || sn < frag.sn) ) { this.emeController .selectKeySystemFormat(frag) .then((keySystemFormat) => { frag.setKeyFormat(keySystemFormat); }); break; } } } } load(frag: Fragment): Promise<KeyLoadedData> { if (!frag.decryptdata && frag.encrypted && this.emeController) { // Multiple keys, but none selected, resolve in eme-controller return this.emeController .selectKeySystemFormat(frag) .then((keySystemFormat) => { return this.loadInternal(frag, keySystemFormat); }); } return this.loadInternal(frag); } loadInternal( frag: Fragment, keySystemFormat?: KeySystemFormats ): Promise<KeyLoadedData> { if (keySystemFormat) { frag.setKeyFormat(keySystemFormat); } const decryptdata = frag.decryptdata; if (!decryptdata) { const error = new Error( keySystemFormat ? `Expected frag.decryptdata to be defined after setting format ${keySystemFormat}` : 'Missing decryption data on fragment in onKeyLoading' ); return Promise.reject( this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error) ); } const uri = decryptdata.uri; if (!uri) { return Promise.reject( this.createKeyLoadError( frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Invalid key URI: "${uri}"`) ) ); } let keyInfo = this.keyUriToKeyInfo[uri]; if (keyInfo?.decryptdata.key) { decryptdata.key = keyInfo.decryptdata.key; return Promise.resolve({ frag, keyInfo }); } // Return key load promise as long as it does not have a mediakey session with an unusable key status if (keyInfo?.keyLoadPromise) { switch (keyInfo.mediaKeySessionContext?.keyStatus) { case undefined: case 'status-pending': case 'usable': case 'usable-in-future': return keyInfo.keyLoadPromise.then((keyLoadedData) => { // Return the correct fragment with updated decryptdata key and loaded keyInfo decryptdata.key = keyLoadedData.keyInfo.decryptdata.key; return { frag, keyInfo }; }); } // If we have a key session and status and it is not pending or usable, continue // This will go back to the eme-controller for expired keys to get a new keyLoadPromise } // Load the key or return the loading promise keyInfo = this.keyUriToKeyInfo[uri] = { decryptdata, keyLoadPromise: null, loader: null, mediaKeySessionContext: null, }; switch (decryptdata.method) { case 'ISO-23001-7': case 'SAMPLE-AES': case 'SAMPLE-AES-CENC': case 'SAMPLE-AES-CTR': if (decryptdata.keyFormat === 'identity') { // loadKeyHTTP handles http(s) and data URLs return this.loadKeyHTTP(keyInfo, frag); } return this.loadKeyEME(keyInfo, frag); case 'AES-128': return this.loadKeyHTTP(keyInfo, frag); default: return Promise.reject( this.createKeyLoadError( frag, ErrorDetails.KEY_LOAD_ERROR, new Error( `Key supplied with unsupported METHOD: "${decryptdata.method}"` ) ) ); } } loadKeyEME(keyInfo: KeyLoaderInfo, frag: Fragment): Promise<KeyLoadedData> { const keyLoadedData: KeyLoadedData = { frag, keyInfo }; if (this.emeController && this.config.emeEnabled) { const keySessionContextPromise = this.emeController.loadKey(keyLoadedData); if (keySessionContextPromise) { return (keyInfo.keyLoadPromise = keySessionContextPromise.then( (keySessionContext) => { keyInfo.mediaKeySessionContext = keySessionContext; return keyLoadedData; } )).catch((error) => { // Remove promise for license renewal or retry keyInfo.keyLoadPromise = null; throw error; }); } } return Promise.resolve(keyLoadedData); } loadKeyHTTP(keyInfo: KeyLoaderInfo, frag: Fragment): Promise<KeyLoadedData> { const config = this.config; const Loader = config.loader; const keyLoader = new Loader(config) as Loader<KeyLoaderContext>; frag.keyLoader = keyInfo.loader = keyLoader; return (keyInfo.keyLoadPromise = new Promise((resolve, reject) => { const loaderContext: KeyLoaderContext = { keyInfo, frag, responseType: 'arraybuffer', url: keyInfo.decryptdata.uri, }; // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times, // key-loader will trigger an error and rely on stream-controller to handle retry logic. // this will also align retry logic with fragment-loader const loadPolicy = config.keyLoadPolicy.default; const loaderConfig: LoaderConfiguration = { loadPolicy, timeout: loadPolicy.maxLoadTimeMs, maxRetry: 0, retryDelay: 0, maxRetryDelay: 0, }; const loaderCallbacks: LoaderCallbacks<KeyLoaderContext> = { onSuccess: ( response: LoaderResponse, stats: LoaderStats, context: KeyLoaderContext, networkDetails: any ) => { const { frag, keyInfo, url: uri } = context; if (!frag.decryptdata || keyInfo !== this.keyUriToKeyInfo[uri]) { return reject( this.createKeyLoadError( frag, ErrorDetails.KEY_LOAD_ERROR, new Error('after key load, decryptdata unset or changed'), networkDetails ) ); } keyInfo.decryptdata.key = frag.decryptdata.key = new Uint8Array( response.data as ArrayBuffer ); // detach fragment key loader on load success frag.keyLoader = null; keyInfo.loader = null; resolve({ frag, keyInfo }); }, onError: ( response: { code: number; text: string }, context: KeyLoaderContext, networkDetails: any, stats: LoaderStats ) => { this.resetLoader(context); reject( this.createKeyLoadError( frag, ErrorDetails.KEY_LOAD_ERROR, new Error( `HTTP Error ${response.code} loading key ${response.text}` ), networkDetails, { url: loaderContext.url, data: undefined, ...response } ) ); }, onTimeout: ( stats: LoaderStats, context: KeyLoaderContext, networkDetails: any ) => { this.resetLoader(context); reject( this.createKeyLoadError( frag, ErrorDetails.KEY_LOAD_TIMEOUT, new Error('key loading timed out'), networkDetails ) ); }, onAbort: ( stats: LoaderStats, context: KeyLoaderContext, networkDetails: any ) => { this.resetLoader(context); reject( this.createKeyLoadError( frag, ErrorDetails.INTERNAL_ABORTED, new Error('key loading aborted'), networkDetails ) ); }, }; keyLoader.load(loaderContext, loaderConfig, loaderCallbacks); })); } private resetLoader(context: KeyLoaderContext) { const { frag, keyInfo, url: uri } = context; const loader = keyInfo.loader; if (frag.keyLoader === loader) { frag.keyLoader = null; keyInfo.loader = null; } delete this.keyUriToKeyInfo[uri]; if (loader) { loader.destroy(); } } }