Signatur
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 Streamsdependencies– Array vonframeIds, auf die dieser Frame beim Dekodieren angewiesen istwidth,height– Auflösung des kodierten Frames (bei Keyframes zuverlässig, sonst ggf.0)spatialIndex,temporalIndex– Layer-Indizes bei SVC/SimulcastsynchronizationSource– SSRC des RTP-StreamscontributingSources– CSRC-Liste (bei Mixern)payloadType– RTP-Payload-Typ,mimeType– Codec, z. B."video/VP8"rtpTimestamp– RTP-Zeitstempel; je nach Browser zusätzlichcaptureTime,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
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);
};
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);
// Wichtig · Fallstricke
- Browser-Unterstützung: Chromium unterstützt die Methode seit Version 86, zunächst über das nicht standardisierte
createEncodedStreams();RTCRtpScriptTransformist in neueren Chromium-Versionen verfügbar. Firefox (ab 117) und Safari (ab 15.4) implementieren den standardisierten Weg überRTCRtpScriptTransformim 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/heightsind oft nur bei Keyframes gesetzt,frameId/dependenciesbrauchen 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.logbei 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.