Signatur
Beschreibung
Die Eigenschaft data eines RTCEncodedVideoFrame enthält die kodierten Bytes eines Video-Frames als ArrayBuffer, also z. B. VP8-, VP9-, AV1- oder H.264-Daten. Auf die Frames greift man über die WebRTC Encoded Transform-API zu, und zwar zwischen Encoder und Paketierer (Senden) oder zwischen Depaketierer und Decoder (Empfangen).
Typische Einsatzfälle sind Ende-zu-Ende-Verschlüsselung (etwa über eine SFU), das Einbetten von Metadaten oder das Analysieren des Bitstroms. Dazu liest man data, erzeugt einen neuen Puffer und weist ihn data wieder zu, bevor man den Frame an den TransformStream-Controller weitergibt.
Auf den ArrayBuffer selbst greift man über typisierte Ansichten wie Uint8Array oder DataView zu. Ob es sich um einen Schlüsselbild-Frame ("key") oder einen Delta-Frame ("delta") handelt, verrät frame.type. Weitere Angaben wie Payload-Typ, SSRC und Abmessungen liefert frame.getMetadata().
Rückgabewert
ArrayBuffer zugewiesen werden.Beispiele
Frame-Daten im Worker per XOR verschlüsseln (Sender)
// worker.js
const KEY = 0x5a;
const encrypt = (frame, controller) => {
const src = new Uint8Array(frame.data);
const out = new Uint8Array(src.length);
// Die ersten 10 Bytes (VP8-Header) unverändert lassen,
// damit der Paketierer weiterarbeiten kann
const headerLen = frame.type === 'key' ? 10 : 3;
for (let i = 0; i < src.length; i++) {
out[i] = i < headerLen ? src[i] : src[i] ^ KEY;
}
frame.data = out.buffer;
controller.enqueue(frame);
};
onrtctransform = ({ transformer }) => {
transformer.readable
.pipeThrough(new TransformStream({ transform: encrypt }))
.pipeTo(transformer.writable);
};
Transform im Hauptthread an einen Sender hängen
const pc = new RTCPeerConnection();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = stream.getVideoTracks();
const sender = pc.addTrack(track, stream);
const worker = new Worker('worker.js');
sender.transform = new RTCRtpScriptTransform(worker, { side: 'send' });
Frame-Größen analysieren
onrtctransform = ({ transformer }) => {
transformer.readable
.pipeThrough(new TransformStream({
transform(frame, controller) {
console.log(frame.type, frame.data.byteLength, 'Bytes');
controller.enqueue(frame);
}
}))
.pipeTo(transformer.writable);
};
// Wichtig · Fallstricke
- Kodierte Daten:
dataenthält keine Pixel, sondern den komprimierten Bitstrom. Rohe Bilddaten erhält man überVideoFrame(WebCodecs) oder ein Canvas. - Header erhalten: Wenn man codec-spezifische Header (z. B. den VP8-Payload-Header) mitverschlüsselt, können SFUs und Paketierer die Frames nicht mehr verarbeiten. Bei H.264 können außerdem die NAL-Unit-Grenzen verloren gehen.
- Zuweisen statt kopieren: Änderungen an einer
Uint8Array-Ansicht wirken direkt auf den Puffer. Einen Puffer anderer Länge muss man dagegen neu zuweisen (frame.data = neu.buffer). Denk daran, den Frame danach mitcontroller.enqueue()weiterzugeben. - Kompatibilität: Der Standardweg ist
RTCRtpScriptTransformin Worker-Threads (Firefox, Safari, neuere Chrome-Versionen). Ältere Chromium-Versionen kennen nur die proprietären Insertable Streams (createEncodedStreams()mitencodedInsertableStreams: true). Prüfe daher per Feature-Erkennung, was verfügbar ist. - Sicherheit: Ein einfaches XOR ist keine echte Verschlüsselung. Nutze für Ende-zu-Ende-Verschlüsselung z. B. AES-GCM über
crypto.subtleund verwalte die Schlüssel sauber (SFrame-Standard).