Skip to content

Commit 4ebd6d2

Browse files
authored
Handle EME key status errors such as "internal-error" and "output-restricted" before appending media (video-dev#7414)
* Handle EME key status errors before appending segments Fixes video-dev#7413 (playback/switching fails on KEY_SYSTEM_STATUS_INTERNAL_ERROR 'internal-error' key status) * Fix handling of one-to-many KEY URI to Key IDs video-dev#7413 * Remove levels with "internal-error" key status errors and optimize key-bytes comparison Add "FIXME" comments for future MediaKeySessionContext multi-key handling improvements * Do not throw fatal keyLoadingPromise error when context changes on KEY_LOADING reproducible with `hls.once(Hls.Events.KEY_LOADING, () => hls.removeLevel(hls.loadLevel))`
1 parent f9a6dbf commit 4ebd6d2

18 files changed

+454
-278
lines changed

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,8 +1189,8 @@ export type EMEControllerConfig = {
11891189
licenseResponseCallback?: (this: Hls, xhr: XMLHttpRequest, url: string, keyContext: MediaKeySessionContext) => ArrayBuffer;
11901190
emeEnabled: boolean;
11911191
widevineLicenseUrl?: string;
1192-
drmSystems: DRMSystemsConfiguration;
1193-
drmSystemOptions: DRMSystemOptions;
1192+
drmSystems: DRMSystemsConfiguration | undefined;
1193+
drmSystemOptions: DRMSystemOptions | undefined;
11941194
requestMediaKeySystemAccessFunc: MediaKeyFunc | null;
11951195
requireKeySystemAccessOnStart: boolean;
11961196
};
@@ -1204,9 +1204,11 @@ export const enum ErrorActionFlags {
12041204
// (undocumented)
12051205
MoveAllAlternatesMatchingHost = 1,
12061206
// (undocumented)
1207+
MoveAllAlternatesMatchingKey = 4,
1208+
// (undocumented)
12071209
None = 0,
12081210
// (undocumented)
1209-
SwitchToSDR = 4
1211+
SwitchToSDR = 8
12101212
}
12111213

12121214
// Warning: (ae-missing-release-tag) "ErrorController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -1239,6 +1241,8 @@ export interface ErrorData {
12391241
// (undocumented)
12401242
context?: PlaylistLoaderContext;
12411243
// (undocumented)
1244+
decryptdata?: LevelKey;
1245+
// (undocumented)
12421246
details: ErrorDetails;
12431247
// @deprecated (undocumented)
12441248
err?: {
@@ -2981,8 +2985,8 @@ export interface KeyLoadedData {
29812985
// Warning: (ae-missing-release-tag) "KeyLoader" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
29822986
//
29832987
// @public (undocumented)
2984-
export class KeyLoader implements ComponentAPI {
2985-
constructor(config: HlsConfig);
2988+
export class KeyLoader extends Logger implements ComponentAPI {
2989+
constructor(config: HlsConfig, logger: ILogger);
29862990
// (undocumented)
29872991
abort(type?: PlaylistLevelType): void;
29882992
// (undocumented)
@@ -2999,10 +3003,6 @@ export class KeyLoader implements ComponentAPI {
29993003
// (undocumented)
30003004
emeController: EMEController | null;
30013005
// (undocumented)
3002-
keyUriToKeyInfo: {
3003-
[keyuri: string]: KeyLoaderInfo;
3004-
};
3005-
// (undocumented)
30063006
load(frag: Fragment): Promise<KeyLoadedData>;
30073007
// (undocumented)
30083008
loadClear(loadingFrag: Fragment, encryptedFragments: Fragment[], startFragRequested: boolean): null | Promise<void>;
@@ -3278,6 +3278,8 @@ export class LevelDetails {
32783278
// (undocumented)
32793279
get fragmentStart(): number;
32803280
// (undocumented)
3281+
hasKey(levelKey: LevelKey): boolean;
3282+
// (undocumented)
32813283
get hasProgramDateTime(): boolean;
32823284
// (undocumented)
32833285
hasVariableRefs: boolean;

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ export type EMEControllerConfig = {
119119
) => ArrayBuffer;
120120
emeEnabled: boolean;
121121
widevineLicenseUrl?: string;
122-
drmSystems: DRMSystemsConfiguration;
123-
drmSystemOptions: DRMSystemOptions;
122+
drmSystems: DRMSystemsConfiguration | undefined;
123+
drmSystemOptions: DRMSystemOptions | undefined;
124124
requestMediaKeySystemAccessFunc: MediaKeyFunc | null;
125125
requireKeySystemAccessOnStart: boolean;
126126
};

src/controller/base-stream-controller.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -841,10 +841,9 @@ export default class BaseStreamController
841841
}
842842
});
843843
this.hls.trigger(Events.KEY_LOADING, { frag });
844-
if (this.fragCurrent === null) {
845-
keyLoadingPromise = Promise.reject(
846-
new Error(`frag load aborted, context changed in KEY_LOADING`),
847-
);
844+
if ((this.fragCurrent as Fragment | null) === null) {
845+
this.log(`context changed in KEY_LOADING`);
846+
return Promise.resolve(null);
848847
}
849848
} else if (!frag.encrypted) {
850849
keyLoadingPromise = this.keyLoader.loadClear(
@@ -1040,11 +1039,16 @@ export default class BaseStreamController
10401039
);
10411040
}
10421041

1043-
private handleFragLoadError(error: LoadError | Error) {
1042+
private handleFragLoadError(
1043+
error: LoadError | Error | (Error & { data: ErrorData }),
1044+
) {
10441045
if ('data' in error) {
10451046
const data = error.data;
1046-
if ((data as any) && data.details === ErrorDetails.INTERNAL_ABORTED) {
1047+
if (data.frag && data.details === ErrorDetails.INTERNAL_ABORTED) {
10471048
this.handleFragLoadAborted(data.frag, data.part);
1049+
} else if (data.frag && data.type === ErrorTypes.KEY_SYSTEM_ERROR) {
1050+
data.frag.abortRequests();
1051+
this.resetFragmentLoading(data.frag);
10481052
} else {
10491053
this.hls.trigger(Events.ERROR, data as ErrorData);
10501054
}
@@ -1826,7 +1830,7 @@ export default class BaseStreamController
18261830
return pos;
18271831
}
18281832

1829-
private handleFragLoadAborted(frag: Fragment, part: Part | undefined) {
1833+
private handleFragLoadAborted(frag: Fragment, part: Part | null | undefined) {
18301834
if (
18311835
this.transmuxer &&
18321836
frag.type === this.playlistType &&
@@ -2033,8 +2037,8 @@ export default class BaseStreamController
20332037
}
20342038

20352039
protected resetWhenMissingContext(chunkMeta: ChunkMetadata | Fragment) {
2036-
this.warn(
2037-
`The loading context changed while buffering fragment ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level}. This chunk will not be buffered.`,
2040+
this.log(
2041+
`Loading context changed while buffering sn ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level === -1 ? '<removed>' : chunkMeta.level}. This chunk will not be buffered.`,
20382042
);
20392043
this.removeUnbufferedFrags();
20402044
this.resetStartWhenNotLoaded(this.levelLastLoaded);

0 commit comments

Comments
 (0)