Start · Sprachen · JavaScript · Referenz · VideoTrackGenerator

VideoTrackGenerator

Schnittstelle

Erzeugt einen Video-<code>MediaStreamTrack</code>, dessen Bilder über einen <code>WritableStream</code> als <code>VideoFrame</code>-Objekte hineingeschrieben werden.

seit JavaScript Nur in Dedicated Workers; Safari 18+, in Chrome/Fi Kategorie: webapi

Signatur

interface VideoTrackGenerator

Beschreibung

VideoTrackGenerator gehört zur Insertable Streams for MediaStreamTrack API (Breakout-Box). Die Schnittstelle ist das Gegenstück zu MediaStreamTrackProcessor: Der Processor zerlegt einen Track in einzelne VideoFrame-Objekte, der Generator setzt einzelne Frames wieder zu einem abspielbaren Track zusammen. Damit lassen sich eigene Videoquellen oder Filter bauen, etwa Hintergrund-Unschärfe, Wasserzeichen, Bildanalyse oder per Canvas gerenderte Inhalte, die dann per WebRTC verschickt, mit MediaRecorder aufgenommen oder in einem <video>-Element angezeigt werden.

Die Schnittstelle ist bewusst schlank:

  • new VideoTrackGenerator() – Konstruktor ohne Parameter.
  • writable (nur lesbar) – ein WritableStream, in den VideoFrame-Objekte geschrieben werden.
  • track (nur lesbar) – der erzeugte MediaStreamTrack vom Typ "video".
  • muted (les- und schreibbar) – ist der Wert true, wird der Track stummgeschaltet (track.muted wird true, geschriebene Frames werden verworfen).

Laut Spezifikation steht VideoTrackGenerator nur in Dedicated Workers zur Verfügung. So läuft die rechenintensive Bildverarbeitung nicht auf dem Haupt-Thread. Den fertigen Track überträgt man per postMessage() als transferierbares Objekt zurück ins Hauptfenster.

Parameter

Name Typ Default Beschreibung
$(keine) — Der Konstruktor new VideoTrackGenerator() erwartet keine Argumente.

Rückgabewert

Typ
VideoTrackGenerator
Beschreibung
Eine neue Instanz mit den Eigenschaften writable (WritableStream<VideoFrame>), track (MediaStreamTrack) und muted (boolean).

Beispiele

Kamerabild im Worker filtern (worker.js)

// worker.js – empfängt einen Kamera-Track, verarbeitet ihn und sendet einen neuen Track zurück
self.onmessage = async ({ data }) => {
  const processor = new MediaStreamTrackProcessor({ track: data.track });
  const generator = new VideoTrackGenerator();

  const transformer = new TransformStream({
    async transform(frame, controller) {
      // Beispiel: Frame horizontal spiegeln
      const canvas = new OffscreenCanvas(frame.displayWidth, frame.displayHeight);
      const ctx = canvas.getContext('2d');
      ctx.scale(-1, 1);
      ctx.drawImage(frame, -canvas.width, 0);
      const newFrame = new VideoFrame(canvas, { timestamp: frame.timestamp });
      frame.close(); // Speicher des Original-Frames freigeben!
      controller.enqueue(newFrame);
    }
  });

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

  // Erzeugten Track an das Hauptfenster übertragen
  self.postMessage({ track: generator.track }, [generator.track]);
};

Hauptfenster: Track übergeben und Ergebnis anzeigen (main.js)

// main.js
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [cameraTrack] = stream.getVideoTracks();

const worker = new Worker('worker.js');
worker.postMessage({ track: cameraTrack }, [cameraTrack]);

worker.onmessage = ({ data }) => {
  const video = document.querySelector('video');
  video.srcObject = new MediaStream([data.track]);
  video.play();
  console.log('Gefilterter Track:', data.track.kind);
};
Gefilterter Track: video

Eigene Videoquelle aus OffscreenCanvas erzeugen

// worker.js – erzeugt einen animierten Testbild-Track
const generator = new VideoTrackGenerator();
const writer = generator.writable.getWriter();
const canvas = new OffscreenCanvas(640, 360);
const ctx = canvas.getContext('2d');
let n = 0;

const drawFrame = async () => {
  ctx.fillStyle = `hsl(${n % 360} 70% 50%)`;
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#fff';
  ctx.font = '48px sans-serif';
  ctx.fillText(`Frame ${n}`, 40, 200);

  const frame = new VideoFrame(canvas, { timestamp: n * 33333 }); // Mikrosekunden
  await writer.write(frame); // der Generator übernimmt und schließt den Frame
  n++;
};

setInterval(drawFrame, 1000 / 30);
self.postMessage({ track: generator.track }, [generator.track]);

Track stummschalten und beenden

// worker.js
const generator = new VideoTrackGenerator();

generator.muted = true;
console.log(generator.track.muted); // Stummschaltung ist im Track sichtbar

generator.muted = false;

// Schließen des Streams beendet den Track
await generator.writable.close();
console.log(generator.track.readyState);
true ended

// Wichtig · Fallstricke

  • Nur im Worker: Im Fenster-Kontext ist VideoTrackGenerator nicht definiert. Tracks müssen per postMessage(…, [track]) übertragen werden. Das setzt transferierbare MediaStreamTrack-Objekte voraus.
  • Browser-Unterstützung: Stand 2024 nur in Safari 18+. Chromium-Browser bieten stattdessen das ältere, nicht standardisierte MediaStreamTrackGenerator (auch im Haupt-Thread, mit { kind: 'video' }). Eine Feature-Erkennung per 'VideoTrackGenerator' in self mit Rückfall auf die alte Variante ist empfehlenswert.
  • Speicherlecks: VideoFrame-Objekte belegen oft GPU-Speicher. Nicht mehr benötigte Frames (z. B. Originale nach einer Transformation) müssen mit frame.close() freigegeben werden, sonst bleibt die Pipeline hängen oder der Speicher läuft voll. In den Generator geschriebene Frames gibt der Generator selbst frei.
  • Zeitstempel: timestamp wird in Mikrosekunden angegeben und sollte monoton steigen, sonst kann es bei Encoder oder MediaRecorder zu Problemen kommen.
  • Lebensdauer: Durch writable.close() oder abort() wird der Track beendet (readyState === "ended"). Umgekehrt führt track.stop() dazu, dass weitere Schreibversuche fehlschlagen.
  • Den Track können Sie mit track.clone() mehrfach verwenden, ohne einen zweiten Generator anzulegen.