Start · Sprachen · JavaScript · Referenz · MediaKeySession: keystatuseschange

MediaKeySession: keystatuseschange

Ereignis

Wird an einer <code>MediaKeySession</code> ausgelöst, wenn sich die Schlüssel der Sitzung oder deren Status (z. B. <code>usable</code>, <code>expired</code>) geändert haben.

seit JavaScript Baseline: alle modernen Browser (EME) Kategorie: webapi

Signatur

mediaKeySession.addEventListener("keystatuseschange", handler)

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

Typ
void
Beschreibung
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);
});
Schlüssel 9eb4050de44b4802932e27d75083e266: usable

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;
      }
    }
  };
};
Lizenz abgelaufen – Sitzung wird geschlossen

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);
Bereit: ["usable"]

// Wichtig · Fallstricke

  • Das Ereignis enthält keine Detaildaten – immer session.keyStatuses auslesen. 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 oder keyStatuses.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. als released oder die Map ist leer.
  • Statuswerte wie usable-in-future sind 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-media in iframes gesperrt sein. Unterstützte Schlüsselsysteme unterscheiden sich je Browser (Widevine in Chrome/Firefox/Edge, FairPlay in Safari, PlayReady in Edge).