Start · Sprachen · JavaScript · Referenz · MediaStreamTrackGenerator.writable

MediaStreamTrackGenerator.writable

Eigenschaft

Liefert einen <code>WritableStream</code>, in den <code>VideoFrame</code>- bzw. <code>AudioData</code>-Objekte geschrieben werden, die der Generator als Medienspur ausgibt.

seit JavaScript Nicht standardisiert – Chromium 94+ (Chrome, Edge, Kategorie: webapi

Signatur

MediaStreamTrackGenerator.writable

Beschreibung

Die schreibgeschützte Eigenschaft writable eines MediaStreamTrackGenerator gibt einen WritableStream zurück. Jeder Chunk, den du in diesen Stream schreibst, wird als neues Medien-Frame der Spur ausgegeben. Der Generator selbst ist ein MediaStreamTrack und lässt sich deshalb überall einsetzen, wo eine Spur erwartet wird: in einem <video>-Element über srcObject, in einer RTCPeerConnection oder im MediaRecorder.

Welcher Typ von Chunk erwartet wird, hängt von kind ab, das beim Erzeugen angegeben wurde: Bei "video" sind es VideoFrame-Objekte, bei "audio" AudioData-Objekte. Werden falsche Typen geschrieben, wird der Schreibvorgang mit einem TypeError abgewiesen.

Typischerweise nutzt man writable zusammen mit MediaStreamTrackProcessor („Insertable Streams for MediaStreamTrack“): Der Prozessor liefert die Frames einer Kamera-Spur als ReadableStream, ein TransformStream verändert sie (z. B. Filter, Hintergrundunschärfe, Wasserzeichen), und das Ergebnis wird per pipeThrough()/pipeTo() in generator.writable geleitet. Genauso können Frames komplett synthetisch erzeugt werden, etwa aus einem Canvas oder aus WebCodecs-Dekodern.

Rückgabewert

Typ
WritableStream<VideoFrame | AudioData>
Beschreibung
Ein WritableStream, dessen Chunks als Frames der generierten Spur ausgegeben werden. Es wird immer dasselbe Stream-Objekt zurückgegeben.

Beispiele

Kamerabild in Graustufen umwandeln (Pipeline)

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(); // Original freigeben!
    controller.enqueue(newFrame);
  }
});

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

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

Synthetische Frames direkt mit einem Writer schreiben

const generator = new MediaStreamTrackGenerator({ kind: 'video' });
const writer = generator.writable.getWriter();

const canvas = new OffscreenCanvas(320, 180);
const ctx = canvas.getContext('2d');
let n = 0;

const timer = setInterval(async () => {
  ctx.fillStyle = `hsl(${(n * 5) % 360} 80% 50%)`;
  ctx.fillRect(0, 0, 320, 180);
  ctx.fillStyle = '#fff';
  ctx.font = '32px sans-serif';
  ctx.fillText(`Frame ${n}`, 20, 100);

  const frame = new VideoFrame(canvas, { timestamp: n * 33_333 });
  await writer.write(frame);
  frame.close();
  n++;
}, 33);

document.querySelector('video').srcObject = new MediaStream([generator]);
console.log(generator.readyState);
live

Stream beenden und Spur schließen

const generator = new MediaStreamTrackGenerator({ kind: 'audio' });
const writer = generator.writable.getWriter();

generator.addEventListener('ended', () => console.log('Spur beendet'));

// ... AudioData-Objekte schreiben ...

await writer.close(); // beendet die Spur
console.log(generator.readyState);
ended

// Wichtig · Fallstricke

  • Kompatibilität: MediaStreamTrackGenerator ist nicht standardisiert und nur in Chromium-Browsern verfügbar. Die W3C-Spezifikation sieht stattdessen VideoTrackGenerator (nur Video, nur in Workern, mit Eigenschaft track) vor – Safari unterstützt diese Variante. Immer per Feature-Detection prüfen: 'MediaStreamTrackGenerator' in self.
  • Speicher: VideoFrame/AudioData belegen oft GPU- oder Hardware-Ressourcen. Frames, die du nicht weiterreichst, musst du mit close() freigeben, sonst stockt die Kamera oder der Speicher läuft voll.
  • Sperre: Nach getWriter() oder pipeTo() ist der Stream gesperrt; ein zweiter Writer wirft einen TypeError, bis releaseLock() aufgerufen wurde.
  • Beenden: close() oder abort() am Stream beendet die Spur (readyState wird "ended"); umgekehrt schlagen Schreibvorgänge nach generator.stop() fehl.
  • Zeitstempel (timestamp in Mikrosekunden) sollten monoton steigen, damit Wiedergabe und Encoder korrekt arbeiten.