Start · Sprachen · JavaScript · Referenz · MediaStreamTrackProcessor.readable

MediaStreamTrackProcessor.readable

Eigenschaft

Nur lesbare Eigenschaft, die einen <code>ReadableStream</code> liefert. Er gibt die Frames eines <code>MediaStreamTrack</code> einzeln aus: <code>VideoFrame</code>s bei Video-Tracks, <code>AudioData</code> bei Audio-Tracks.

seit JavaScript Chrome/Edge 94+ (auch im Hauptthread), Safari 18+ Kategorie: webapi

Signatur

MediaStreamTrackProcessor.readable

Beschreibung

Über readable kommt man an die Rohdaten eines Kamera-, Bildschirm- oder Mikrofon-Tracks heran. Der MediaStreamTrackProcessor teilt den übergebenen MediaStreamTrack in einzelne Medien-Frames auf. Diese Frames liefert er als Einträge (Chunks) eines ReadableStream aus. Bei Video-Tracks ist jeder Chunk ein VideoFrame, bei Audio-Tracks ein AudioData-Objekt. Audio-Tracks unterstützt bisher nur Chromium.

Typische Einsatzzwecke sind die Echtzeit-Verarbeitung von Video mit WebCodecs und die Bildanalyse per Canvas, WebGL oder WebGPU. Beispiele dafür sind weichgezeichnete Hintergründe, Filter, Wasserzeichen und maschinelles Lernen. Ein weiterer Einsatzzweck ist das direkte Kodieren mit einem VideoEncoder. Den Stream liest man entweder mit einem Reader (getReader()) oder man leitet ihn mit pipeThrough() durch einen TransformStream. Das Ergebnis leitet man danach mit pipeTo() in den writable-Stream eines VideoTrackGenerator bzw. MediaStreamTrackGenerator. So entsteht wieder ein normaler Track, den man z. B. in einem <video>-Element abspielen oder über WebRTC senden kann.

Die Eigenschaft ist nur lesbar und gibt bei jedem Zugriff dasselbe Stream-Objekt zurück. Wenn der Track endet, wird der Stream geschlossen. Liest der Verbraucher zu langsam, verwirft der Processor ältere Frames. Wie viele Frames er höchstens zwischenspeichert, legt die Konstruktor-Option maxBufferSize fest.

Rückgabewert

Typ
ReadableStream<VideoFrame | AudioData>
Beschreibung
Ein ReadableStream, dessen Chunks die Frames des Tracks sind: VideoFrame-Objekte bei Video-Tracks, AudioData-Objekte bei Audio-Tracks.

Beispiele

Kamera-Frames einzeln lesen und Metadaten ausgeben

const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = stream.getVideoTracks();

const processor = new MediaStreamTrackProcessor({ track });
const reader = processor.readable.getReader();

let count = 0;
while (count < 5) {
  const { value: frame, done } = await reader.read();
  if (done) break;
  console.log(`Frame ${count}: ${frame.displayWidth}x${frame.displayHeight}, t=${frame.timestamp}µs`);
  frame.close(); // Speicher sofort freigeben!
  count++;
}

reader.releaseLock();
track.stop();
Frame 0: 640x480, t=123456µs Frame 1: 640x480, t=156789µs ...

Graustufen-Filter mit TransformStream und MediaStreamTrackGenerator (Chromium)

const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = stream.getVideoTracks();

const processor = new MediaStreamTrackProcessor({ track });
const generator = new MediaStreamTrackGenerator({ kind: 'video' });

const canvas = new OffscreenCanvas(1, 1);
const ctx = canvas.getContext('2d');

const grayscale = new TransformStream({
  transform(frame, controller) {
    canvas.width = frame.displayWidth;
    canvas.height = frame.displayHeight;
    ctx.filter = 'grayscale(1)';
    ctx.drawImage(frame, 0, 0);
    const newFrame = new VideoFrame(canvas, { timestamp: frame.timestamp });
    frame.close();
    controller.enqueue(newFrame);
  }
});

processor.readable
  .pipeThrough(grayscale)
  .pipeTo(generator.writable)
  .catch((err) => console.error('Pipeline beendet:', err));

document.querySelector('video').srcObject = new MediaStream([generator]);

Standardkonform im Worker verarbeiten (Safari/Spezifikation)

// main.js
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = stream.getVideoTracks();
const worker = new Worker('worker.js');
worker.postMessage({ track }, [track]); // Track ist übertragbar

// worker.js
self.onmessage = async ({ data: { track } }) => {
  const processor = new MediaStreamTrackProcessor({ track, maxBufferSize: 2 });
  const generator = new VideoTrackGenerator();

  await processor.readable
    .pipeThrough(new TransformStream({
      transform(frame, controller) {
        // hier z. B. Analyse oder Effekte
        controller.enqueue(frame);
      }
    }))
    .pipeTo(generator.writable);
};

// Wichtig · Fallstricke

  • Frames immer schließen: Jeder VideoFrame und jedes AudioData-Objekt belegt Speicher auf der GPU oder in Decoder-Puffern. Ohne frame.close() stockt der Stream schnell, und der Browser gibt Warnungen aus. Das gilt auch dann, wenn ein Frame nur verworfen wird.
  • Browser-Unterstützung: Laut Spezifikation ist MediaStreamTrackProcessor nur in Dedicated Workern verfügbar. Safari hält sich daran. Chromium erlaubt die Nutzung zusätzlich im Hauptthread und stellt statt VideoTrackGenerator den nicht standardisierten MediaStreamTrackGenerator bereit. Firefox unterstützt die API nicht. Verfügbarkeit daher vorab prüfen mit 'MediaStreamTrackProcessor' in self.
  • Nur ein Leser: Der Stream kann nur einmal gesperrt werden. Ein zweites getReader() oder pipeTo() wirft einen TypeError. Wer die Frames an mehrere Verbraucher verteilen will, nutzt readable.tee() und schließt dann in beiden Zweigen jeden Frame.
  • Latenz vs. Vollständigkeit: Ist die Verarbeitung zu langsam, verwirft der Processor Frames. Ein kleiner Wert für maxBufferSize hält die Latenz niedrig.
  • Datenschutz: Die Frames enthalten die unverarbeiteten Kamera- und Mikrofondaten. Daher ist die API nur in sicheren Kontexten (HTTPS) und nach Erteilung der Medienberechtigung nutzbar.