问题描述
我正在使用webrtc与同级之间进行通信.我不想在旧的生成的流中添加新的曲目,因为我不想为用户提供在音频通信过程中切换麦克风的功能.我正在使用的代码是
I'm using webrtc to communicate between to peers. I wan't to add new track to old generated stream, as I wan't to give functionality to users to switch their microphones during audio communications. The code I'm using is,
让"pc"成为peerConnection对象,通过该peerConnection对象进行音频通信& "newStream"是使用新选择的麦克风设备从getUserMedia函数获得的新生成的MediaStream.
Let "pc" be the peerConnection object through which audio communication takes place & "newStream" be the new generated MediaStream got from getUserMedia function with new selected microphone device.
var localStreams = pc.getLocalStreams()[0];
localStreams.removeTrack(localStreams.getAudioTracks()[0]);
var audioTrack = newStream.getAudioTracks()[0];
localStreams.addTrack(audioTrack);
新添加的曲目以任何方式开始到达另一个先前连接的对等方而又没有再次向他提供整个SDP的方式是什么?
Is their any way that the newly added track starts reaching to the other previously connected peer without offering him again the whole SDP?
在对等体之间已经建立连接的情况下,在切换媒体设备(即麦克风)的情况下使用的最佳方式是什么?
What would be the optimized way to use in such case of switch media device, i.e., microphones when the connections is already established between peers?
推荐答案
更新: 靠近底部的工作示例.
由于规范的不断发展,这很大程度上取决于您当前使用的浏览器.
This depends greatly on which browser you are using at the moment, due to an evolving spec.
在规范和Firefox中,对等连接现在从根本上来说是基于轨道的,并且不依赖于本地流关联.您有var sender = pc.addTrack(track, stream)
,pc.removeTrack(sender)
甚至sender.replaceTrack(track)
,后者根本不涉及重新协商.
In the specification and Firefox, peer connections are now fundamentally track-based, and do not depend on local stream associations. You have var sender = pc.addTrack(track, stream)
, pc.removeTrack(sender)
, and even sender.replaceTrack(track)
, the latter involving no renegotiation at all.
在Chrome中,您仍然只有pc.addStream
和pc.removeStream
,并且从本地流中删除曲目会导致其发送停止,但是将其重新添加是行不通的.我很幸运删除并重新将整个流添加到对等连接中,然后进行重新协商.
In Chrome you still have just pc.addStream
and pc.removeStream
, and removing a track from a local stream causes sending of it to cease, but adding it back didn't work. I had luck removing and re-adding the entire stream to the peer connection, followed by renegotiation.
不幸的是,使用 adapter.js 不能解决问题,因为addTrack
很难填充.
Unfortunately, using adapter.js does not help here, as addTrack
is tricky to polyfill.
重新谈判尚未开始.您只需要:
Renegotiation is not starting over. All you need is:
pc.onnegotiationneeded = e => pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.then(() => signalingChannel.send(JSON.stringify({ "sdp": pc.localDescription }));
.catch(failed);
添加此选项后,对等连接将在需要时使用信令通道自动重新协商.这甚至取代了对createOffer
和您现在正在做的朋友的呼叫,从而赢得了胜利.
Once you add this, the peer connection automatically renegotiates when needed using your signaling channel. This even replaces the calls to createOffer
and friends you're doing now, a net win.
使用此功能,您可以在实时连接期间添加/删除轨道,并且它应该正常".
With this in place, you can add/remove tracks during a live connection, and it should "just work".
如果这还不够平滑,您甚至可以pc.createDataChannel("yourOwnSignalingChannel")
If that's not smooth enough, you can even pc.createDataChannel("yourOwnSignalingChannel")
以下是所有示例(在Chrome中使用 https小提琴):
Here's an example of all of that (use https fiddle in Chrome):
var config = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
var signalingDelayMs = 0;
var dc, sc, pc = new RTCPeerConnection(config), live = false;
pc.onaddstream = e => v2.srcObject = e.stream;
pc.ondatachannel = e => dc? scInit(sc = e.channel) : dcInit(dc = e.channel);
var streams = [];
var haveGum = navigator.mediaDevices.getUserMedia({fake:true, video:true})
.then(stream => streams[1] = stream)
.then(() => navigator.mediaDevices.getUserMedia({ video: true }))
.then(stream => v1.srcObject = streams[0] = stream);
pc.oniceconnectionstatechange = () => update(pc.iceConnectionState);
var negotiating; // Chrome workaround
pc.onnegotiationneeded = () => {
if (negotiating) return;
negotiating = true;
pc.createOffer().then(d => pc.setLocalDescription(d))
.then(() => live && sc.send(JSON.stringify({ sdp: pc.localDescription })))
.catch(log);
};
pc.onsignalingstatechange = () => negotiating = pc.signalingState != "stable";
function scInit() {
sc.onmessage = e => wait(signalingDelayMs).then(() => {
var msg = JSON.parse(e.data);
if (msg.sdp) {
var desc = new RTCSessionDescription(JSON.parse(e.data).sdp);
if (desc.type == "offer") {
pc.setRemoteDescription(desc).then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer)).then(() => {
sc.send(JSON.stringify({ sdp: pc.localDescription }));
}).catch(log);
} else {
pc.setRemoteDescription(desc).catch(log);
}
} else if (msg.candidate) {
pc.addIceCandidate(new RTCIceCandidate(msg.candidate)).catch(log);
}
}).catch(log);
}
function dcInit() {
dc.onopen = () => {
live = true; update("Chat:"); chat.disabled = false; chat.select();
};
dc.onmessage = e => log(e.data);
}
function createOffer() {
button.disabled = true;
pc.onicecandidate = e => {
if (live) {
sc.send(JSON.stringify({ "candidate": e.candidate }));
} else if (!e.candidate) {
offer.value = pc.localDescription.sdp;
offer.select();
answer.placeholder = "Paste answer here";
}
};
dcInit(dc = pc.createDataChannel("chat"));
scInit(sc = pc.createDataChannel("signaling"));
};
offer.onkeypress = e => {
if (e.keyCode != 13 || pc.signalingState != "stable") return;
button.disabled = offer.disabled = true;
var obj = { type:"offer", sdp:offer.value };
pc.setRemoteDescription(new RTCSessionDescription(obj))
.then(() => pc.createAnswer()).then(d => pc.setLocalDescription(d))
.catch(log);
pc.onicecandidate = e => {
if (e.candidate) return;
if (!live) {
answer.focus();
answer.value = pc.localDescription.sdp;
answer.select();
} else {
sc.send(JSON.stringify({ "candidate": e.candidate }));
}
};
};
answer.onkeypress = e => {
if (e.keyCode != 13 || pc.signalingState != "have-local-offer") return;
answer.disabled = true;
var obj = { type:"answer", sdp:answer.value };
pc.setRemoteDescription(new RTCSessionDescription(obj)).catch(log);
};
chat.onkeypress = e => {
if (e.keyCode != 13) return;
dc.send(chat.value);
log("> " + chat.value);
chat.value = "";
};
function addTrack() {
pc.addStream(streams[0]);
flipButton.disabled = false;
removeAddButton.disabled = false;
}
var flipped = 0;
function flip() {
pc.getSenders()[0].replaceTrack(streams[flipped = 1 - flipped].getVideoTracks()[0])
.catch(log);
}
function removeAdd() {
if ("removeTrack" in pc) {
pc.removeTrack(pc.getSenders()[0]);
pc.addStream(streams[flipped = 1 - flipped]);
} else {
pc.removeStream(streams[flipped]);
pc.addStream(streams[flipped = 1 - flipped]);
}
}
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var update = msg => div2.innerHTML = msg;
var log = msg => div.innerHTML += msg + "<br>";
<video id="v1" width="120" height="90" autoplay muted></video>
<video id="v2" width="120" height="90" autoplay></video><br>
<button id="button" onclick="createOffer()">Offer:</button>
<textarea id="offer" placeholder="Paste offer here"></textarea><br>
Answer: <textarea id="answer"></textarea><br>
<button id="button" onclick="addTrack()">AddTrack</button>
<button id="removeAddButton" onclick="removeAdd()" disabled>Remove+Add</button>
<button id="flipButton" onclick="flip()" disabled>ReplaceTrack (FF only)</button>
<div id="div"><p></div><br>
<table><tr><td><div id="div2">Not connected</div></td>
<td><input id="chat" disabled></input></td></tr></table><br>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
说明:
不涉及服务器,因此请按Offer
键,然后在两个选项卡之间手动剪切并粘贴报价并回答(粘贴后按ENTER键).
No server is involved, so hit Offer
, then cut'n'paste offer and answer manually between two tabs (hit the ENTER key after pasting).
完成后,您可以在数据通道上聊天,然后点击addTrack
将视频添加到另一侧.
Once done, you can chat over the data-channel, and hit addTrack
to add video to the other side.
然后您可以使用Remove + Add
或replaceTrack (FF only)
切换出远程显示的视频(如果要使用辅助摄像机,请在Chrome中修改小提琴.)
You can then switch out video shown remotely with Remove + Add
or replaceTrack (FF only)
(modify fiddle in Chrome if you have a secondary camera you want to use.)
协商现在已经在数据通道上进行了(不再粘贴).
Renegotiation is all happening over the data channel now (no more cut'n'paste).
这篇关于如何在WebRTC的MediaStream中添加Track的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!