Signatur
Beschreibung
Die Eigenschaft data eines RTCEncodedAudioFrame enthält die bereits kodierten Bytes eines Audio-Frames als ArrayBuffer. Das sind die Daten, die der Encoder (typischerweise Opus) erzeugt hat bzw. die vor dem Dekodieren empfangen wurden. Die RTP-Header gehören nicht dazu. Die Eigenschaft ist les- und schreibbar: Du kannst den Puffer lesen, verändern oder durch einen neuen ArrayBuffer ersetzen, bevor du den Frame in der Pipeline weiterreichst.
Gebraucht wird das bei der WebRTC Encoded Transform API (RTCRtpScriptTransform im Worker oder, in älteren Chrome-Versionen, createEncodedStreams()). Typische Einsatzfälle sind Ende-zu-Ende-Verschlüsselung (E2EE) in Konferenzen über einen SFU-Server, das Anhängen eigener Metadaten an einen Frame oder Analyse und Statistik direkt auf Bitstrom-Ebene.
Weil ArrayBuffer selbst nicht direkt zugreifbar ist, legst du eine Ansicht darüber an, etwa new Uint8Array(frame.data) oder new DataView(frame.data). Änderungen wirken erst, wenn du den Frame danach mit controller.enqueue(frame) weitergibst. Soll sich die Länge ändern (z. B. durch einen Authentifizierungs-Tag), musst du einen neuen Puffer anlegen und frame.data zuweisen.
Rückgabewert
ArrayBuffer mit den kodierten Audiodaten des Frames. Du kannst einen neuen ArrayBuffer zuweisen, um die Nutzdaten zu ersetzen.Beispiele
Frames im Worker mit XOR „verschlüsseln“ (Demo)
// worker.js
const KEY = 0x5a;
const xorTransform = new TransformStream({
transform(frame, controller) {
const bytes = new Uint8Array(frame.data);
for (let i = 0; i < bytes.length; i++) {
bytes[i] ^= KEY;
}
// Die Bytes wurden direkt im Puffer geändert, der Frame kann weiter
controller.enqueue(frame);
}
});
addEventListener('rtctransform', (event) => {
const { readable, writable } = event.transformer;
readable.pipeThrough(xorTransform).pipeTo(writable);
});
Transform am Sender registrieren (Hauptthread)
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const pc = new RTCPeerConnection();
const [track] = stream.getAudioTracks();
const sender = pc.addTrack(track, stream);
const worker = new Worker('worker.js');
sender.transform = new RTCRtpScriptTransform(worker, { side: 'send' });
Trailer anhängen: data durch einen größeren Puffer ersetzen
const appendTrailer = new TransformStream({
transform(frame, controller) {
const original = new Uint8Array(frame.data);
const out = new Uint8Array(original.length + 1);
out.set(original);
out[original.length] = 0x01; // eigenes Marker-Byte
frame.data = out.buffer; // neuen ArrayBuffer zuweisen
controller.enqueue(frame);
}
});
Framegrößen protokollieren
const logSizes = new TransformStream({
transform(frame, controller) {
console.log(`Audio-Frame: ${frame.data.byteLength} Bytes, ts=${frame.timestamp}`);
controller.enqueue(frame);
}
});
// Wichtig · Fallstricke
- Browser-Unterstützung:
RTCRtpScriptTransformgibt es in Firefox und Safari. Chrome hatte lange nur die ältere, nicht standardisierte APIRTCRtpSender.createEncodedStreams(), für dieencodedInsertableStreams: truein derRTCPeerConnection-Konfiguration nötig ist. Prüfe die Unterstützung zur Laufzeit mit'RTCRtpScriptTransform' in window. - Bitstrom beschädigen: Wenn du die Daten veränderst und die Gegenseite sie nicht mit einer passenden Transformation zurückwandelt, kann der Decoder sie nicht lesen. Das Ergebnis sind Stille oder Störgeräusche.
- Sicherheit: XOR ist keine Verschlüsselung. Für echte E2EE nimmst du WebCrypto (z. B. AES-GCM mit eindeutigem IV pro Frame) und ein sauberes Schlüsselmanagement, wie etwa in SFrame.
- Der Puffer wird nicht automatisch kopiert. Brauchst du die Originaldaten später noch, kopiere sie vorher mit
frame.data.slice(0). - Größere Frames verbrauchen Bitrate. Halte zusätzliche Daten deshalb klein, weil die Paketgröße (MTU) begrenzt ist.