Skip to content

Commit 8fad022

Browse files
committed
Call generateRequest BEFORE pushing any segment with PlayReady
Another day, another PlayReady-specific issue :/ A partner signalled to us that they weren't able to play a mix of unencrypted and encrypted content on any PlayReady devices. After investigation, it seems that calling `generateRequest` for the first time after clear segments are already present on a MSE `SourceBuffer` associated to the MediaSource linked to the corresponding media element immediately triggered an HTML5 `MEDIA_ERR_DECODE` error. We tried A LOT of work-arounds: - patching clear segments with a `tenc` box with a `0x0` key id to incite the CDM to understand that encrypted contents may be pushed in the future - Rewriting the pssh sent through the EME `generateRequest` API so that it is barebone to limit weird PlayReady edge cases. - Replacing those stream clear segments' with those in our demo page, just to check that the clear segments were not at fault here - Waiting more time between the association of a MediaKeys to the media element and pushing the first segments. None of those actions had an effect. However, what had an effect, was to call the `generateRequest` API BEFORE buffering any segment yet AFTER attaching the MediaKeys (and perhaps MediaSource) to the media element. So this commit does just that, communicating dummy initialization data for a session that will be closed directly after. Note that we already do a fake `generateRequest` on Edge Chromium with Playready since #1434, yet this test was not sufficient, seemingly because it is performed BEFORE MediaKeys attachment. Note that this commit fixes the clear -> encrypted issues our partner were having, but we're unsure yet of if it fixes the encrypted -> clear issues (and I have good reasons to think it does not). So, uh, yeah, PlayReady seems to keep being hard-at-work giving us challenges and head-scratchers.
1 parent 74b603a commit 8fad022

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* (2024-07-23) We noticed issues with most devices relying on PlayReady when
3+
* playing some contents with mix encrypted and clear contents (not with
4+
* Canal+ own contents weirdly enough, yet with multiple other contents
5+
* encoded/packaged differently).
6+
* The issue fixed itself when we called the
7+
* `MediaKeySession.prototype.generateRequest` EME API **BEFORE** any segment
8+
* was buffered.
9+
* @param {string} keySystem - The key system in use.
10+
* @returns {boolean}
11+
*/
12+
export default function shouldCallGenerateRequestBeforeBufferingMedia(
13+
keySystem: string,
14+
): boolean {
15+
if (keySystem.indexOf("playready") !== -1) {
16+
return true;
17+
}
18+
return false;
19+
}

src/main_thread/decrypt/content_decryptor.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
*/
1616

1717
import type { ICustomMediaKeys, ICustomMediaKeySystemAccess } from "../../compat/eme";
18-
import eme, { getInitData } from "../../compat/eme";
18+
import eme, { closeSession, getInitData } from "../../compat/eme";
19+
import {
20+
DUMMY_PLAY_READY_HEADER,
21+
generatePlayReadyInitData,
22+
} from "../../compat/generate_init_data";
23+
import shouldCallGenerateRequestBeforeBufferingMedia from "../../compat/should_call_generate_request_before_buffering_media";
1924
import config from "../../config";
2025
import { EncryptedMediaError, OtherError } from "../../errors";
2126
import log from "../../log";
@@ -291,6 +296,28 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
291296
return;
292297
}
293298

299+
if (
300+
shouldCallGenerateRequestBeforeBufferingMedia(mediaKeySystemAccess.keySystem)
301+
) {
302+
try {
303+
const session = mediaKeys.createSession();
304+
const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
305+
await session.generateRequest("cenc", initData);
306+
closeSession(session).catch((err) => {
307+
const error = err instanceof Error ? err : new Error("Unknown Error");
308+
log.warn("DRM: unable to close initial fake MediaKeySession", error);
309+
});
310+
} catch (err) {
311+
const error = err instanceof Error ? err : new Error("Unknown Error");
312+
log.warn("DRM: unable to fully perform fake generateRequest call", error);
313+
}
314+
}
315+
316+
if (this._isStopped()) {
317+
// We might be stopped since then
318+
return;
319+
}
320+
294321
const prevState = this._stateData.state;
295322
this._stateData = {
296323
state: ContentDecryptorState.ReadyForContent,

0 commit comments

Comments
 (0)