RTCPeerConnection: Methode addTrack()

Die addTrack()-Methode der RTCPeerConnection-Schnittstelle fügt eine neue Media-Tracks zu der Menge von Tracks hinzu, die an den anderen Peer übertragen werden.

Hinweis: Das Hinzufügen eines Tracks zu einer Verbindung löst eine Neuverhandlung aus, indem ein negotiationneeded-Ereignis ausgelöst wird. Siehe Beginn der Verhandlung für Details.

Syntax

js
addTrack(track)
addTrack(track, stream1)
addTrack(track, stream1, stream2)
addTrack(track, stream1, stream2, /* …, */ streamN)

Parameter

track

Ein MediaStreamTrack-Objekt, das den Media-Track darstellt, der zur Peer-Verbindung hinzugefügt werden soll.

stream1, …, streamN Optional

Ein oder mehrere lokale MediaStream-Objekte, zu denen der Track hinzugefügt werden soll.

Der angegebene track muss nicht notwendigerweise bereits Teil eines der angegebenen streams sein. Vielmehr sind die streams eine Möglichkeit, Tracks auf der Empfangsseite der Verbindung zu gruppieren und sicherzustellen, dass sie synchronisiert werden. Alle Tracks, die auf derselben Seite der Verbindung zu demselben Stream hinzugefügt werden, befinden sich auf dem Remote-Ende im selben Stream.

Rückgabewert

Das RTCRtpSender-Objekt, welches zur Übertragung der Mediendaten verwendet wird.

Hinweis: Jeder RTCRtpSender ist mit einem RTCRtpReceiver gepaart, um einen RTCRtpTransceiver zu bilden. Der zugehörige Empfänger ist stummgeschaltet (was anzeigt, dass er keine Pakete liefern kann), bis und sofern nicht ein oder mehrere Streams von dem Remote-Peer zum Empfänger hinzugefügt werden.

Ausnahmen

InvalidAccessError DOMException

Wird ausgelöst, wenn der angegebene Track (oder alle seine zugrundeliegenden Streams) bereits Teil der RTCPeerConnection ist.

InvalidStateError DOMException

Wird ausgelöst, wenn die RTCPeerConnection geschlossen ist.

Nutzungsnotizen

Tracks zu mehreren Streams hinzufügen

Nach dem track-Parameter können Sie optional ein oder mehrere MediaStream-Objekte angeben, zu denen der Track hinzugefügt werden soll. Es werden nur Tracks von einem Peer zum anderen gesendet, nicht Streams. Da Streams spezifisch für jeden Peer sind, bedeutet das Angeben eines oder mehrerer Streams, dass der andere Peer einen entsprechenden Stream (oder Streams) automatisch am anderen Ende der Verbindung erstellt und dann den empfangenen Track zu diesen Streams hinzufügt.

Streamlose Tracks

Wenn keine Streams angegeben werden, ist der Track streamlos. Dies ist völlig akzeptabel, obwohl es dem Remote-Peer obliegt, zu entscheiden, in welchen Stream der Track eingefügt werden soll, falls überhaupt. Dies ist eine sehr gängige Weise, addTrack() zu verwenden, wenn viele Arten von einfachen Anwendungen erstellt werden, bei denen nur ein Stream benötigt wird. Zum Beispiel, wenn Sie dem Remote-Peer nur einen einzelnen Stream mit einem Audio- und einem Videotrack teilen, brauchen Sie sich nicht darum zu kümmern, welchen Track in welchem Stream verwaltet werden soll. Sie können also den Transceiver dies für Sie erledigen lassen.

Hier ist ein Beispiel, das eine Funktion zeigt, die getUserMedia() verwendet, um einen Stream von der Kamera und dem Mikrofon eines Benutzers zu erhalten und dann jeden Track aus dem Stream zur Peer-Verbindung hinzuzufügen, ohne für jeden Track einen Stream anzugeben:

js
async function openCall(pc) {
  const gumStream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true,
  });
  for (const track of gumStream.getTracks()) {
    pc.addTrack(track);
  }
}

Das Ergebnis ist, dass eine Reihe von Tracks an den Remote-Peer gesendet wird, ohne dass Stream-Zuordnungen vorliegen. Der Handler für das track-Ereignis auf dem Remote-Peer ist dafür verantwortlich, zu bestimmen, in welchen Stream jeder Track hinzugefügt wird, selbst wenn das bedeutet, sie alle zu demselben Stream hinzuzufügen. Der ontrack-Handler könnte so aussehen:

js
let inboundStream = null;

pc.ontrack = (ev) => {
  if (ev.streams && ev.streams[0]) {
    videoElem.srcObject = ev.streams[0];
  } else {
    if (!inboundStream) {
      inboundStream = new MediaStream();
      videoElem.srcObject = inboundStream;
    }
    inboundStream.addTrack(ev.track);
  }
};

Hier fügt der track-Ereignishandler den Track dem ersten vom Ereignis angegebenen Stream hinzu, falls ein Stream angegeben ist. Andernfalls wird beim ersten Aufruf von ontrack ein neuer Stream erstellt und dem Videoelement angefügt, und dann wird der Track dem neuen Stream hinzugefügt. Ab diesem Zeitpunkt werden neue Tracks zu diesem Stream hinzugefügt.

Sie könnten auch einfach für jeden empfangenen Track einen neuen Stream erstellen:

js
pc.ontrack = (ev) => {
  if (ev.streams && ev.streams[0]) {
    videoElem.srcObject = ev.streams[0];
  } else {
    let inboundStream = new MediaStream(ev.track);
    videoElem.srcObject = inboundStream;
  }
};

Zuordnung von Tracks zu bestimmten Streams

Durch die Angabe eines Streams und das zulassen von RTCPeerConnection, Streams für Sie zu erstellen, werden die Tracks' Zuordnungen zu Streams automatisch von der WebRTC-Infrastruktur für Sie verwaltet. Dies umfasst Dinge wie Änderungen der direction des Transceivers und Tracks, die mit removeTrack() gestoppt werden.

Betrachten Sie zum Beispiel diese Funktion, die eine Anwendung verwenden könnte, um das Streaming von Kamera- und Mikrofoneingaben eines Geräts über eine RTCPeerConnection zu einem Remote-Peer zu beginnen:

js
async function openCall(pc) {
  const gumStream = await navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true,
  });
  for (const track of gumStream.getTracks()) {
    pc.addTrack(track, gumStream);
  }
}

Der Remote-Peer könnte dann einen track-Ereignishandler verwenden, der so aussieht:

js
pc.ontrack = ({ streams: [stream] }) => (videoElem.srcObject = stream);

Dieser setzt den aktuellen Stream des Videoelements auf den, der den zur Verbindung hinzugefügten Track enthält.

Wiederverwendete Sender

Diese Methode gibt entweder einen neuen RTCRtpSender oder eine bestehende Instanz zur Wiederverwendung zurück. Eine RTCRtpSender-Instanz ist nur dann zur Wiederverwendung kompatibel, wenn sie die folgenden Kriterien erfüllt:

  • Es ist kein Track bereits mit dem Sender verknüpft.
  • Der mit dem Sender verknüpfte RTCRtpTransceiver verfügt über einen RTCRtpReceiver, dessen track-Eigenschaft eine MediaStreamTrack angibt, deren kind dem kind des beim Aufruf von RTCPeerConnection.addTrack() angegebenen track-Parameters entspricht. Dies stellt sicher, dass ein Transceiver nur Audio oder Video und niemals beides behandelt.
  • Die RTCRtpTransceiver.currentDirection-Eigenschaft ist nicht "stopped".
  • Der RTCRtpSender, der in Betracht gezogen wird, wurde nie verwendet, um Daten zu senden. Wenn die currentDirection des Transceivers jemals "sendrecv" oder "sendonly" war, kann der Sender nicht wiederverwendet werden.

Wenn all diese Kriterien erfüllt sind, wird der Sender wiederverwendet, was dazu führt, dass diese Änderungen am bestehenden RTCRtpSender und seinem RTCRtpTransceiver erfolgen:

  • Der RTCRtpSender's track wird auf den angegebenen Track gesetzt.
  • Die Menge der dem Sender zugeordneten Streams wird auf die Liste der Streams gesetzt, die in diese Methode übergeben werden, stream....
  • Der zugehörige RTCRtpTransceiver hat seine currentDirection aktualisiert, um anzuzeigen, dass er sendet; wenn sein aktueller Wert "recvonly" ist, wird er zu "sendrecv" und wenn sein aktueller Wert "inactive" ist, wird er zu "sendonly".

Neue Sender

Wenn kein vorhandener und wiederverwendbarer Sender existiert, wird ein neuer erstellt. Dies führt ebenfalls zur Erstellung der zugehörigen Objekte, die existieren müssen. Der Prozess zur Erstellung eines neuen Senders führt zu diesen Änderungen:

  • Der neue RTCRtpSender wird mit dem angegebenen Track und der Menge an Stream(s) erstellt.
  • Ein neuer RTCRtpReceiver wird mit einem neuen MediaStreamTrack als seiner track-Eigenschaft erstellt (nicht der beim Aufruf von addTrack() angegebene Track). Dieser Track's kind wird so eingestellt, dass er dem kind des als Eingabeparameter bereitgestellten Tracks entspricht.
  • Ein neuer RTCRtpTransceiver wird erstellt und mit dem neuen Sender und Empfänger verknüpft.
  • Die direction (RTCRtpTransceiver/direction) des neuen Transceivers wird auf "sendrecv" gesetzt.
  • Der neue Transceiver wird zur Menge der Transceiver der RTCPeerConnection hinzugefügt.

Beispiele

Dieses Beispiel stammt aus dem im Artikel Signalisierung und Videotelefonie vorgestellten Code und seinem dazugehörigen Beispielcode. Es stammt aus der Methode handleVideoOfferMsg(), die aufgerufen wird, wenn eine Angebotsnachricht vom Remote-Peer empfangen wird.

js
const mediaConstraints = {
  audio: true, // We want an audio track
  video: true, // And we want a video track
};

const desc = new RTCSessionDescription(sdp);

pc.setRemoteDescription(desc)
  .then(() => navigator.mediaDevices.getUserMedia(mediaConstraints))
  .then((stream) => {
    previewElement.srcObject = stream;

    stream.getTracks().forEach((track) => pc.addTrack(track, stream));
  });

Dieser Code nimmt SDP, die vom Remote-Peer empfangen wurde, und erstellt eine neue RTCSessionDescription, die an setRemoteDescription() übergeben werden soll. Sobald dies erfolgreich ist, verwendet er MediaDevices.getUserMedia(), um Zugriff auf die lokale Webcam und das Mikrofon zu erhalten.

Wenn das erfolgreich ist, wird der resultierende Stream als Quelle für ein <video>-Element zugewiesen, das durch die Variable previewElement referenziert wird.

Der letzte Schritt besteht darin, das lokale Video über die Peer-Verbindung an den Anrufer zu senden. Dies geschieht, indem jeder Track im Stream hinzugefügt wird, indem über die Liste iteriert wird, die von MediaStream.getTracks() zurückgegeben wird, und sie an addTrack() zusammen mit dem stream, dessen Bestandteil sie sind, übergeben wird.

Spezifikationen

Specification
WebRTC: Real-Time Communication in Browsers
# dom-rtcpeerconnection-addtrack

Browser-Kompatibilität

BCD tables only load in the browser

Siehe auch