Skip to content

Commit 33af213

Browse files
committed
Improve support for DRM key-systems and key handling
Resolves #2833 #2737 #4538
1 parent f6a5da8 commit 33af213

32 files changed

+2668
-1492
lines changed

api-extractor/report/hls.js.api.md

+66-31
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ export class DateRange {
313313
export type DRMSystemOptions = {
314314
audioRobustness?: string;
315315
videoRobustness?: string;
316+
persistentState?: MediaKeysRequirement;
317+
distinctiveIdentifier?: MediaKeysRequirement;
318+
sessionTypes?: string[];
319+
sessionType?: string;
316320
};
317321

318322
// Warning: (ae-missing-release-tag) "ElementaryStreamInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -352,9 +356,10 @@ export enum ElementaryStreamTypes {
352356
//
353357
// @public (undocumented)
354358
export type EMEControllerConfig = {
355-
licenseXhrSetup?: (xhr: XMLHttpRequest, url: string, keySystem: KeySystems) => void | Promise<void>;
356-
licenseResponseCallback?: (xhr: XMLHttpRequest, url: string, keySystem: KeySystems) => ArrayBuffer;
359+
licenseXhrSetup?: (this: Hls, xhr: XMLHttpRequest, url: string, keyContext: MediaKeySessionContext, licenseChallenge: Uint8Array) => void | Promise<Uint8Array | void>;
360+
licenseResponseCallback?: (this: Hls, xhr: XMLHttpRequest, url: string, keyContext: MediaKeySessionContext) => ArrayBuffer;
357361
emeEnabled: boolean;
362+
useEmeEncryptedEvent: boolean;
358363
widevineLicenseUrl?: string;
359364
drmSystems: DRMSystemsConfiguration;
360365
drmSystemOptions: DRMSystemOptions;
@@ -464,6 +469,10 @@ export enum ErrorDetails {
464469
// (undocumented)
465470
KEY_SYSTEM_SESSION_UPDATE_FAILED = "keySystemSessionUpdateFailed",
466471
// (undocumented)
472+
KEY_SYSTEM_STATUS_INTERNAL_ERROR = "keySystemStatusInternalError",
473+
// (undocumented)
474+
KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED = "keySystemStatusOutputRestricted",
475+
// (undocumented)
467476
LEVEL_EMPTY_ERROR = "levelEmptyError",
468477
// (undocumented)
469478
LEVEL_LOAD_ERROR = "levelLoadError",
@@ -740,7 +749,6 @@ export class Fragment extends BaseSegment {
740749
cc: number;
741750
// (undocumented)
742751
clearElementaryStreamInfo(): void;
743-
createInitializationVector(segmentNumber: number): Uint8Array;
744752
// (undocumented)
745753
data?: Uint8Array;
746754
// (undocumented)
@@ -761,12 +769,16 @@ export class Fragment extends BaseSegment {
761769
endPTS?: number;
762770
// (undocumented)
763771
initSegment: Fragment | null;
772+
// Warning: (ae-forgotten-export) The symbol "KeyLoaderContext" needs to be exported by the entry point hls.d.ts
773+
//
764774
// (undocumented)
765775
keyLoader: Loader<KeyLoaderContext> | null;
766776
// (undocumented)
767777
level: number;
768778
// (undocumented)
769-
levelkey?: LevelKey;
779+
levelkeys?: {
780+
[key: string]: LevelKey;
781+
};
770782
// (undocumented)
771783
loader: Loader<FragmentLoaderContext> | null;
772784
// (undocumented)
@@ -777,10 +789,11 @@ export class Fragment extends BaseSegment {
777789
programDateTime: number | null;
778790
// (undocumented)
779791
rawProgramDateTime: string | null;
780-
setDecryptDataFromLevelKey(levelkey: LevelKey, segmentNumber: number): LevelKey;
781792
// (undocumented)
782793
setElementaryStreamInfo(type: ElementaryStreamTypes, startPTS: number, endPTS: number, startDTS: number, endDTS: number, partial?: boolean): void;
783794
// (undocumented)
795+
setKeyFormat(keyFormat: KeySystemFormats): void;
796+
// (undocumented)
784797
sn: number | 'initSegment';
785798
// (undocumented)
786799
start: number;
@@ -894,7 +907,7 @@ class Hls implements HlsEventEmitter {
894907
// (undocumented)
895908
readonly config: HlsConfig;
896909
// (undocumented)
897-
createController(ControllerClass: any, fragmentTracker: any, components: any): any;
910+
createController(ControllerClass: any, components: any): any;
898911
get currentLevel(): number;
899912
// Warning: (ae-setter-with-docs) The doc comment for the property "currentLevel" must appear on the getter, not the setter.
900913
set currentLevel(newLevel: number);
@@ -1222,26 +1235,40 @@ export interface InitPTSFoundData {
12221235
export interface KeyLoadedData {
12231236
// (undocumented)
12241237
frag: Fragment;
1238+
// Warning: (ae-forgotten-export) The symbol "KeyLoaderInfo" needs to be exported by the entry point hls.d.ts
1239+
//
1240+
// (undocumented)
1241+
keyInfo: KeyLoaderInfo;
12251242
}
12261243

1227-
// Warning: (ae-missing-release-tag) "KeyLoaderContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
1244+
// Warning: (ae-missing-release-tag) "KeyLoadingData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
12281245
//
12291246
// @public (undocumented)
1230-
export interface KeyLoaderContext extends FragmentLoaderContext {
1247+
export interface KeyLoadingData {
1248+
// (undocumented)
1249+
frag: Fragment;
12311250
}
12321251

1233-
// Warning: (ae-missing-release-tag) "KeyLoadingData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
1252+
// Warning: (ae-missing-release-tag) "KeySystemFormats" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
12341253
//
12351254
// @public (undocumented)
1236-
export interface KeyLoadingData {
1255+
export enum KeySystemFormats {
12371256
// (undocumented)
1238-
frag: Fragment;
1257+
CLEARKEY = "org.w3.clearkey",
1258+
// (undocumented)
1259+
FAIRPLAY = "com.apple.streamingkeydelivery",
1260+
// (undocumented)
1261+
PLAYREADY = "com.microsoft.playready",
1262+
// (undocumented)
1263+
WIDEVINE = "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
12391264
}
12401265

12411266
// Warning: (ae-missing-release-tag) "KeySystems" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
12421267
//
12431268
// @public (undocumented)
12441269
export enum KeySystems {
1270+
// (undocumented)
1271+
CLEARKEY = "org.w3.clearkey",
12451272
// (undocumented)
12461273
FAIRPLAY = "com.apple.fps",
12471274
// (undocumented)
@@ -1478,24 +1505,31 @@ export class LevelDetails {
14781505
//
14791506
// @public (undocumented)
14801507
export class LevelKey {
1508+
constructor(method: string, uri: string, format: string, formatversions?: number[], iv?: Uint8Array | null);
1509+
// (undocumented)
1510+
static clearKeyUriToKeyIdMap(): void;
1511+
// (undocumented)
1512+
get encrypted(): boolean | "";
14811513
// (undocumented)
1482-
static fromURI(uri: string): LevelKey;
1514+
getDecryptData(sn: number | 'initSegment'): LevelKey | null;
14831515
// (undocumented)
1484-
static fromURL(baseUrl: string, relativeUrl: string): LevelKey;
1516+
get isCommonEncryption(): boolean | "";
14851517
// (undocumented)
14861518
iv: Uint8Array | null;
14871519
// (undocumented)
14881520
key: Uint8Array | null;
14891521
// (undocumented)
1490-
keyFormat: string | null;
1522+
keyFormat: string;
14911523
// (undocumented)
1492-
keyFormatVersions: string | null;
1524+
keyFormatVersions: number[];
14931525
// (undocumented)
1494-
keyID: string | null;
1526+
keyId: Uint8Array | null;
14951527
// (undocumented)
1496-
method: string | null;
1528+
method: string;
14971529
// (undocumented)
1498-
get uri(): string | null;
1530+
pssh: Uint8Array | null;
1531+
// (undocumented)
1532+
get uri(): string;
14991533
}
15001534

15011535
// Warning: (ae-missing-release-tag) "LevelLoadedData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -2237,19 +2271,20 @@ export interface UserdataSample {
22372271

22382272
// Warnings were encountered during analysis:
22392273
//
2240-
// src/config.ts:84:3 - (ae-forgotten-export) The symbol "DRMSystemsConfiguration" needs to be exported by the entry point hls.d.ts
2241-
// src/config.ts:187:3 - (ae-forgotten-export) The symbol "ILogger" needs to be exported by the entry point hls.d.ts
2242-
// src/config.ts:197:3 - (ae-forgotten-export) The symbol "AudioStreamController" needs to be exported by the entry point hls.d.ts
2243-
// src/config.ts:198:3 - (ae-forgotten-export) The symbol "AudioTrackController" needs to be exported by the entry point hls.d.ts
2244-
// src/config.ts:200:3 - (ae-forgotten-export) The symbol "SubtitleStreamController" needs to be exported by the entry point hls.d.ts
2245-
// src/config.ts:201:3 - (ae-forgotten-export) The symbol "SubtitleTrackController" needs to be exported by the entry point hls.d.ts
2246-
// src/config.ts:202:3 - (ae-forgotten-export) The symbol "TimelineController" needs to be exported by the entry point hls.d.ts
2247-
// src/config.ts:204:3 - (ae-forgotten-export) The symbol "EMEController" needs to be exported by the entry point hls.d.ts
2248-
// src/config.ts:207:3 - (ae-forgotten-export) The symbol "CMCDController" needs to be exported by the entry point hls.d.ts
2249-
// src/config.ts:209:3 - (ae-forgotten-export) The symbol "AbrController" needs to be exported by the entry point hls.d.ts
2250-
// src/config.ts:210:3 - (ae-forgotten-export) The symbol "BufferController" needs to be exported by the entry point hls.d.ts
2251-
// src/config.ts:211:3 - (ae-forgotten-export) The symbol "CapLevelController" needs to be exported by the entry point hls.d.ts
2252-
// src/config.ts:212:3 - (ae-forgotten-export) The symbol "FPSController" needs to be exported by the entry point hls.d.ts
2274+
// src/config.ts:79:3 - (ae-forgotten-export) The symbol "MediaKeySessionContext" needs to be exported by the entry point hls.d.ts
2275+
// src/config.ts:95:3 - (ae-forgotten-export) The symbol "DRMSystemsConfiguration" needs to be exported by the entry point hls.d.ts
2276+
// src/config.ts:198:3 - (ae-forgotten-export) The symbol "ILogger" needs to be exported by the entry point hls.d.ts
2277+
// src/config.ts:208:3 - (ae-forgotten-export) The symbol "AudioStreamController" needs to be exported by the entry point hls.d.ts
2278+
// src/config.ts:209:3 - (ae-forgotten-export) The symbol "AudioTrackController" needs to be exported by the entry point hls.d.ts
2279+
// src/config.ts:211:3 - (ae-forgotten-export) The symbol "SubtitleStreamController" needs to be exported by the entry point hls.d.ts
2280+
// src/config.ts:212:3 - (ae-forgotten-export) The symbol "SubtitleTrackController" needs to be exported by the entry point hls.d.ts
2281+
// src/config.ts:213:3 - (ae-forgotten-export) The symbol "TimelineController" needs to be exported by the entry point hls.d.ts
2282+
// src/config.ts:215:3 - (ae-forgotten-export) The symbol "EMEController" needs to be exported by the entry point hls.d.ts
2283+
// src/config.ts:218:3 - (ae-forgotten-export) The symbol "CMCDController" needs to be exported by the entry point hls.d.ts
2284+
// src/config.ts:220:3 - (ae-forgotten-export) The symbol "AbrController" needs to be exported by the entry point hls.d.ts
2285+
// src/config.ts:221:3 - (ae-forgotten-export) The symbol "BufferController" needs to be exported by the entry point hls.d.ts
2286+
// src/config.ts:222:3 - (ae-forgotten-export) The symbol "CapLevelController" needs to be exported by the entry point hls.d.ts
2287+
// src/config.ts:223:3 - (ae-forgotten-export) The symbol "FPSController" needs to be exported by the entry point hls.d.ts
22532288

22542289
// (No @packageDocumentation comment for this package)
22552290

docs/API.md

+8
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
- [`abrMaxWithRealBitrate`](#abrmaxwithrealbitrate)
9595
- [`minAutoBitrate`](#minautobitrate)
9696
- [`emeEnabled`](#emeEnabled)
97+
- [`useEmeEncryptedEvent`](#useEmeEncryptedEvent)
9798
- [`widevineLicenseUrl`](#widevineLicenseUrl)
9899
- [`licenseXhrSetup`](#licenseXhrSetup)
99100
- [`licenseResponseCallback`](#licenseResponseCallback)
@@ -399,6 +400,7 @@ var config = {
399400
maxLoadingDelay: 4,
400401
minAutoBitrate: 0,
401402
emeEnabled: false,
403+
useEmeEncryptedEvent: false,
402404
widevineLicenseUrl: undefined,
403405
licenseXhrSetup: undefined,
404406
drmSystems: {},
@@ -1192,6 +1194,12 @@ Useful when browser or tab of the browser is not in the focus and bandwidth drop
11921194

11931195
Set to `true` to enable DRM key system access and license retrieval.
11941196

1197+
### `useEmeEncryptedEvent`
1198+
1199+
(default: `false`)
1200+
1201+
Set to `true` to use media "encrypted" event initData and ignore manifest DRM keys.
1202+
11951203
### `widevineLicenseUrl`
11961204

11971205
(default: `undefined`)

src/config.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ import BufferController from './controller/buffer-controller';
77
import { TimelineController } from './controller/timeline-controller';
88
import CapLevelController from './controller/cap-level-controller';
99
import FPSController from './controller/fps-controller';
10-
import EMEController from './controller/eme-controller';
10+
import EMEController, {
11+
MediaKeySessionContext,
12+
} from './controller/eme-controller';
1113
import CMCDController from './controller/cmcd-controller';
1214
import XhrLoader from './utils/xhr-loader';
1315
import FetchLoader, { fetchSupported } from './utils/fetch-loader';
1416
import Cues from './utils/cues';
1517
import { requestMediaKeySystemAccess } from './utils/mediakeys-helper';
1618
import { ILogger, logger } from './utils/logger';
1719

20+
import type Hls from './hls';
1821
import type { CuesInterface } from './utils/cues';
1922
import type { MediaKeyFunc, KeySystems } from './utils/mediakeys-helper';
2023
import type {
@@ -57,6 +60,10 @@ export type CMCDControllerConfig = {
5760
export type DRMSystemOptions = {
5861
audioRobustness?: string;
5962
videoRobustness?: string;
63+
persistentState?: MediaKeysRequirement;
64+
distinctiveIdentifier?: MediaKeysRequirement;
65+
sessionTypes?: string[];
66+
sessionType?: string;
6067
};
6168

6269
export type DRMSystemConfiguration = {
@@ -70,16 +77,20 @@ export type DRMSystemsConfiguration = Partial<
7077

7178
export type EMEControllerConfig = {
7279
licenseXhrSetup?: (
80+
this: Hls,
7381
xhr: XMLHttpRequest,
7482
url: string,
75-
keySystem: KeySystems
76-
) => void | Promise<void>;
83+
keyContext: MediaKeySessionContext,
84+
licenseChallenge: Uint8Array
85+
) => void | Promise<Uint8Array | void>;
7786
licenseResponseCallback?: (
87+
this: Hls,
7888
xhr: XMLHttpRequest,
7989
url: string,
80-
keySystem: KeySystems
90+
keyContext: MediaKeySessionContext
8191
) => ArrayBuffer;
8292
emeEnabled: boolean;
93+
useEmeEncryptedEvent: boolean;
8394
widevineLicenseUrl?: string;
8495
drmSystems: DRMSystemsConfiguration;
8596
drmSystemOptions: DRMSystemOptions;
@@ -300,6 +311,7 @@ export const hlsDefaultConfig: HlsConfig = {
300311
maxLoadingDelay: 4, // used by abr-controller
301312
minAutoBitrate: 0, // used by hls
302313
emeEnabled: false, // used by eme-controller
314+
useEmeEncryptedEvent: false, // used by eme-controller
303315
widevineLicenseUrl: undefined, // used by eme-controller
304316
drmSystems: {}, // used by eme-controller
305317
drmSystemOptions: {}, // used by eme-controller

src/controller/audio-stream-controller.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { fragmentWithinToleranceTest } from './fragment-finders';
1212
import { alignMediaPlaylistByPDT } from '../utils/discontinuities';
1313
import { ErrorDetails } from '../errors';
1414
import type { NetworkComponentAPI } from '../types/component-api';
15+
import type Hls from '../hls';
1516
import type { FragmentTracker } from './fragment-tracker';
17+
import type KeyLoader from '../loader/key-loader';
1618
import type { TransmuxerResult } from '../types/transmuxer';
17-
import type Hls from '../hls';
1819
import type { LevelDetails } from '../loader/level-details';
1920
import type { TrackSet } from '../types/track';
2021
import type {
@@ -56,8 +57,12 @@ class AudioStreamController
5657
private bufferFlushed: boolean = false;
5758
private cachedTrackLoadedData: TrackLoadedData | null = null;
5859

59-
constructor(hls: Hls, fragmentTracker: FragmentTracker) {
60-
super(hls, fragmentTracker, '[audio-stream-controller]');
60+
constructor(
61+
hls: Hls,
62+
fragmentTracker: FragmentTracker,
63+
keyLoader: KeyLoader
64+
) {
65+
super(hls, fragmentTracker, keyLoader, '[audio-stream-controller]');
6166
this._registerListeners();
6267
}
6368

src/controller/base-stream-controller.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { FragmentState } from './fragment-tracker';
33
import { Bufferable, BufferHelper } from '../utils/buffer-helper';
44
import { logger } from '../utils/logger';
55
import { Events } from '../events';
6-
import { ErrorDetails } from '../errors';
6+
import { ErrorDetails, ErrorTypes } from '../errors';
77
import { ChunkMetadata } from '../types/transmuxer';
88
import { appendUint8Array } from '../utils/mp4-tools';
99
import { alignStream } from '../utils/discontinuities';
@@ -100,14 +100,19 @@ export default class BaseStreamController
100100
protected log: (msg: any) => void;
101101
protected warn: (msg: any) => void;
102102

103-
constructor(hls: Hls, fragmentTracker: FragmentTracker, logPrefix: string) {
103+
constructor(
104+
hls: Hls,
105+
fragmentTracker: FragmentTracker,
106+
keyLoader: KeyLoader,
107+
logPrefix: string
108+
) {
104109
super();
105110
this.logPrefix = logPrefix;
106111
this.log = logger.log.bind(logger, `${logPrefix}:`);
107112
this.warn = logger.warn.bind(logger, `${logPrefix}:`);
108113
this.hls = hls;
109114
this.fragmentLoader = new FragmentLoader(hls.config);
110-
this.keyLoader = new KeyLoader(hls.config);
115+
this.keyLoader = keyLoader;
111116
this.fragmentTracker = fragmentTracker;
112117
this.config = hls.config;
113118
this.decrypter = new Decrypter(hls as HlsEventEmitter, hls.config);
@@ -720,11 +725,21 @@ export default class BaseStreamController
720725
);
721726
}
722727

723-
private handleFragLoadError({ data }: LoadError) {
724-
if (data && data.details === ErrorDetails.INTERNAL_ABORTED) {
725-
this.handleFragLoadAborted(data.frag, data.part);
728+
private handleFragLoadError(error: LoadError | Error) {
729+
if ('data' in error) {
730+
const data = error.data;
731+
if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) {
732+
this.handleFragLoadAborted(data.frag, data.part);
733+
} else {
734+
this.hls.trigger(Events.ERROR, data as ErrorData);
735+
}
726736
} else {
727-
this.hls.trigger(Events.ERROR, data as ErrorData);
737+
this.hls.trigger(Events.ERROR, {
738+
type: ErrorTypes.OTHER_ERROR,
739+
details: ErrorDetails.INTERNAL_EXCEPTION,
740+
err: error,
741+
fatal: true,
742+
});
728743
}
729744
return null;
730745
}

0 commit comments

Comments
 (0)