From 6ee2433c19138cbcf11c623b9e455aa1b958bdf0 Mon Sep 17 00:00:00 2001 From: Paul Berberian Date: Thu, 2 Nov 2023 14:10:41 +0100 Subject: [PATCH] Add the possibility to set a new `keySystems` option on the `reload` API A majority of the bigger issues we encounter in production is DRM-related, leading us to either work-around those in the RxPlayer, or to facilitate a work-around on the application-side through a better DRM-related API. Recently, we've seen that many Windows/Edge users (but still a minority of them) could encounter an issue on the `generateRequest` EME call when relying on PlayReady SL3000 (hardware-backed decryption and playback, seen as one the most secure DRM mechanism for OTT contents) which would lead to a failure to play the content. When this happens, fallbacking to a different key system like PlayReady SL2000 (where decryption happens in software) or Widevine usually (though not always) seems to avoid the issue, even if it might lead to less protection and thus might lead generally to only lower video qualities (as higher security requirements are in practice generally just enforced for the higher video qualities, depending on license policies). After brainstorming whether this fallback should be done on the RxPlayer-side, or on the application-side, we're for now implementing the easier way (for us :p) of just providing here an API allowing to just let the application replay the last loaded content (whose loading may have failed due to the aforementioned `generateRequest` error) with a different `keySystems` configuration, thus allowing an application to reload the last loaded content after blacklisting the current key system if the error appears to be linked to that issue. --- doc/api/Basic_Methods/reload.md | 11 +++++++++++ src/core/api/option_utils.ts | 8 ++++++++ src/core/api/public_api.ts | 14 +++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/doc/api/Basic_Methods/reload.md b/doc/api/Basic_Methods/reload.md index cc3534ba03..d64547a486 100644 --- a/doc/api/Basic_Methods/reload.md +++ b/doc/api/Basic_Methods/reload.md @@ -35,6 +35,17 @@ The options argument is an object containing : content was playing the last time it was played and stay in the `"LOADED"` state (and paused) if it was paused last time it was played. +- _keySystems_ (`Array. | undefined`): If set, a new configuration will + be set on this reloaded content regarding its decryption. + + The value of this property follows the exact same structure than for the + original `loadVideo` call, it is described in the [decryption options + documentation page](../Decryption_Options.md). + + You might for example want to update that way the `keySystems` option compared + to the one of the original `loadVideo` call when you suspect that there is a + decryption-related issue with the original `keySystems` given. + Note that despite this method's name, the player will not go through the `RELOADING` state while reloading the content but through the regular `LOADING` state - as if `loadVideo` was called on that same content again. diff --git a/src/core/api/option_utils.ts b/src/core/api/option_utils.ts index 1b00fd1778..ed7c230437 100644 --- a/src/core/api/option_utils.ts +++ b/src/core/api/option_utils.ts @@ -397,6 +397,8 @@ function parseConstructorOptions( */ function checkReloadOptions(options?: { reloadAt?: { position?: number; relative?: number }; + keySystems?: IKeySystemOption[]; + autoPlay?: boolean; }): void { if (options === null || (typeof options !== "object" && options !== undefined)) { @@ -414,6 +416,12 @@ function checkReloadOptions(options?: { options?.reloadAt?.relative !== undefined) { throw new Error("API: reload - Invalid 'reloadAt.relative' option format."); } + if (!Array.isArray(options?.keySystems) && options?.keySystems !== undefined) { + throw new Error("API: reload - Invalid 'keySystems' option format."); + } + if (options?.autoPlay !== undefined && typeof options.autoPlay !== "boolean") { + throw new Error("API: reload - Invalid 'autoPlay' option format."); + } } /** diff --git a/src/core/api/public_api.ts b/src/core/api/public_api.ts index 8d1ff7682b..40d044e4ca 100644 --- a/src/core/api/public_api.ts +++ b/src/core/api/public_api.ts @@ -59,6 +59,7 @@ import { IConstructorOptions, IDecipherabilityUpdateContent, IKeySystemConfigurationOutput, + IKeySystemOption, ILoadVideoOptions, IPeriod, IPlayerError, @@ -577,6 +578,7 @@ class Player extends EventEmitter { */ reload(reloadOpts?: { reloadAt?: { position?: number; relative?: number }; + keySystems?: IKeySystemOption[]; autoPlay?: boolean; }): void { const { options, @@ -609,6 +611,13 @@ class Player extends EventEmitter { autoPlay = !reloadInPause; } + let keySystems : IKeySystemOption[] | undefined; + if (reloadOpts?.keySystems !== undefined) { + keySystems = reloadOpts.keySystems; + } else if (this._priv_reloadingMetadata.options?.keySystems !== undefined) { + keySystems = this._priv_reloadingMetadata.options.keySystems; + } + const newOptions = { ...options, initialManifest: manifest }; if (startAt !== undefined) { @@ -617,6 +626,9 @@ class Player extends EventEmitter { if (autoPlay !== undefined) { newOptions.autoPlay = autoPlay; } + if (keySystems !== undefined) { + newOptions.keySystems = keySystems; + } this._priv_initializeContentPlayback(newOptions); } @@ -626,7 +638,7 @@ class Player extends EventEmitter { if (features.createDebugElement === null) { throw new Error("Feature `DEBUG_ELEMENT` not added to the RxPlayer"); } - const canceller = new TaskCanceller() ; + const canceller = new TaskCanceller(); features.createDebugElement(element, this, canceller.signal); return { dispose() {