Movatterモバイル変換


[0]ホーム

URL:


  1. Web
  2. Web-APIs
  3. WebRTC API
  4. Verwenden von WebRTC Encoded Transforms

Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.Erfahre mehr über dieses Experiment.

View in EnglishAlways switch to English

Verwenden von WebRTC Encoded Transforms

Baseline 2025
Newly available

Since ⁨October 2025⁩, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

WebRTC Encoded Transforms bieten einen Mechanismus, um eine leistungsfähigeStream-API zum Modifizieren von kodierten Video- und Audio-Frames in die eingehenden und ausgehenden WebRTC-Pipelines zu injizieren.Dies ermöglicht Anwendungsfälle wie Ende-zu-Ende-Verschlüsselung von kodierten Frames durch Drittanbieter-Code.

Die API definiert Objekte für den Hauptthread und für Worker-Seiten.Die Schnittstelle des Hauptthreads ist eineRTCRtpScriptTransform-Instanz, die bei der Konstruktion denWorker angibt, welcher den Transformator-Code implementieren soll.Der im Worker laufende Transformator wird in die eingehende oder ausgehende WebRTC-Pipeline eingefügt, indem dieRTCRtpScriptTransform zuRTCRtpReceiver.transform bzw.RTCRtpSender.transform hinzugefügt wird.

Ein entsprechendesRTCRtpScriptTransformer-Objekt wird im Worker-Thread erstellt, das eineReadableStreamreadable-Eigenschaft, eineWritableStreamwritable-Eigenschaft und einoptions-Objekt hat, welches vom zugehörigenRTCRtpScriptTransform-Konstruktor übergeben wird.Kodierte Videoframes (RTCEncodedVideoFrame) oder Audioframes (RTCEncodedAudioFrame) aus der WebRTC-Pipeline werden zur Verarbeitung inreadable eingereiht.

DerRTCRtpScriptTransformer wird alstransformer-Eigenschaft desrtctransform-Ereignisses bereitgestellt, welches im Worker-Globalumfang ausgelöst wird, wenn ein kodierter Frame zur Verarbeitung eingereiht wird (und anfangs bei der Konstruktion der entsprechendenRTCRtpScriptTransform).Der Worker-Code muss einen Handler für das Ereignis implementieren, der kodierte Frames vontransformer.readable liest, sie bei Bedarf modifiziert und in derselben Reihenfolge und ohne Duplikation intransformer.writable schreibt.

Während die Schnittstelle keine weiteren Einschränkungen für die Implementierung vorschreibt, besteht eine natürliche Möglichkeit, die Frames zu transformieren darin, einePipe-Kette zu erstellen, die Frames aus demevent.transformer.readable-Stream durch einenTransformStream zumevent.transformer.writable-Stream sendet.Wir können dieevent.transformer.options-Eigenschaft verwenden, um jeden Transformcode zu konfigurieren, der davon abhängt, ob der Transformator eingehende Frames vom Paketierer oder ausgehende Frames von einem Codec einreiht.

DieRTCRtpScriptTransformer-Schnittstelle bietet auch Methoden, die verwendet werden können, wenn kodiertes Video gesendet wird, um den Codec dazu zu bringen, einen "Schlüssel"-Frame zu generieren, und wenn Video empfangen wird, um den Versand eines neuen Schlüssel-Frames anzufordern.Diese können nützlich sein, damit ein Empfänger das Video schneller sehen kann, falls (zum Beispiel) er einem Konferenzgespräch beitritt, wenn Delta-Frames gesendet werden.

Die folgenden Beispiele bieten spezifischere Beispiele zur Nutzung des Frameworks mit einer aufTransformStream basierenden Implementierung.

Testen, ob kodierte Transforms unterstützt werden

Testen Sie, obkodierte Transforms unterstützt werden, indem Sie auf die Existenz vonRTCRtpSender.transform (oderRTCRtpReceiver.transform) prüfen:

js
const supportsEncodedTransforms =  window.RTCRtpSender && "transform" in RTCRtpSender.prototype;

Hinzufügen eines Transforms für ausgehende Frames

Ein im Worker laufender Transformator wird in die ausgehende WebRTC-Pipeline eingefügt, indem seine entsprechendeRTCRtpScriptTransform demRTCRtpSender.transform für eine ausgehende Spur zugewiesen wird.

Dieses Beispiel zeigt, wie Sie Video von der Webcam eines Benutzers über WebRTC streamen, indem Sie einen WebRTC-kodierten Transformator hinzufügen, um die ausgehenden Streams zu modifizieren.Der Code geht davon aus, dass eineRTCPeerConnection namenspeerConnection vorhanden ist, die bereits mit einem entfernten Peer verbunden ist.

Zuerst erhalten wir eineMediaStreamTrack, indem wirgetUserMedia() verwenden, um einen Video-MediaStream von einem Mediengerät zu erhalten, und dann die MethodeMediaStream.getTracks() verwenden, um die ersteMediaStreamTrack im Stream zu bekommen.

Die Spur wird der Peer-Verbindung mithilfe vonaddTrack() hinzugefügt, wodurch sie an den entfernten Peer gestreamt wird.Die MethodeaddTrack() gibt denRTCRtpSender zurück, der verwendet wird, um die Spur zu senden.

js
// Get Video stream and MediaTrackconst stream = await navigator.mediaDevices.getUserMedia({ video: true });const [track] = stream.getTracks();const videoSender = peerConnection.addTrack(track, stream);

EinRTCRtpScriptTransform wird dann erstellt, indem ein Worker-Skript, das den Transform definiert, und ein optionales Objekt, das verwendet werden kann, um beliebige Nachrichten an den Worker zu übergeben (in diesem Fall haben wir einename-Eigenschaft mit dem Wert "senderTransform" verwendet, um dem Worker mitzuteilen, dass dieser Transform zum ausgehenden Stream hinzugefügt wird), übergeben wird.Wir fügen den Transform zum ausgehenden Pipeline hinzu, indem wir ihn derRTCRtpSender.transform-Eigenschaft zuweisen.

js
// Create a worker containing a TransformStreamconst worker = new Worker("worker.js");videoSender.transform = new RTCRtpScriptTransform(worker, {  name: "senderTransform",});

Der AbschnittVerwenden getrennter Sender- und Empfänger-Transforms weiter unten zeigt, wie dername-Wert in einem Worker verwendet werden könnte.

Beachten Sie, dass der Transform zu jedem Zeitpunkt hinzugefügt werden kann, jedoch durch das sofortige Hinzufügen nach Aufruf vonaddTrack() der Transform den ersten kodierten Frame erhält, der gesendet wird.

Hinzufügen eines Transforms für eingehende Frames

Ein im Worker laufender Transformator wird in die eingehende WebRTC-Pipeline eingefügt, indem seine entsprechendeRTCRtpScriptTransform demRTCRtpReceiver.transform für eine eingehende Spur zugewiesen wird.

Dieses Beispiel zeigt, wie Sie einen Transform hinzufügen, um einen eingehenden Stream zu modifizieren.Der Code geht davon aus, dass eineRTCPeerConnection namenspeerConnection vorhanden ist, die bereits mit einem entfernten Peer verbunden ist.

Zuerst fügen wir einentrack-Ereignis-Handler derRTCPeerConnection hinzu, um das Ereignis zu erfassen, wenn der Peer beginnt, eine neue Spur zu empfangen.Innerhalb des Handlers erstellen wir einRTCRtpScriptTransform und fügen esevent.receiver.transform hinzu (event.receiver ist einRTCRtpReceiver).Wie im vorherigen Abschnitt nimmt der Konstruktor ein Objekt mit einername-Eigenschaft auf, verwendet jedoch hierreceiverTransform als Wert, um dem Worker mitzuteilen, dass Frames eingehen.

js
peerConnection.ontrack = (event) => {  const worker = new Worker("worker.js");  event.receiver.transform = new RTCRtpScriptTransform(worker, {    name: "receiverTransform",  });  receivedVideo.srcObject = event.streams[0];};

Beachten Sie erneut, dass die Transformstream jederzeit hinzugefügt werden kann.Durch das Hinzufügen imtrack-Ereignishandler wird jedoch sichergestellt, dass der Transformstream den ersten kodierten Frame für die Spur erhält.

Worker-Implementierung

Das Workerskript muss einen Handler für dasrtctransform-Ereignis implementieren, indem einePipe-Kette erstellt wird, die denevent.transformer.readable (ReadableStream)-Stream durch einenTransformStream zumevent.transformer.writable (WritableStream)-Stream leitet.

Ein Worker könnte sowohl eingehende als auch ausgehende kodierte Frames transformieren oder beide Szenarien unterstützen, und der Transform könnte fest codiert sein oder zur Laufzeit unter Verwendung von Informationen aus der Webanwendung konfiguriert werden.

Grundlegende WebRTC Encoded Transform

Das folgende Beispiel zeigt einen grundlegenden WebRTC Encoded Transform, der alle Bits in eingereihten Frames negiert.Es verwendet oder benötigt keine vom Hauptthread übergebenen Optionen, da derselbe Algorithmus in der Sender-Pipeline verwendet werden kann, um die Bits zu negieren, und in der Empfänger-Pipeline, um sie wiederherzustellen.

Der Code implementiert einen Ereignishandler für dasrtctransform-Ereignis.Dieser erstellt einenTransformStream, leitet dann durch ihn unter Verwendung vonReadableStream.pipeThrough(), und leitet schließlich zuevent.transformer.writable unter Verwendung vonReadableStream.pipeTo() weiter.

js
addEventListener("rtctransform", (event) => {  const transform = new TransformStream({    start() {}, // Called on startup.    flush() {}, // Called when the stream is about to be closed.    async transform(encodedFrame, controller) {      // Reconstruct the original frame.      const view = new DataView(encodedFrame.data);      // Construct a new buffer      const newData = new ArrayBuffer(encodedFrame.data.byteLength);      const newView = new DataView(newData);      // Negate all bits in the incoming frame      for (let i = 0; i < encodedFrame.data.byteLength; ++i) {        newView.setInt8(i, ~view.getInt8(i));      }      encodedFrame.data = newData;      controller.enqueue(encodedFrame);    },  });  event.transformer.readable    .pipeThrough(transform)    .pipeTo(event.transformer.writable);});

Die Implementierung des WebRTC Encoded Transform ist ähnlich wie ein "generischer"TransformStream, jedoch mit einigen wichtigen Unterschieden.Wie der generische Stream nimmt seinKonstruktor ein Objekt, das eineoptionalestart()-Methode definiert, die bei der Konstruktion aufgerufen wird, eineflush()-Methode, die aufgerufen wird, wenn der Stream geschlossen wird, und einetransform()-Methode, die jedes Mal aufgerufen wird, wenn ein Chunk verarbeitet werden muss.Im Gegensatz zum generischen Konstruktor werden alle im Konstruktorobjekt übergebenenwritableStrategy- oderreadableStrategy-Eigenschaften ignoriert, und die Warteschlangenstrategie wird vollständig vom Benutzer-Agenten verwaltet.

Dietransform()-Methode unterscheidet sich auch insoweit, als dass sie entweder einenRTCEncodedVideoFrame oderRTCEncodedAudioFrame anstelle eines generischen "Chunks" erhält.Der hier gezeigte Code für die Methode ist nicht besonders bemerkenswert, außer dass er zeigt, wie man den Frame in eine Form konvertiert, in der er modifiziert und anschließend im Stream eingereiht werden kann.

Verwendung getrennter Sender- und Empfänger-Transforms

Das vorherige Beispiel funktioniert, wenn die Transform-Funktion beim Senden und Empfangen dieselbe ist, in vielen Fällen werden die Algorithmen jedoch unterschiedlich sein.Sie könnten separate Worker-Skripte für den Sender und den Empfänger verwenden oder beide Fälle in einem Worker wie unten gezeigt behandeln.

Wenn der Worker sowohl für den Sender als auch den Empfänger verwendet wird, muss er wissen, ob der aktuelle kodierte Frame vom Codec ausgehend oder vom Paketierer eingehend ist.Diese Information kann mit der zweiten Option imRTCRtpScriptTransform-Konstruktor angegeben werden.Zum Beispiel können wir einen separatenRTCRtpScriptTransform für den Sender und den Empfänger definieren, indem wir denselben Worker und ein Optionsobjekt mit der Eigenschaftname übergeben, das angibt, ob der Transform im Sender oder Empfänger verwendet wird (wie in den vorherigen Abschnitten oben gezeigt).Diese Information ist dann im Worker inevent.transformer.options verfügbar.

In diesem Beispiel implementieren wir denonrtctransform-Ereignishandler auf dem globalen dedizierten Worker-Umfangsobjekt.Der Wert dername-Eigenschaft wird verwendet, um zu bestimmen, welcherTransformStream konstruiert werden soll (die tatsächlichen Konstruktormethoden werden nicht gezeigt).

js
// Code to instantiate transform and attach them to sender/receiver pipelines.onrtctransform = (event) => {  let transform;  if (event.transformer.options.name === "senderTransform")    transform = createSenderTransform(); // returns a TransformStream  else if (event.transformer.options.name === "receiverTransform")    transform = createReceiverTransform(); // returns a TransformStream  else return;  event.transformer.readable    .pipeThrough(transform)    .pipeTo(event.transformer.writable);};

Beachten Sie, dass der Code zum Erstellen der Pipe-Kette derselbe wie im vorherigen Beispiel ist.

Laufzeitkommunikation mit dem Transform

DerRTCRtpScriptTransform-Konstruktor erlaubt es Ihnen, Optionen und Transferobjekte an den Worker zu übergeben.Im vorherigen Beispiel haben wir statische Informationen übergeben, aber manchmal möchte man den Transform-Algorithmus im Worker zur Laufzeit ändern oder Informationen vom Worker zurück erhalten.Zum Beispiel könnte ein WebRTC-Konferenzanruf, der Verschlüsselung unterstützt, einen neuen Schlüssel dem Algorithmus hinzufügen müssen, der vom Transform verwendet wird.

Obwohl es möglich ist, Informationen zwischen dem Worker, der den Transform-Code ausführt, und dem Hauptthread mitWorker.postMessage() zu teilen, ist es im Allgemeinen einfacher, einenMessageChannel als eineRTCRtpScriptTransform-Konstruktor-Option zu teilen, da dann der Kanal-Kontext direkt in denevent.transformer.options verfügbar ist, wenn Sie einen neuen kodierten Frame behandeln.

Der folgende Code erstellt einenMessageChannel undüberträgt dessen zweiten Port an den Worker.Der Hauptthread und der Transform können anschließend über den ersten und zweiten Port kommunizieren.

js
// Create a worker containing a TransformStreamconst worker = new Worker("worker.js");// Create a channel// Pass channel.port2 to the transform as a constructor option// and also transfer it to the workerconst channel = new MessageChannel();const transform = new RTCRtpScriptTransform(  worker,  { purpose: "encrypt", port: channel.port2 },  [channel.port2],);// Use the port1 to send a string.// (we can send and transfer basic types/objects).channel.port1.postMessage("A message for the worker");channel.port1.start();

Im Worker ist der Port alsevent.transformer.options.port verfügbar.Der folgende Code zeigt, wie man am Port auf dasmessage-Ereignis hört, um Nachrichten vom Hauptthread zu erhalten.Sie können den Port auch verwenden, um Nachrichten an den Hauptthread zu senden.

js
event.transformer.options.port.onmessage = (event) => {  // The message payload is in 'event.data';  console.log(event.data);};

Auslösen eines Schlüssel-Frames

Rohe Videos werden selten gesendet oder gespeichert, da sie viel Platz und Bandbreite verbrauchen, um jedes Frame als vollständiges Bild darzustellen.Stattdessen generieren Codecs periodisch einen "Schlüssel-Frame", der genügend Informationen enthält, um ein vollständiges Bild zu konstruieren, und senden zwischen den Schlüssel-Frames "Delta-Frames", die nur die Änderungen seit dem letzten Delta-Frame einschließen.Obwohl dies weitaus effizienter ist als das Senden von rohem Video, bedeutet es, dass, um das Bild, das mit einem bestimmten Delta-Frame assoziiert ist, anzuzeigen, Sie den letzten Schlüssel-Frame und alle nachfolgenden Delta-Frames benötigen.

Dies kann eine Verzögerung für neue Benutzer verursachen, die einer WebRTC-Konferenzanwendung beitreten, da sie das Video nicht anzeigen können, bis sie ihren ersten Schlüssel-Frame erhalten haben.Ähnlich wäre es, wenn ein kodierter Transform zur Verschlüsselung von Frames verwendet wurde, der Empfänger nicht in der Lage, das Video anzuzeigen, bis er den ersten Schlüssel-Frame erhält, der mit seinem Schlüssel verschlüsselt wurde.

Um sicherzustellen, dass ein neuer Schlüssel-Frame so früh wie möglich bei Bedarf gesendet werden kann, hat dasRTCRtpScriptTransformer-Objekt inevent.transformer zwei Methoden:RTCRtpScriptTransformer.generateKeyFrame(), die den Codec dazu bringt, einen Schlüssel-Frame zu erzeugen, undRTCRTpScriptTransformer.sendKeyFrameRequest(), die ein Empfänger verwenden kann, um einen Schlüssel-Frame vom Sender anzufordern.

Das folgende Beispiel zeigt, wie der Hauptthread einen Verschlüsselungsschlüssel an einen Sender-Transform übergeben und den Codec dazu veranlassen könnte, einen Schlüssel-Frame zu erzeugen.Beachten Sie, dass der Hauptthread keinen direkten Zugriff auf dasRTCRTpScriptTransformer-Objekt hat, sodass er den Schlüssel und die Einschränkungskennung ("rid") an den Worker weitergeben muss (das "rid" ist eine Stream-ID, die den Encoder angibt, der den Schlüssel-Frame erzeugen muss).Hier tun wir das mit einemMessageChannel, unter Verwendung dasselbe Muster wie in der vorherigen Abschnitt.Der Code geht davon aus, dass es bereits eine Peer-Verbindung gibt und dassvideoSender einRTCRTpSender ist.

js
const worker = new Worker("worker.js");const channel = new MessageChannel();videoSender.transform = new RTCRtpScriptTransform(  worker,  { name: "senderTransform", port: channel.port2 },  [channel.port2],);// Post rid and new key to the senderchannel.port1.start();channel.port1.postMessage({  rid: "1",  key: "93ae0927a4f8e527f1gce6d10bc6ab6c",});

Derrtctransform-Ereignishandler im Worker erhält den Port und verwendet ihn, um aufmessage-Ereignisse vom Hauptthread zu hören.Wenn ein Ereignis empfangen wird, erhält es dasrid und denkey und ruft danngenerateKeyFrame() auf.

js
event.transformer.options.port.onmessage = (event) => {  const { rid, key } = event.data;  // key is used by the transformer to encrypt frames (not shown)  // Get codec to generate a new key frame using the rid  // Here 'rcEvent' is the rtctransform event.  rcEvent.transformer.generateKeyFrame(rid);};

Der Code für einen Empfänger, um einen neuen Schlüssel-Frame anzufordern, wäre fast identisch, außer dass "rid" nicht spezifiziert wird.Hier ist der Code nur für den Port-Nachrichtenhandler:

js
event.transformer.options.port.onmessage = (event) => {  const { key } = event.data;  // key is used by the transformer to decrypt frames (not shown)  // Request sender to emit a key frame.  transformer.sendKeyFrameRequest();};

Browser-Kompatibilität

Siehe auch

Help improve MDN

Learn how to contribute Diese Seite wurde automatisch aus dem Englischen übersetzt.

[8]ページ先頭

©2009-2025 Movatter.jp