Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

Experiment: Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.

Verwendung von WebRTC Encoded Transforms

Limited availability

WebRTC Encoded Transforms bieten einen Mechanismus zur Integration einer leistungsstarkenStream API zur Modifizierung von kodierten Video- und Audiobildern in eingehenden und ausgehenden WebRTC-Pipelines.Dies ermöglicht Anwendungsfälle wie die End-to-End-Verschlüsselung von kodierten Bildern durch Code von Drittanbietern.

Die API definiert sowohl Objekte für den Hauptthread als auch für den Worker-Bereich.Die Schnittstelle im Hauptthread ist eine Instanz vonRTCRtpScriptTransform, die bei der Erstellung denWorker angibt, der den Transformationscode implementieren soll.Die Transformation, die im Worker ausgeführt wird, wird in die eingehende oder ausgehende WebRTC-Pipeline eingefügt, indemRTCRtpScriptTransform zuRTCRtpReceiver.transform oderRTCRtpSender.transform hinzugefügt wird.

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

DerRTCRtpScriptTransformer wird dem Code alstransformer-Eigenschaft desrtctransform-Ereignisses zur Verfügung gestellt, das im globalen Bereich des Workers ausgelöst wird, wann immer ein kodiertes Bild zur Verarbeitung eingereiht wird (und anfangs beim Erstellen des entsprechendenRTCRtpScriptTransform).Der Worker-Code muss einen Handler für das Ereignis implementieren, der kodierte Bilder vontransformer.readable liest, sie nach Bedarf modifiziert und in derselben Reihenfolge ohne Duplikate zutransformer.writable schreibt.

Während die Schnittstelle keine weiteren Einschränkungen zur Implementierung macht, besteht eine natürliche Art, die Bilder zu transformieren, darin, einePipelin-Kette zu erstellen, die Bilder, die auf denevent.transformer.readable-Stream eingereiht werden, durch einenTransformStream zumevent.transformer.writable-Stream sendet.Wir können die Eigenschaftevent.transformer.options verwenden, um beliebigen Transformationscode zu konfigurieren, der davon abhängt, ob der Transformator eingehende Bilder vom Paketierer oder ausgehende Bilder von einem Codec einreiht.

Die SchnittstelleRTCRtpScriptTransformer bietet auch Methoden, die beim Senden von kodierten Videos verwendet werden können, um den Codec dazu zu bringen, ein "Schlüssel"-Bild zu erzeugen, und beim Empfangen von Videos, um anzufordern, dass ein neues Schlüsselbild gesendet wird.Diese können nützlich sein, um einem Empfänger zu ermöglichen, das Video schneller zu sehen, wenn er (zum Beispiel) einer Telefonkonferenz beitritt, während Delta-Bilder gesendet werden.

Die folgenden Beispiele bieten konkretere Beispiele dafür, wie das Framework mithilfe einer aufTransformStream basierenden Implementierung zu verwenden ist.

Testen, ob kodierte Transformations unterstützt werden

Testen Sie, obkodierte Transformationen 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 einer Transformation für ausgehende Bilder

Eine in einem Worker ausgeführte Transformation wird in die ausgehende WebRTC-Pipeline eingefügt, indem ihr entsprechendesRTCRtpScriptTransform demRTCRtpSender.transform für eine ausgehende Spur zugewiesen wird.

Dieses Beispiel zeigt, wie Sie Video aus der Webcam eines Benutzers über WebRTC streamen und eine WebRTC-kodierte Transformation hinzufügen, um die ausgehenden Streams zu modifizieren.Der Code geht davon aus, dass es eineRTCPeerConnection namenspeerConnection gibt, die bereits mit einem entfernten Peer verbunden ist.

Zuerst erhalten wir einMediaStreamTrack, indem wirgetUserMedia() verwenden, um einen VideoMediaStream von einem Mediengerät abzurufen, und dann die MethodeMediaStream.getTracks(), um die ersteMediaStreamTrack im Stream zu erhalten.

Die Spur wird der Peer-Verbindung mitaddTrack() hinzugefügt, wodurch sie mit dem entfernten Peer zu streamen beginnt.Die MethodeaddTrack() gibt denRTCRtpSender zurück, der für das Senden der Spur verwendet wird.

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, das ein Worker-Skript, das die Transformation definiert, und ein optionales Objekt nimmt, 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 diese Transformation zum ausgehenden Stream hinzugefügt wird).Wir fügen die Transformation zur ausgehenden Pipeline hinzu, indem wir sie der EigenschaftRTCRtpSender.transform zuweisen.

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

Der AbschnittVerwendung getrennter Sender- und Empfängertransformationen unten zeigt, wie dername im Worker verwendet werden könnte.

Beachten Sie, dass Sie die Transformation jederzeit hinzufügen können, aber indem Sie sie sofort nach dem Aufruf vonaddTrack() hinzufügen, erhält die Transformation das erste kodierte Bild, das gesendet wird.

Hinzufügen einer Transformation für eingehende Bilder

Eine in einem Worker ausgeführte Transformation wird in die eingehende WebRTC-Pipeline eingefügt, indem ihr entsprechendesRTCRtpScriptTransform demRTCRtpReceiver.transform für eine eingehende Spur zugewiesen wird.

Dieses Beispiel zeigt, wie man eine Transformation hinzufügt, um einen eingehenden Stream zu modifizieren.Der Code geht davon aus, dass es eineRTCPeerConnection namenspeerConnection gibt, die bereits mit einem entfernten Peer verbunden ist.

Zuerst fügen wir einentrack-Event-Handler zu einerRTCPeerConnection hinzu, um das Ereignis abzufangen, wenn der Peer beginnt, eine neue Spur zu empfangen.Innerhalb des Handlers konstruieren wir einRTCRtpScriptTransform und fügen es zuevent.receiver.transform hinzu (event.receiver ist einRTCRtpReceiver).Wie im vorherigen Abschnitt nimmt der Konstruktor ein Objekt mit dername-Eigenschaft auf, aber hier verwenden wirreceiverTransform als Wert, um dem Worker mitzuteilen, dass Bilder eingehen.

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

Beachten Sie erneut, dass Sie den Transformationsstrom jederzeit hinzufügen können.Indem Sie ihn jedoch imtrack-Event-Handler hinzufügen, wird sichergestellt, dass der Transformationsstrom das erste kodierte Bild für die Spur erhält.

Implementierung des Workers

Das Worker-Skript muss einen Handler für dasrtctransform-Ereignis implementieren, der einePipeline-Kette erstellt, die denevent.transformer.readable (ReadableStream)-Stream durch einenTransformStream zumevent.transformer.writable (WritableStream)-Stream leitet.

Ein Worker könnte das Transformieren eingehender oder ausgehender kodierter Bilder oder beides unterstützen, und die Transformation könnte hart codiert oder zur Laufzeit konfiguriert werden, indem Informationen von der Webanwendung übergeben werden.

Grundlegende WebRTC kodierte Transformation

Das untenstehende Beispiel zeigt eine grundlegende WebRTC-kodierte Transformation, die alle Bits in eingereihten Bildern negiert.Es werden keine Optionen verwendet oder benötigt, die vom Haupt-Thread übergeben werden, 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 Event-Handler für dasrtctransform-Ereignis.Dieser konstruiert einenTransformStream, leitet dann durch ihn unter Verwendung vonReadableStream.pipeThrough(), und leitet schließlich zuevent.transformer.writable unter Verwendung vonReadableStream.pipeTo().

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 der WebRTC-kodierten Transformation ist ähnlich wie ein "generischer"TransformStream, jedoch mit einigen wichtigen Unterschieden.Wie der generische Stream nimmt seinKonstruktor ein Objekt auf, das eineoptionalestart()-Methode definiert, die beim Konstruieren aufgerufen wird, eineflush()-Methode, die aufgerufen wird, wenn der Stream geschlossen werden soll, und einetransform()-Methode, die jedes Mal aufgerufen wird, wenn es einen zu verarbeitenden Chunk gibt.Anders als beim generischen Konstruktor werden allewritableStrategy- oderreadableStrategy-Eigenschaften, die im Konstruktor-Objekt übergeben werden, ignoriert, und die Wartestrategie wird vollständig vom Benutzeragenten verwaltet.

Dietransform()-Methode unterscheidet sich auch darin, dass ihr entweder einRTCEncodedVideoFrame oderRTCEncodedAudioFrame übergeben wird, anstatt eines generischen "Chunks".Der hier gezeigte Code für die Methode ist nicht bemerkenswert, abgesehen davon, dass er zeigt, wie man das Bild in eine Form umwandelt, in der man es modifizieren und danach im Stream einreihen kann.

Verwendung getrennter Sender- und Empfängertransformationen

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

Wenn der Worker sowohl für Sender als auch Empfänger verwendet wird, muss er wissen, ob das aktuelle kodierte Bild aus einem Codec ausgehend oder von einem 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 Empfänger definieren, dabei denselben Worker verwenden, und ein Optionen-Objekt mit der Eigenschaftname übergeben, das angibt, ob die Transformation im Sender oder Empfänger verwendet wird (wie in den vorherigen Abschnitten oben gezeigt).Die Informationen sind dann im Worker inevent.transformer.options verfügbar.

In diesem Beispiel implementieren wir denonrtctransform-Event-Handler im globalen dedizierten Worker-Bereichsobjekt.Der Wert dername-Eigenschaft wird verwendet, um zu bestimmen, welchenTransformStream zu konstruieren ist (die tatsächlichen Konstruktor-Methoden 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 Pipeline-Kette derselbe ist wie im vorherigen Beispiel.

Laufzeitkommunikation mit der Transformation

DerRTCRtpScriptTransform-Konstruktor ermöglicht es, Optionen und Übertragungsobjekte an den Worker zu übergeben.Im vorherigen Beispiel haben wir statische Informationen übergeben, aber manchmal möchten Sie den Transformationsalgorithmus im Worker zur Laufzeit ändern oder Informationen vom Worker zurückerhalten.Zum Beispiel könnte eine WebRTC-Telefonkonferenz die Verschlüsselung unterstützt, einen neuen Schlüssel zum Algorithmus hinzufügen müssen, der von der Transformation verwendet wird.

Obwohl es möglich ist, Informationen zwischen dem Worker, der den Transformations-Code ausführt, und dem Haupt-Thread mitWorker.postMessage() zu teilen, ist es im Allgemeinen einfacher, einenMessageChannel als eine Option imRTCRtpScriptTransform-Konstruktor zu teilen, da der Kanal-Kontext dann direkt inevent.transformer.options verfügbar ist, wenn Sie ein neues kodiertes Bild verarbeiten.

Der Code unten erstellt einenMessageChannel undüberträgt seinen zweiten Port an den Worker.Der Haupt-Thread und die Transformation können anschließend über die ersten und zweiten Ports miteinander 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 Code unten zeigt, wie Sie auf dasmessage-Ereignis des Ports lauschen, um Nachrichten vom Haupt-Thread zu erhalten.Sie können den Port auch verwenden, um Nachrichten zurück an den Haupt-Thread zu senden.

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

Auslösen eines Schlüsselbildes

Rohvideo wird selten gesendet oder gespeichert, da es viel Platz und Bandbreite benötigt, um jedes Bild als vollständiges Bild darzustellen.Stattdessen erzeugen Codecs periodisch ein "Schlüsselbild", das genügend Informationen enthält, um ein vollständiges Bild zu konstruieren, und senden zwischen den Schlüsselbildern "Delta-Bilder", die nur die Änderungen seit dem letzten Delta-Bild enthalten.Obwohl dies viel effizienter ist als das Senden von Rohvideo, bedeutet es, dass zum Anzeigen des mit einem bestimmten Delta-Bild verbundenen Bildes das letzte Schlüsselbild und alle anschließenden Delta-Bilder benötigt werden.

Dies kann zu einer Verzögerung für neue Benutzer führen, die einer WebRTC-Konferenzanwendung beitreten, da sie das Video nicht anzeigen können, bis sie ihr erstes Schlüsselbild erhalten haben.Ähnlich würden die Empfänger bei Verwendung einer kodierten Transformation zur Verschlüsselung von Bildern das Video erst anzeigen können, wenn sie das erste mit ihrem Schlüssel verschlüsselte Schlüsselbild erhalten haben.

Um sicherzustellen, dass bei Bedarf so schnell wie möglich ein neues Schlüsselbild gesendet werden kann, verfügt dasRTCRtpScriptTransformer-Objekt inevent.transformer über zwei Methoden:RTCRtpScriptTransformer.generateKeyFrame(), die den Codec dazu veranlasst, ein Schlüsselbild zu erzeugen, undRTCRtpScriptTransformer.sendKeyFrameRequest(), die ein Empfänger verwenden kann, um ein Schlüsselbild vom Sender anzufordern.

Das Beispiel unten zeigt, wie der Haupt-Thread einen Verschlüsselungsschlüssel an eine Sender-Transformation übergibt und den Codec dazu veranlasst, ein Schlüsselbild zu erzeugen.Beachten Sie, dass der Haupt-Thread keinen direkten Zugriff auf dasRTCRtpScriptTransformer-Objekt hat, daher muss er den Schlüssel und die Beschränkungskennzeichnung ("rid") an den Worker übergeben (die "rid" ist eine Stream-ID, die den Encoder angibt, der das Schlüsselbild generieren muss).Hier tun wir das mit einemMessageChannel, wobei dasselbe Muster wie in den vorherigen Abschnitten verwendet wird.Der Code geht davon aus, dass bereits eine Peer-Verbindung besteht undvideoSender 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-Event-Handler im Worker erhält den Port und verwendet ihn, um aufmessage-Ereignisse vom Haupt-Thread zu hören.Wenn ein Ereignis empfangen wird, erhält es dierid undkey 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 ein neues Schlüsselbild anzufordern, wäre fast identisch, mit Ausnahme dessen, dass "rid" nicht angegeben wird.Hier ist der Code nur für den Port-Message-Handler:

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

MDN-Feedback-Box

Diese Seite wurde automatisch aus dem Englischen übersetzt.


[8]ページ先頭

©2009-2025 Movatter.jp