Skip to content

Commit 6d30359

Browse files
committed
(experimental for PlayReady issue #7519) Timeout missing key-status with internal-error
1 parent ee0169f commit 6d30359

File tree

2 files changed

+69
-47
lines changed

2 files changed

+69
-47
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4000,6 +4000,10 @@ export interface MediaKeySessionContext {
40004000
[keyId: string]: MediaKeyStatus;
40014001
};
40024002
// (undocumented)
4003+
keyStatusTimeouts?: {
4004+
[keyId: string]: number;
4005+
};
4006+
// (undocumented)
40034007
keySystem: KeySystems;
40044008
// (undocumented)
40054009
levelKeys: LevelKey[];
@@ -4027,6 +4031,9 @@ export type MediaKeySessionContextAndLevelKey = {
40274031
keyStatuses: {
40284032
[keyId: string]: MediaKeyStatus;
40294033
};
4034+
keyStatusTimeouts?: {
4035+
[keyId: string]: number;
4036+
};
40304037
licenseXhr?: XMLHttpRequest;
40314038
};
40324039

src/controller/eme-controller.ts

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface MediaKeySessionContext {
5959
mediaKeys: MediaKeys;
6060
mediaKeysSession: MediaKeySession;
6161
keyStatuses: { [keyId: string]: MediaKeyStatus };
62+
keyStatusTimeouts?: { [keyId: string]: number };
6263
licenseXhr?: XMLHttpRequest;
6364
_onmessage?: (this: MediaKeySession, ev: MediaKeyMessageEvent) => any;
6465
_onkeystatuseschange?: (this: MediaKeySession, ev: Event) => any;
@@ -71,6 +72,7 @@ export type MediaKeySessionContextAndLevelKey = {
7172
mediaKeys: MediaKeys;
7273
mediaKeysSession: MediaKeySession;
7374
keyStatuses: { [keyId: string]: MediaKeyStatus };
75+
keyStatusTimeouts?: { [keyId: string]: number };
7476
licenseXhr?: XMLHttpRequest;
7577
};
7678

@@ -873,54 +875,8 @@ class EMEController extends Logger implements ComponentAPI {
873875
}
874876
});
875877

876-
const onkeystatuseschange = (context._onkeystatuseschange = (
877-
event: Event,
878-
) => {
879-
const keySession = context.mediaKeysSession;
880-
if (!keySession as any) {
881-
licenseStatus.emit('error', new Error('invalid state'));
882-
return;
883-
}
884-
885-
// populate/update context.keyStatuses
886-
this.onKeyStatusChange(context);
887-
888-
// renew when a key status comes back expired
889-
const keyIds = Object.keys(context.keyStatuses);
890-
if (keyIds.some((id) => context.keyStatuses[id] === 'expired')) {
891-
this.log(
892-
`${context.keySystem} expired for key ${stringify(context.keyStatuses)}`,
893-
);
894-
this.renewKeySession(context, levelKey);
895-
}
896-
897-
// handle status of current key
898-
let keyStatus = context.keyStatuses[keyId] as MediaKeyStatus | undefined;
878+
const handleKeyStatus = (keyStatus: MediaKeyStatus) => {
899879
let keyError: EMEKeyError | Error | undefined;
900-
if (!keyStatus) {
901-
// Handle case where no keyIds matched but all have the same status
902-
const keyIds = Object.keys(context.keyStatuses);
903-
if (keyIds.length) {
904-
const keyStatuses = keyIds.map((id) => context.keyStatuses[id]);
905-
const firstStatus = keyStatuses[0];
906-
const allSameStatus = !keyStatuses.some(
907-
(status) => status.substring(0, 6) !== firstStatus.substring(0, 6),
908-
);
909-
if (allSameStatus) {
910-
keyStatus = firstStatus;
911-
} else if (keyStatuses.some((status) => status === 'expired')) {
912-
keyStatus = 'expired';
913-
} else if (keyStatuses.some((status) => status === 'released')) {
914-
keyStatus = 'released';
915-
}
916-
}
917-
if (!keyStatus) {
918-
keyStatus = 'internal-error';
919-
}
920-
this.log(
921-
`No status for keyId ${keyId}. Using session key-status ${keyStatus} (${stringify(context.keyStatuses)}).`,
922-
);
923-
}
924880
if (keyStatus.startsWith('usable')) {
925881
licenseStatus.emit('resolved');
926882
} else if (
@@ -949,6 +905,59 @@ class EMEController extends Logger implements ComponentAPI {
949905
this.handleError(keyError);
950906
}
951907
}
908+
};
909+
const onkeystatuseschange = (context._onkeystatuseschange = (
910+
event: Event,
911+
) => {
912+
const keySession = context.mediaKeysSession;
913+
if (!keySession as any) {
914+
licenseStatus.emit('error', new Error('invalid state'));
915+
return;
916+
}
917+
918+
// populate/update context.keyStatuses
919+
this.onKeyStatusChange(context);
920+
921+
// renew when a key status comes back expired
922+
const keyIds = Object.keys(context.keyStatuses);
923+
if (keyIds.some((id) => context.keyStatuses[id] === 'expired')) {
924+
this.log(
925+
`${context.keySystem} expired for key ${stringify(context.keyStatuses)}`,
926+
);
927+
this.renewKeySession(context, levelKey);
928+
}
929+
930+
// handle status of current key
931+
let keyStatus = context.keyStatuses[keyId] as MediaKeyStatus | undefined;
932+
933+
if (!keyStatus) {
934+
keyStatus = 'status-pending';
935+
const keyIds = Object.keys(context.keyStatuses);
936+
if (keyIds.length) {
937+
const timeout = (this.hls as any)?.config.keyLoadPolicy.default
938+
.maxTimeToFirstByteMs;
939+
context.keyStatusTimeouts ||= {};
940+
context.keyStatusTimeouts[keyId] ||= self.setTimeout(() => {
941+
if (!context.levelKeys.length || !this.mediaKeys) {
942+
return;
943+
}
944+
945+
if (keyId in context.keyStatuses) {
946+
keyStatus = context.keyStatuses[keyId];
947+
} else {
948+
this.log(
949+
`key status for ${keyId} in session ${context.mediaKeysSession.sessionId} timed out after 5s.`,
950+
);
951+
keyStatus = 'internal-error';
952+
}
953+
handleKeyStatus(keyStatus);
954+
}, timeout);
955+
this.log(
956+
`No status for keyId ${keyId}. Using session key-status ${keyStatus} (${stringify(context.keyStatuses)}).`,
957+
);
958+
}
959+
}
960+
handleKeyStatus(keyStatus);
952961
});
953962

954963
addEventListener(context.mediaKeysSession, 'message', onmessage);
@@ -1526,6 +1535,12 @@ class EMEController extends Logger implements ComponentAPI {
15261535
if (index > -1) {
15271536
this.mediaKeySessions.splice(index, 1);
15281537
}
1538+
const { keyStatusTimeouts } = mediaKeySessionContext;
1539+
if (keyStatusTimeouts) {
1540+
Object.keys(keyStatusTimeouts).forEach((keyId) =>
1541+
self.clearTimeout(keyStatusTimeouts[keyId]),
1542+
);
1543+
}
15291544
const { drmSystemOptions } = this.config;
15301545
const removePromise = isPersistentSessionType(drmSystemOptions)
15311546
? new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)