Start · Sprachen · JavaScript · Referenz · RTCEncodedVideoFrame.getMetadata()

RTCEncodedVideoFrame.getMetadata()

Methode

Gibt ein <code>RTCEncodedVideoFrameMetadata</code>-Objekt mit den Metadaten eines kodierten Videoframes zurück, z. B. Auflösung, SSRC, Frame-ID, Abhängigkeiten und Layer-Indizes.

seit JavaScript Chrome 86+, Edge 86+, Firefox 117+, Safari 15.4+ ( Kategorie: webapi

Signatur

RTCEncodedVideoFrame.prototype.getMetadata()

Beschreibung

Die Methode getMetadata() gehört zur Schnittstelle RTCEncodedVideoFrame der WebRTC Encoded Transform API (früher „Insertable Streams“). Mit dieser API kannst du kodierte Videoframes abfangen, bevor sie gesendet werden bzw. nachdem sie empfangen wurden, aber bevor sie dekodiert werden. getMetadata() liefert dabei Informationen, die nicht in den eigentlichen Nutzdaten (frame.data) stecken.

Zum zurückgegebenen Objekt gehören unter anderem:

  • frameId – eindeutige ID des Frames innerhalb eines Streams
  • dependencies – Array von frameIds, auf die dieser Frame beim Dekodieren angewiesen ist
  • width, height – Auflösung des kodierten Frames (bei Keyframes zuverlässig, sonst ggf. 0)
  • spatialIndex, temporalIndex – Layer-Indizes bei SVC/Simulcast
  • synchronizationSource – SSRC des RTP-Streams
  • contributingSources – CSRC-Liste (bei Mixern)
  • payloadType – RTP-Payload-Typ, mimeType – Codec, z. B. "video/VP8"
  • rtpTimestamp – RTP-Zeitstempel; je nach Browser zusätzlich captureTime, receiveTime

Typische Einsatzzwecke sind Ende-zu-Ende-Verschlüsselung (E2EE) von Videokonferenzen, bei der z. B. anhand von frame.type und der Metadaten entschieden wird, welche Bytes unverschlüsselt bleiben müssen, sowie Analyse, Logging, Layer-Filterung oder das Einbetten eigener Zusatzdaten. Die Methode wird fast immer innerhalb eines TransformStream in einem Worker aufgerufen, der über RTCRtpScriptTransform an einen RTCRtpSender oder RTCRtpReceiver gebunden ist.

Rückgabewert

Typ
RTCEncodedVideoFrameMetadata
Beschreibung
Ein neues, einfaches Objekt mit den Metadaten des Frames (u. a. frameId, dependencies, width, height, spatialIndex, temporalIndex, synchronizationSource, contributingSources, payloadType, mimeType, rtpTimestamp). Nicht verfügbare Felder fehlen oder sind undefined. Änderungen am Objekt wirken sich nicht auf den Frame aus.

Beispiele

Transform im Hauptthread an einen Sender binden

// main.js
const worker = new Worker('video-worker.js');

const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const pc = new RTCPeerConnection();
const [track] = stream.getVideoTracks();
const sender = pc.addTrack(track, stream);

// Kodierte Frames dieses Senders laufen nun durch den Worker
sender.transform = new RTCRtpScriptTransform(worker, { side: 'send' });

Metadaten im Worker auslesen und protokollieren

// video-worker.js
onrtctransform = (event) => {
  const { readable, writable, options } = event.transformer;

  const logger = new TransformStream({
    transform(frame, controller) {
      const meta = frame.getMetadata();
      if (frame.type === 'key') {
        console.log(
          `[${options.side}] Keyframe ${meta.frameId}: ` +
          `${meta.width}x${meta.height}, SSRC ${meta.synchronizationSource}, ` +
          `Codec ${meta.mimeType ?? meta.payloadType}`
        );
      }
      controller.enqueue(frame); // Frame unverändert weiterreichen
    }
  });

  readable.pipeThrough(logger).pipeTo(writable);
};
[send] Keyframe 1: 640x480, SSRC 3735928559, Codec video/VP8

Empfangsseite: nur den temporalen Basis-Layer durchlassen

// receiver-worker.js – verwirft höhere temporale Layer (SVC, z. B. L1T3)
onrtctransform = ({ transformer }) => {
  const filter = new TransformStream({
    transform(frame, controller) {
      const { temporalIndex = 0, dependencies = [] } = frame.getMetadata();
      if (temporalIndex === 0) {
        controller.enqueue(frame);
      }
      // Frames mit temporalIndex > 0 werden nicht weitergegeben
    }
  });
  transformer.readable.pipeThrough(filter).pipeTo(transformer.writable);
};

// main.js
// pc.ontrack = ({ receiver }) => {
//   receiver.transform = new RTCRtpScriptTransform(
//     new Worker('receiver-worker.js'), { side: 'receive' });
// };

Legacy-Variante in Chromium (createEncodedStreams)

// Nur Chromium, erfordert encodedInsertableStreams: true
const pc = new RTCPeerConnection({ encodedInsertableStreams: true });
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const sender = pc.addTrack(stream.getVideoTracks()[0], stream);

const { readable, writable } = sender.createEncodedStreams();
readable
  .pipeThrough(new TransformStream({
    transform(frame, controller) {
      const { frameId, dependencies } = frame.getMetadata();
      console.log(frameId, 'hängt ab von', dependencies);
      controller.enqueue(frame);
    }
  }))
  .pipeTo(writable);
42 hängt ab von [41]

// Wichtig · Fallstricke

  • Browser-Unterstützung: Chromium unterstützt die Methode seit Version 86, zunächst über das nicht standardisierte createEncodedStreams(); RTCRtpScriptTransform ist in neueren Chromium-Versionen verfügbar. Firefox (ab 117) und Safari (ab 15.4) implementieren den standardisierten Weg über RTCRtpScriptTransform im Worker. Vor der Nutzung per Feature-Erkennung prüfen, z. B. 'RTCRtpScriptTransform' in window.
  • Felder variieren: Welche Eigenschaften befüllt sind, hängt von Browser, Codec und Richtung ab. width/height sind oft nur bei Keyframes gesetzt, frameId/dependencies brauchen z. B. die Dependency-Descriptor-RTP-Erweiterung. Immer mit Standardwerten oder ?? absichern.
  • Kopie, keine Referenz: Wer das zurückgegebene Objekt ändert, ändert den Frame nicht. Zum Ändern gibt es in neueren Spezifikationen setMetadata(), das aber noch nicht überall unterstützt wird.
  • Performance: Der Transform läuft für jeden Frame, also 30–60 Mal pro Sekunde. Aufwendige Logik oder console.log bei jedem Frame vermeiden, sonst steigen Latenz und Frame-Verluste.
  • Frames nicht vergessen: Frames, die nicht per controller.enqueue() weitergegeben werden, gehen verloren. Verwirft man Frames, von denen andere abhängen (dependencies), führt das auf der Empfangsseite zu Dekodierfehlern oder eingefrorenem Bild.
  • Sicherheit: Die Metadaten sind bei E2EE nicht verschlüsselt, weil sie im RTP-Header bzw. in den Header-Erweiterungen stehen. Deshalb sind sie für den Media-Server sichtbar.