diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index b3f2ebcc3ad..6afecb0573e 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -522,10 +522,27 @@ class EMEController extends EventHandler { } onMediaDetached () { - if (this._media) { - this._media.removeEventListener('encrypted', this._onMediaEncrypted); - this._media = null; // release reference + const media = this._media; + const mediaKeysList = this._mediaKeysList; + if (!media) { + return; } + media.removeEventListener('encrypted', this._onMediaEncrypted); + this._media = null; + this._mediaKeysList = []; + // Close all sessions and remove media keys from the video element. + Promise.all(mediaKeysList.map((mediaKeysListItem) => { + if (mediaKeysListItem.mediaKeysSession) { + return mediaKeysListItem.mediaKeysSession.close().catch(() => { + // Ignore errors when closing the sessions. Closing a session that + // generated no key requests will throw an error. + }); + } + })).then(() => { + return media.setMediaKeys(null); + }).catch(() => { + // Ignore any failures while removing media keys from the video element. + }); } // TODO: Use manifest types here when they are defined diff --git a/tests/unit/controller/eme-controller.js b/tests/unit/controller/eme-controller.js index cad40ea2deb..1811ead495b 100644 --- a/tests/unit/controller/eme-controller.js +++ b/tests/unit/controller/eme-controller.js @@ -9,6 +9,7 @@ const MediaMock = function () { let media = new EventEmitter(); media.setMediaKeys = sinon.spy(); media.addEventListener = media.addListener.bind(media); + media.removeEventListener = media.removeListener.bind(media); return media; }; @@ -104,4 +105,33 @@ describe('EMEController', function () { done(); }, 0); }); + + it('should close all media key sessions and remove media keys when media is detached', function (done) { + let reqMediaKsAccessSpy = sinon.spy(function () { + return Promise.resolve({ + // Media-keys mock + }); + }); + let keySessionCloseSpy = sinon.spy(() => Promise.resolve()); + + setupEach({ + emeEnabled: true, + requestMediaKeySystemAccessFunc: reqMediaKsAccessSpy + }); + + emeController.onMediaAttached({ media }); + emeController._mediaKeysList = [{ + mediaKeysSession: { + close: keySessionCloseSpy + } + }]; + emeController.onMediaDetached(); + + setTimeout(function () { + expect(keySessionCloseSpy.callCount).to.equal(1); + expect(emeController._mediaKeysList.length).to.equal(0); + expect(media.setMediaKeys.calledWith(null)).to.be.true; + done(); + }, 0); + }); });