Signatur
Beschreibung
MediaKeySession gehört zur Encrypted Media Extensions API (EME). Mit einer Sitzung spricht deine Webanwendung mit dem Content Decryption Module (CDM) des Browsers, etwa Widevine, PlayReady, FairPlay oder Clear Key. Die Sitzung erzeugt Lizenzanfragen, nimmt die Antworten des Lizenzservers entgegen und verwaltet die Schlüssel, mit denen verschlüsselte Medien entschlüsselt werden.
Du erzeugst eine Sitzung nicht mit einem Konstruktor, sondern über MediaKeys.prototype.createSession(sessionType). Danach läuft es meist so ab:
generateRequest(initDataType, initData)mit den Initialisierungsdaten aus demencrypted-Event des Media-Elements aufrufen.- Auf das
message-Event warten. Es liefert die Lizenzanfrage alsArrayBuffer. - Die Anfrage an den Lizenzserver schicken und die Antwort mit
update(response)an die Sitzung übergeben. - Mit
keystatuseschangebzw.keyStatusesden Zustand der Schlüssel überwachen. Am Ende die Sitzung mitclose()beenden.
Eigenschaften: sessionId (string, schreibgeschützt), expiration (Ablaufzeit in ms seit der Epoche oder NaN), closed (Promise, das beim Schließen der Sitzung erfüllt wird), keyStatuses (MediaKeyStatusMap), sowie die Handler onmessage und onkeystatuseschange.
Methoden: generateRequest(), load(sessionId) (lädt eine persistente Sitzung), update(response), close() und remove() (löscht gespeicherte Lizenzdaten). Alle Methoden geben ein Promise zurück.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $sessionType | "temporary" | "persistent-license" | "temporary" | Wird an MediaKeys.createSession() übergeben und legt fest, ob die Lizenz nur im Speicher liegt oder dauerhaft gespeichert wird. "persistent-license" muss vorher in der MediaKeySystemConfiguration über sessionTypes angefordert worden sein. |
| $initDataType Pflicht | "cenc" | "keyids" | "webm" | string | Parameter von generateRequest(): Format der Initialisierungsdaten, meist event.initDataType aus dem encrypted-Event. |
|
| $initData Pflicht | ArrayBuffer | ArrayBufferView | Parameter von generateRequest(): die Initialisierungsdaten, z. B. die PSSH-Box bei CENC. |
|
| $response Pflicht | ArrayBuffer | ArrayBufferView | Parameter von update(): die Antwort des Lizenzservers bzw. bei Clear Key ein JSON-Web-Key-Set. |
Rückgabewert
MediaKeys.createSession(). Ihre Methoden geben jeweils ein Promise zurück: generateRequest(), update(), close() und remove() ein Promise<void>, load() ein Promise<boolean>.Beispiele
Sitzung erzeugen und Lizenz anfordern (Widevine)
const video = document.querySelector('video');
const config = [{
initDataTypes: ['cenc'],
videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }]
}];
const access = await navigator.requestMediaKeySystemAccess('com.widevine.alpha', config);
const mediaKeys = await access.createMediaKeys();
await video.setMediaKeys(mediaKeys);
video.addEventListener('encrypted', async (event) => {
const session = mediaKeys.createSession();
session.addEventListener('message', async (msgEvent) => {
// Lizenzanfrage an den Server schicken
const res = await fetch('https://license.example.com/widevine', {
method: 'POST',
body: msgEvent.message
});
const license = await res.arrayBuffer();
await session.update(license);
console.log('Lizenz übernommen für Sitzung', session.sessionId);
});
await session.generateRequest(event.initDataType, event.initData);
});
Clear Key: Schlüssel direkt als JSON Web Key übergeben
const toBase64Url = (bytes) =>
btoa(String.fromCharCode(...bytes))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const keyId = new Uint8Array([0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x00]);
const key = new Uint8Array([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10]);
const access = await navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{
initDataTypes: ['keyids'],
videoCapabilities: [{ contentType: 'video/webm; codecs="vp9"' }]
}]);
const mediaKeys = await access.createMediaKeys();
const session = mediaKeys.createSession('temporary');
session.onmessage = async () => {
const jwks = { keys: [{ kty: 'oct', kid: toBase64Url(keyId), k: toBase64Url(key) }] };
await session.update(new TextEncoder().encode(JSON.stringify(jwks)));
};
const initData = new TextEncoder().encode(JSON.stringify({ kids: [toBase64Url(keyId)] }));
await session.generateRequest('keyids', initData);
Schlüsselstatus überwachen und Sitzung aufräumen
const watchSession = (session) => {
session.addEventListener('keystatuseschange', () => {
for (const [keyId, status] of session.keyStatuses) {
console.log('Schlüssel', new Uint8Array(keyId)[0], '→', status);
if (status === 'expired') session.close();
}
});
session.closed.then((reason) => {
// reason ab neueren Spezifikationen, z. B. "closed-by-application"
console.log('Sitzung geschlossen:', reason ?? 'unbekannt');
});
};
// Beim Verlassen der Seite alle Sitzungen schließen
const sessions = new Set();
window.addEventListener('pagehide', () => {
sessions.forEach((s) => s.close());
});
Persistente Sitzung laden und Lizenz entfernen
const restoreAndRemove = async (mediaKeys, storedId) => {
const session = mediaKeys.createSession('persistent-license');
const found = await session.load(storedId);
if (!found) {
console.log('Keine gespeicherte Sitzung gefunden');
return;
}
// Lizenzdaten löschen; CDM sendet ggf. eine "license-release"-Nachricht
session.onmessage = async (e) => {
const res = await fetch('/license/release', { method: 'POST', body: e.message });
await session.update(await res.arrayBuffer());
};
await session.remove();
console.log('Lizenz entfernt');
};