Skip to content

Commit 39f02b0

Browse files
author
Rob Walch
committed
[WIP] Fix EME async errors
1 parent dea79af commit 39f02b0

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

src/controller/eme-controller.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,16 @@ class EMEController extends EventHandler {
8383
private _widevineLicenseUrl?: string;
8484
private _licenseXhrSetup?: (xhr: XMLHttpRequest, url: string) => void;
8585
private _emeEnabled: boolean;
86-
private _requestMediaKeySystemAccess: MediaKeyFunc | null
86+
private _requestMediaKeySystemAccess: MediaKeyFunc | null;
8787

8888
private _config: EMEControllerConfig;
8989
private _mediaKeysList: MediaKeysListItem[] = [];
9090
private _media: HTMLMediaElement | null = null;
9191
private _hasSetMediaKeys: boolean = false;
9292
private _requestLicenseFailureCount: number = 0;
9393

94+
private mediaKeysPromise: Promise<MediaKeys> | null = null;
95+
9496
/**
9597
* @constructs
9698
* @param {Hls} hls Our Hls.js instance
@@ -143,13 +145,14 @@ class EMEController extends EventHandler {
143145
logger.log('Requesting encrypted media key-system access');
144146

145147
// expecting interface like window.navigator.requestMediaKeySystemAccess
146-
this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs)
147-
.then((mediaKeySystemAccess) => {
148-
this._onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess);
149-
})
150-
.catch((err) => {
151-
logger.error(`Failed to obtain key-system "${keySystem}" access:`, err);
152-
});
148+
const keySystemAccessPromise = this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs);
149+
150+
this.mediaKeysPromise = keySystemAccessPromise.then((mediaKeySystemAccess) =>
151+
this._onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess));
152+
153+
keySystemAccessPromise.catch((err) => {
154+
logger.error(`Failed to obtain key-system "${keySystem}" access:`, err);
155+
});
153156
}
154157

155158
get requestMediaKeySystemAccess () {
@@ -166,7 +169,7 @@ class EMEController extends EventHandler {
166169
* @param {string} keySystem
167170
* @param {MediaKeySystemAccess} mediaKeySystemAccess https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemAccess
168171
*/
169-
private _onMediaKeySystemAccessObtained (keySystem: KeySystems, mediaKeySystemAccess: MediaKeySystemAccess) {
172+
private _onMediaKeySystemAccessObtained (keySystem: KeySystems, mediaKeySystemAccess: MediaKeySystemAccess): Promise<MediaKeys> {
170173
logger.log(`Access for key-system "${keySystem}" obtained`);
171174

172175
const mediaKeysListItem: MediaKeysListItem = {
@@ -177,17 +180,22 @@ class EMEController extends EventHandler {
177180

178181
this._mediaKeysList.push(mediaKeysListItem);
179182

180-
mediaKeySystemAccess.createMediaKeys()
183+
const mediaKeysPromise = Promise.resolve().then(() => mediaKeySystemAccess.createMediaKeys())
181184
.then((mediaKeys) => {
182185
mediaKeysListItem.mediaKeys = mediaKeys;
183186

184187
logger.log(`Media-keys created for key-system "${keySystem}"`);
185188

186189
this._onMediaKeysCreated();
187-
})
188-
.catch((err) => {
189-
logger.error('Failed to create media-keys:', err);
190+
191+
return mediaKeys;
190192
});
193+
194+
mediaKeysPromise.catch((err) => {
195+
logger.error('Failed to create media-keys:', err);
196+
});
197+
198+
return mediaKeysPromise;
191199
}
192200

193201
/**
@@ -235,20 +243,37 @@ class EMEController extends EventHandler {
235243

236244
/**
237245
* @private
238-
* @param {string} initDataType
239-
* @param {ArrayBuffer|null} initData
246+
* @param e {MediaEncryptedEvent}
240247
*/
241248
private _onMediaEncrypted = (e: MediaEncryptedEvent) => {
242249
logger.log(`Media is encrypted using "${e.initDataType}" init data type`);
243250

244-
this._attemptSetMediaKeys();
245-
this._generateRequestWithPreferredKeySession(e.initDataType, e.initData);
251+
if (!this.mediaKeysPromise) {
252+
logger.error('Fatal: Media is encrypted but no CDM access or no keys have been requested');
253+
this.hls.trigger(Event.ERROR, {
254+
type: ErrorTypes.KEY_SYSTEM_ERROR,
255+
details: ErrorDetails.KEY_SYSTEM_NO_KEYS,
256+
fatal: true
257+
});
258+
return;
259+
}
260+
261+
const finallySetKeyAndStartSession = (mediaKeys) => {
262+
if (!this._media) {
263+
return;
264+
}
265+
this._attemptSetMediaKeys(mediaKeys);
266+
this._generateRequestWithPreferredKeySession(e.initDataType, e.initData);
267+
};
268+
269+
// Could use `Promise.finally` but some Promise polyfills are missing it
270+
this.mediaKeysPromise.then(finallySetKeyAndStartSession).catch(finallySetKeyAndStartSession);
246271
}
247272

248273
/**
249274
* @private
250275
*/
251-
private _attemptSetMediaKeys () {
276+
private _attemptSetMediaKeys (mediaKeys?: MediaKeys) {
252277
if (!this._media) {
253278
throw new Error('Attempted to set mediaKeys without first attaching a media element');
254279
}

tests/unit/controller/eme-controller.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('EMEController', function () {
7575
}, 0);
7676
});
7777

78-
it('should trigger key system error when bad encrypted data is received', function (done) {
78+
it('should trigger key system error(s) when bad encrypted data is received', function (done) {
7979
let reqMediaKsAccessSpy = sinon.spy(function () {
8080
return Promise.resolve({
8181
// Media-keys mock
@@ -98,8 +98,9 @@ describe('EMEController', function () {
9898
media.emit('encrypted', badData);
9999

100100
setTimeout(function () {
101+
expect(emeController.hls.trigger).to.have.been.calledTwice;
101102
expect(emeController.hls.trigger.args[0][1].details).to.equal(ErrorDetails.KEY_SYSTEM_NO_KEYS);
102-
expect(emeController.hls.trigger.args[1][1].details).to.equal(ErrorDetails.KEY_SYSTEM_NO_ACCESS);
103+
expect(emeController.hls.trigger.args[1][1].details).to.equal(ErrorDetails.KEY_SYSTEM_NO_SESSION);
103104
done();
104105
}, 0);
105106
});

0 commit comments

Comments
 (0)