RTCPeerConnection: phương thức addTrack()
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020.
Phương thức addTrack() của giao diện RTCPeerConnection thêm một media track mới vào tập các track sẽ được truyền tới máy ngang hàng còn lại.
Note:
Việc thêm một track vào kết nối sẽ kích hoạt thương lượng lại bằng cách gửi sự kiện negotiationneeded.
Xem Bắt đầu thương lượng để biết chi tiết.
Cú pháp
addTrack(track)
addTrack(track, stream1)
addTrack(track, stream1, stream2)
addTrack(track, stream1, stream2, /* …, */ streamN)
Tham số
track-
Một đối tượng
MediaStreamTrackbiểu diễn media track cần thêm vào kết nối ngang hàng. stream1, …,streamNOptional-
Một hoặc nhiều đối tượng
MediaStreamcục bộ mà track nên được thêm vào.
Track được chỉ định không nhất thiết đã phải là một phần của bất kỳ stream nào được chỉ định.
Thay vào đó, các stream là cách để nhóm các track ở đầu nhận của kết nối, bảo đảm chúng được đồng bộ hóa.
Bất kỳ track nào được thêm vào cùng một stream ở đầu cục bộ của kết nối sẽ nằm trong cùng một stream ở đầu từ xa.
Giá trị trả về
Đối tượng RTCRtpSender sẽ được dùng để truyền dữ liệu media.
Note:
Mỗi RTCRtpSender được ghép với một RTCRtpReceiver để tạo thành một RTCRtpTransceiver.
Bộ nhận liên quan sẽ ở trạng thái tắt tiếng cho tới khi và trừ khi một hay nhiều stream được máy ngang hàng từ xa thêm vào bộ nhận.
Ngoại lệ
InvalidAccessErrorDOMException-
Ném ra nếu track được chỉ định (hoặc tất cả các stream nền của nó) đã là một phần của
RTCPeerConnection. InvalidStateErrorDOMException-
Ném ra nếu
RTCPeerConnectionđã đóng.
Ghi chú sử dụng
>Thêm track vào nhiều stream
Sau tham số track, bạn có thể tùy chọn chỉ định một hoặc nhiều đối tượng MediaStream để thêm track vào.
Chỉ các track được gửi từ một máy ngang hàng sang máy ngang hàng khác, không phải stream.
Vì stream là riêng cho từng máy ngang hàng, việc chỉ định một hay nhiều stream có nghĩa là máy ngang hàng còn lại sẽ tự động tạo một stream tương ứng ở đầu kia của kết nối, rồi tự động thêm track đã nhận vào các stream đó.
Track không gắn stream
Nếu không chỉ định stream nào, track sẽ là không gắn stream.
Điều đó hoàn toàn chấp nhận được, dù máy ngang hàng từ xa sẽ phải tự quyết định gắn track vào stream nào, nếu có.
Đây là cách dùng addTrack() rất phổ biến khi xây dựng nhiều kiểu ứng dụng đơn giản, nơi chỉ cần một stream.
Ví dụ, nếu tất cả những gì bạn chia sẻ với máy ngang hàng từ xa là một stream duy nhất gồm một audio track và một video track, bạn không cần phải quản lý track nào thuộc stream nào, nên cứ để transceiver xử lý hộ.
Ví dụ sau cho thấy một hàm dùng getUserMedia() để lấy stream từ camera và microphone của người dùng, rồi thêm từng track từ stream đó vào kết nối ngang hàng mà không chỉ định stream cho mỗi track:
async function openCall(pc) {
const gumStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
for (const track of gumStream.getTracks()) {
pc.addTrack(track);
}
}
Kết quả là một tập các track được gửi tới máy ngang hàng từ xa, mà không có liên kết stream nào.
Trình xử lý cho sự kiện track ở máy ngang hàng từ xa sẽ chịu trách nhiệm quyết định track nào cần được thêm vào stream nào, ngay cả khi điều đó có nghĩa là thêm tất cả vào cùng một stream.
Trình xử lý ontrack có thể trông như sau:
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);
}
};
Ở đây, trình xử lý sự kiện track sẽ thêm track vào stream đầu tiên được sự kiện chỉ định, nếu có.
Nếu không, lần đầu ontrack được gọi, một stream mới sẽ được tạo và gắn vào phần tử video, rồi track được thêm vào stream mới.
Từ đó trở đi, các track mới sẽ được thêm vào stream đó.
Bạn cũng có thể chỉ tạo một stream mới cho mỗi track nhận được:
pc.ontrack = (ev) => {
if (ev.streams && ev.streams[0]) {
videoElem.srcObject = ev.streams[0];
} else {
let inboundStream = new MediaStream(ev.track);
videoElem.srcObject = inboundStream;
}
};
Track gắn stream
Để thêm track vào một stream cụ thể trên máy ngang hàng từ xa, hãy chỉ định các stream mà track sẽ được gắn vào khi gọi addTrack().
Đây là cách làm tương đối đơn giản để nhóm track khi gửi chúng cho máy ngang hàng:
async function openCall(pc) {
const gumStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
for (const track of gumStream.getTracks()) {
pc.addTrack(track, gumStream);
}
}
Máy ngang hàng từ xa sau đó có thể dùng trình xử lý sự kiện track như sau:
pc.ontrack = ({ streams: [stream] }) => (videoElem.srcObject = stream);
Điều này đặt stream hiện tại của phần tử video thành stream chứa track vừa được thêm vào kết nối.
Sender được tái sử dụng
Phương thức này trả về một RTCRtpSender mới hoặc một thể hiện hiện có để tái sử dụng.
Một thể hiện RTCRtpSender chỉ có thể được tái sử dụng nếu nó đáp ứng các tiêu chí sau:
- Không có track nào hiện đã được gắn với sender.
RTCRtpTransceivergắn với sender có mộtRTCRtpReceivermà thuộc tínhtrackcủa nó chỉ định mộtMediaStreamTrackcókindgiống vớikindcủa tham sốtrackđược chỉ định khi gọiRTCPeerConnection.addTrack().- Thuộc tính
RTCRtpTransceiver.currentDirectionkhông phải"stopped". RTCRtpSenderđang được xét chưa từng được dùng để gửi dữ liệu. NếucurrentDirectioncủa transceiver từng là"sendrecv"hoặc"sendonly", sender không thể được tái sử dụng.
Nếu tất cả các tiêu chí đó đều được đáp ứng, sender sẽ được tái sử dụng và các thay đổi sau sẽ xảy ra với RTCRtpSender và RTCRtpTransceiver hiện có:
trackcủaRTCRtpSenderđược đặt thành track được chỉ định.- Tập các stream gắn với sender được đặt thành danh sách stream được truyền vào phương thức này,
stream1, …,streamN. RTCRtpTransceiverliên quan cócurrentDirectionđược cập nhật để thể hiện rằng nó đang gửi; nếu giá trị hiện tại của nó là"recvonly", nó sẽ trở thành"sendrecv", và nếu giá trị hiện tại là"inactive", nó sẽ trở thành"sendonly".
Sender mới
Nếu không có sender hiện có nào có thể tái sử dụng, một sender mới sẽ được tạo. Việc này cũng tạo ra các đối tượng liên quan cần tồn tại. Quá trình tạo sender mới tạo ra các thay đổi sau:
RTCRtpSendermới được tạo với track được chỉ định và tập stream được chỉ định.- Một
RTCRtpReceivermới được tạo với mộtMediaStreamTrackmới làm thuộc tínhtrackcủa nó (không phải track được chỉ định khi gọiaddTrack()).kindcủa track này được đặt cho khớp vớikindcủa track được truyền vào như tham số đầu vào. - Một
RTCRtpTransceivermới được tạo và gắn với sender và receiver mới. directioncủa transceiver mới được đặt thành"sendrecv".- Transceiver mới được thêm vào tập transceiver của
RTCPeerConnection.
Ví dụ
Ví dụ này được rút ra từ mã trình bày trong bài viết Báo hiệu và gọi video và mã mẫu đi kèm.
Nó đến từ phương thức handleVideoOfferMsg() ở đó, được gọi khi nhận được thông điệp offer từ máy ngang hàng từ xa.
const mediaConstraints = {
audio: true, // Chúng ta muốn có một audio track
video: true, // Và chúng ta muốn có một 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));
});
Mã này lấy SDP đã nhận từ máy ngang hàng từ xa và tạo một RTCSessionDescription mới để truyền vào setRemoteDescription().
Khi thao tác đó thành công, nó dùng MediaDevices.getUserMedia() để lấy quyền truy cập vào webcam và microphone cục bộ.
Nếu thành công, stream thu được sẽ được gán làm nguồn cho phần tử <video> được tham chiếu bởi biến previewElement.
Bước cuối cùng là bắt đầu gửi video cục bộ qua kết nối ngang hàng tới bên gọi.
Việc này được thực hiện bằng cách thêm từng track trong stream bằng cách lặp qua danh sách được trả về bởi MediaStream.getTracks() và truyền chúng cho addTrack() cùng với stream mà chúng là một thành phần.
Thông số kỹ thuật
| Thông số kỹ thuật |
|---|
| WebRTC: Real-Time Communication in Browsers> # dom-rtcpeerconnection-addtrack> |