--- title: RTCPeerConnection.addTrack() slug: Web/API/RTCPeerConnection/addTrack translation_of: Web/API/RTCPeerConnection/addTrack ---
{{domxref("RTCPeerConnection")}} 对象的addTrack()
方法将一个新的媒体音轨添加到一组音轨中,这些音轨将被传输给另一个对等点。
注意:通过触发一个{{event("negotiationneeded")}}事件,向连接添加一个跟踪将触发重新协商。详情请参见{{SectionOnPage("/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling", "Starting negotiation")}}。
rtpSender = rtcPeerConnection.addTrack(track, stream...);
track
stream...
{{optional_inline}}指定的track
不一定已经是任何指定streams
的一部分。相反,streams
只是在连接的接收端将轨迹分组在一起的一种方式,以确保它们是同步的。在连接的本地端添加到相同流的任何轨道都将位于远程端相同的流上。
将用于传输媒体数据的{{domxref("RTCRtpSender")}}对象。
注意:每个RTCRtpSender
都与{{domxref("RTCRtpReceiver")}}配对,组成{{domxref("RTCRtpTransceiver")}}。关联的接收方处于静默状态(指示它不能发送数据包),直到或除非远程对等方向接收方添加一个或多个流。
InvalidAccessError
InvalidStateError
在track
参数之后,您可以选择指定一个或多个{{domxref("MediaStream")}}对象来添加track
。只有轨道从一个点发送到另一个点,而不是一个媒体流。由于流是特定于每个对等点的,因此指定一个或多个流意味着另一个对等点将在连接的另一端自动创建一个相应的流(或多个流),然后自动将接收到的轨道添加到这些流中。
如果没有指定媒体流,则轨道是无流的。这是完全可以接受的,尽管要由远程对等点决定将轨道插入到哪个流(如果有的话)。当构建一个多类型的简单应用只有一个媒体流时,使用addTrack()
是一个非常常用的办法。例如,如果您与远程对等点共享的只是带有音频轨道和视频轨道的单个流,那么您不需要管理流中的哪个轨道,所以您不妨让transceriver为您处理它。
下面是一个使用{{domxref("MediaDevices.getUserMedia", "getUserMedia()")}}从用户的摄像机和麦克风获取一个流,然后将流中的每条轨迹添加到对等连接,而不为每条轨迹指定一个流:
async openCall(pc) { const gumStream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); for (const track of gumStream.getTracks()) { pc.addTrack(track); } }
结果是一组没有流关联的跟踪被发送到远程对等点。远程对等点上的{{event("track")}}事件的处理程序将负责决定将每个跟踪添加到哪个流中,即使这意味着只是将它们全部添加到同一个流中。{{domxref("RTCPeerConnection.ontrack", "ontrack")}}方法如下:
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); } }
在这里,如果指定了流,则track
事件处理程序将跟踪添加到事件指定的第一个流。否则,在第一次调用ontrack
时,将创建一个新流并附加到视频元素,然后将音轨添加到新流中。从那时起,新的堆track被添加到这个流中。
你也可以为每个接收到的track创建一个新的流:
pc.ontrack = ev => { if (ev.streams && ev.streams[0]) { videoElem.srcObject = ev.streams[0]; } else { let inboundStream = new MediaStream(ev.track); videoElem.srcObject = inboundStream; } }
通过指定一个流并允许{{domxref("RTCPeerConnection")}}为您创建流,流的跟踪关联将由WebRTC基础设施自动为您管理。这包括对收发器的{{domxref("RTCRtpTransceiver.direction","direction")}} 的更改和被停止使用{{domxref("RTCPeerConnection.removeTrack","removeTrack()")}}。
例如,考虑应用程序可能使用的这个函数,通过{{domxref("RTCPeerConnection")}}将设备的摄像头和麦克风输入流化为远程对等点:
async openCall(pc) { const gumStream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); for (const track of gumStream.getTracks()) { pc.addTrack(track, gumStream); } }
远程对等点然后可以使用一个看起来像这样的{{event("track")}}事件处理程序:
pc.ontrack = ({streams: [stream]} => videoElem.srcObject = stream;
这将把视频元素的当前流设置为包含已添加到连接中的音轨的流。
这种方法可以返回一个新的RTCRtpSender
,或者在非常特殊的情况下,返回一个尚未用于传输数据的现有的兼容发送方。兼容的可重用RTCRtpSender
实例满足以下条件:
RTCPeerConnection.addTrack()
时指定的track参数的kind相同。这确保了收发器只能处理音频或视频,而不能同时处理二者。RTCRtpTransceiver
的{{domxref("RTCRtpTransceiver.stopped", "stopped")}}属性为false
。RTCRtpSender
从未被用于发送数据。如果收发器的{{domxref("RTCRtpTransceiver.currentDirection", "currentDirection")}} 曾经是“sendrecv
”或“sendonly
”,发送方不能被重用。如果所有这些条件都满足,发送方会被重用,这将导致现有RTCRtpSender
和它的RTCRtpTransceiver
发生这些变化:
RTCRtpSender
的{{domxref("RTCRtpSender.track", "track")}}被设置为指定的track。stream…
recvonly
”,它就变成“sendrecv
”,如果它的当前值是“inactive
”,它就变成“sendonly
”。如果现有的发送方不存在可重用,则创建一个新的发送方。这也会导致必须存在的关联对象的创建。创建新发送方的过程会导致以下更改:
track
和streams
集创建新的RTCRtpSender
。addTrack()
时指定作为参数的track)。这跟踪的{{domxref("MediaStreamTrack.kind", "kind")}}设置为与作为输入参数提供的音轨类型匹配。transceiver
的{{domxref("RTCRtpTransceiver.direction", "direction")}}设置为"sendrecv
"。transceiver
被添加到RTCPeerConnection的收发器集合中。这个例子是从文章中给出的Signaling and video calling及其相应的示例代码中提取的。它来自那里的handleVideoOfferMsg()
方法,该方法在从远程对等方接收到报价消息时被调用。
var mediaConstraints = { audio: true, // We want an audio track video: true // ...and we want a video track }; var desc = new RTCSessionDescription(sdp); pc.setRemoteDescription(desc).then(function () { return navigator.mediaDevices.getUserMedia(mediaConstraints); }) .then(function(stream) { previewElement.srcObject = stream; stream.getTracks().forEach(track => pc.addTrack(track, stream)); })
这段代码获取从远程对等方接收到的SDP,并构造一个新的{{domxref("RTCSessionDescription")}}传递到{{domxref("RTCPeerConnection.setRemoteDescription", "setRemoteDescription()")}}。成功之后,它使用{{domxref(" mediadevic. getusermedia()")}}获得对本地摄像头和麦克风的访问。
如果成功,结果流将被分配为变量previewElement
引用的{{HTMLElement("video")}}元素的源。
最后一步是开始通过对等连接向调用者发送本地视频。通过遍历{{domxref("MediaStream.getTracks()")}}返回的列表,并将它们与作为其组件的流一起传递给addTrack()
,从而在流中添加每条跟踪。
Specification | Status | Comment |
---|---|---|
{{ SpecName('WebRTC 1.0', '#dom-rtcpeerconnection-addtrack', 'RTCPeerConnection.addTrack()') }} | {{ Spec2('WebRTC 1.0') }} | Initial specification. |
{{Compat("api.RTCPeerConnection.addTrack")}}