Signatur
Beschreibung
Das Ereignis keystatuseschange gehört zur Encrypted Media Extensions (EME)-API. Es wird an einer MediaKeySession ausgelöst, sobald das Content Decryption Module (CDM) die Menge der Schlüssel oder den Status mindestens eines Schlüssels verändert hat, etwa nachdem mit session.update() eine Lizenz eingespielt wurde, ein Schlüssel abläuft oder Ausgabebeschränkungen (HDCP) greifen.
Das Ereignis selbst ist ein einfaches Event ohne zusätzliche Daten. Den aktuellen Zustand liest man über die Eigenschaft session.keyStatuses aus, eine MediaKeyStatusMap, die Schlüssel-IDs (BufferSource) auf Statuswerte abbildet: usable, expired, released, output-restricted, output-downscaled, usable-in-future, status-pending und internal-error.
Typische Einsatzzwecke: erkennen, dass eine Wiedergabe jetzt möglich ist, abgelaufene Lizenzen erneuern, bei output-restricted auf eine niedrigere Qualitätsstufe wechseln oder dem Nutzer eine verständliche Fehlermeldung anzeigen. Registrieren lässt sich der Handler per addEventListener("keystatuseschange", …) oder über die Eigenschaft onkeystatuseschange.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "keystatuseschange". |
|
| $handler Pflicht | (event: Event) => void | Callback, das beim Ereignis aufgerufen wird. event.target ist die betroffene MediaKeySession. |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Das übergebene Ereignisobjekt ist ein generisches Event; die Statusdaten stehen in session.keyStatuses.Beispiele
Schlüsselstatus mit addEventListener auswerten
const video = document.querySelector('video');
const hex = (buf) => [...new Uint8Array(buf instanceof ArrayBuffer ? buf : buf.buffer)]
.map((b) => b.toString(16).padStart(2, '0')).join('');
video.addEventListener('encrypted', async (e) => {
const access = await navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{
initDataTypes: ['cenc'],
videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }]
}]);
const mediaKeys = await access.createMediaKeys();
await video.setMediaKeys(mediaKeys);
const session = mediaKeys.createSession();
session.addEventListener('keystatuseschange', () => {
session.keyStatuses.forEach((status, keyId) => {
console.log(`Schlüssel ${hex(keyId)}: ${status}`);
});
});
await session.generateRequest(e.initDataType, e.initData);
});
Auf abgelaufene oder eingeschränkte Schlüssel reagieren (on-Property)
const handleKeyStatuses = (session, player) => {
session.onkeystatuseschange = () => {
for (const [keyId, status] of session.keyStatuses) {
switch (status) {
case 'expired':
console.warn('Lizenz abgelaufen – Sitzung wird geschlossen');
session.close();
break;
case 'output-restricted':
console.warn('HDCP-Einschränkung – wechsle auf niedrigere Auflösung');
player.limitResolution(480);
break;
case 'internal-error':
player.showError('Entschlüsselung fehlgeschlagen.');
break;
}
}
};
};
Auf nutzbare Schlüssel warten und Listener wieder entfernen
const waitForUsableKey = (session, timeoutMs = 10000) =>
new Promise((resolve, reject) => {
const onChange = () => {
const statuses = [...session.keyStatuses.values()];
if (statuses.includes('usable')) {
cleanup();
resolve(statuses);
}
};
const timer = setTimeout(() => {
cleanup();
reject(new Error('Kein nutzbarer Schlüssel erhalten'));
}, timeoutMs);
const cleanup = () => {
clearTimeout(timer);
session.removeEventListener('keystatuseschange', onChange);
};
session.addEventListener('keystatuseschange', onChange);
});
// Verwendung nach session.update(license):
// const statuses = await waitForUsableKey(session);
// console.log('Bereit:', statuses);
// Wichtig · Fallstricke
- Das Ereignis enthält keine Detaildaten – immer
session.keyStatusesauslesen. Die Map wird vollständig ersetzt, einzelne Änderungen muss man selbst per Vergleich mit dem vorherigen Zustand ermitteln. - Schlüssel-IDs sind
ArrayBuffer/ArrayBufferView– ein Vergleich mit===funktioniert nicht; zum Vergleichen oder Loggen in Hex/Base64 umwandeln oderkeyStatuses.has(keyId)bzw.get(keyId)nutzen. - Das Ereignis kann mehrfach und auch ohne tatsächliche Statusänderung feuern (z. B. nach jedem
update()); Handler sollten daher idempotent sein. - Nach
session.close()bzw.remove()erscheinen Schlüssel ggf. alsreleasedoder die Map ist leer. - Statuswerte wie
usable-in-futuresind neuer und werden nicht von allen CDMs geliefert; unbekannte Werte robust behandeln. - EME ist nur in sicheren Kontexten (HTTPS) verfügbar und kann per Permissions-Policy
encrypted-mediain iframes gesperrt sein. Unterstützte Schlüsselsysteme unterscheiden sich je Browser (Widevine in Chrome/Firefox/Edge, FairPlay in Safari, PlayReady in Edge).