[英]How to set remote description for a WebRTC caller in Chrome without errors?
I hope there is no flaw in the logic. 我希望逻辑上没有缺陷。
Step 1: caller creates offer 步骤1:呼叫者创建报价
Step 2: caller sets localDescription 步骤2:呼叫者设置localDescription
Step 3: caller sends the description to the callee 步骤3:呼叫者将描述发送给被呼叫者
//------------------------------------------------------// // ------------------------------------------------ ------ //
Step 4: callee receives the offer sets remote description 步骤4:被叫方收到要约集的远程描述
Step 5: callee creates answer 步骤5:被叫方创建答案
Step 6: callee sets local description 步骤6:被叫方设置本地描述
Step 7: callee send the description to caller 步骤7:被呼叫者将说明发送给呼叫者
//------------------------------------------------------// // ------------------------------------------------ ------ //
Step 8: caller receives the answer and sets remote description 步骤8:呼叫者收到答案并设置远程描述
And here is the code for the above 这是上面的代码
const socket = io();
const constraints = {
audio: true,
video: true
};
const configuration = {
iceServers: [{
"url": "stun:23.21.150.121"
}, {
"url": "stun:stun.l.google.com:19302"
}]
};
const selfView = $('#selfView')[0];
const remoteView = $('#remoteView')[0];
var pc = new RTCPeerConnection(configuration);
pc.onicecandidate = ({
candidate
}) => {
socket.emit('message', {
to: $('#remote').val(),
candidate: candidate
});
};
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
socket.emit('message', {
to: $('#remote').val(),
desc: pc.localDescription
});
} catch (err) {
console.error(err);
}
};
pc.ontrack = (event) => {
// don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
socket.on('message', async ({
from,
desc,
candidate
}) => {
$('#remote').val(from);
try {
if (desc) {
// if we get an offer, we need to reply with an answer
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream = await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
selfView.srcObject = stream;
await pc.setLocalDescription(await pc.createAnswer());
console.log(pc.localDescription);
socket.emit({
to: from,
desc: pc.localDescription
});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc).catch(err => console.log(err));
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(err => console.log(err));
}
} catch (err) {
console.error(err);
}
});
async function start() {
try {
// get local stream, show it in self-view and add it to be sent
const stream = await requestUserMedia(constraints);
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
attachMediaStream(selfView, stream);
} catch (err) {
console.error(err);
}
}
socket.on('id', (data) => {
$('#myid').text(data.id);
});
// this function is called once the caller hits connect after inserting the unique id of the callee
async function connect() {
try {
await pc.setLocalDescription(await pc.createOffer());
socket.emit('message', {
to: $('#remote').val(),
desc: pc.localDescription
});
} catch (err) {
console.error(err);
}
}
socket.on('error', data => {
console.log(data);
});
Now this code throws an error while executing Step 8 现在,此代码在执行步骤8时引发错误
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer DOMException:无法在'RTCPeerConnection'上执行'setRemoteDescription':无法设置远程商品sdp:处于错误状态:kHaveLocalOffer
DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate DOMException:无法在“ RTCPeerConnection”上执行“ addIceCandidate”:处理ICE候选者时出错
Tried to debug but didn't find any flaw in the logic or code. 尝试调试,但未发现逻辑或代码中的任何缺陷。 Noticed one weird thing that the the pc
object has localDescription
and currentLocalDescription
and i think the callee who creates the answer must have both the description type to be answer
but instead shows the localDescription
to be offer
and currentLocalDescription
type is answer
. 注意到一个奇怪的事情是,在pc
对象有localDescription
和currentLocalDescription
,我认为谁创造了答案被叫方必须同时说明类型为answer
,而是给出了localDescription
是offer
和currentLocalDescription
类型answer
。
I have no idea if it is supposed to behave like that or not as I am begginer. 我不知道它是否应该像我开始时那样表现。
Thanks in advance. 提前致谢。
Your code is correct. 您的代码是正确的。 This is a long-standing bug in Chrome with negotiationneeded
. 这是Chrome中长期存在的错误,需要经过negotiationneeded
。
I've instrumented it in a fiddle (right-click and open in TWO adjacent windows, then click call in one). 我已经在一个小提琴中对其进行了检测 (右键单击并在两个相邻的窗口中打开,然后单击“其中一个”)。
In Firefox, it works. 在Firefox中,它可以工作。 The offerer negotiates once because you add two tracks (video/audio) at once: 要约人协商一次,因为您一次添加了两个轨道(视频/音频):
negotiating in stable
onmessage answer
and, on the answerer side, the tracks you add outside of 'stable'
state are added to the answer: 并且在答题器端,您在'stable'
状态之外添加的曲目将添加到答案中:
onmessage offer
adding audio track
adding video track
But in Chrome, it's broken, firing negotiationneeded
twice on the offerer, once per track added: 但是在Chrome浏览器中,它很坏,需要在要约人上negotiationneeded
两次negotiationneeded
,每添加一条轨道, negotiationneeded
一次:
negotiating in stable
negotiating in stable
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
and firing negotiationneeded
twice on the answerer side, which isn't even in 'stable'
state: 并且在答答器端需要negotiationneeded
两次negotiationneeded
,这甚至都不处于'stable'
状态:
onmessage offer
adding audio track
adding video track
negotiating in have-remote-offer
negotiating in have-remote-offer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
These extra events cause the havoc of reciprocal state errors seen on both ends here. 这些额外的事件导致在此处两端都看到的相互状态错误的破坏。
To be specific, Chrome violates two parts of the spec here: 具体来说,Chrome违反了规范的两个部分:
"Queue a task" to fire this event. “排队任务”以触发此事件。 "queueing prevents negotiationneeded from firing prematurely, in the common situation where multiple modifications to connection are being made at once." “在通常要同时对连接进行多次修改的常见情况下,排队可以防止协商过早触发。”
If connection's signaling state is not "stable"
, abort these steps [to fire the event]. 如果连接的信令状态不是"stable"
,请中止这些步骤[以触发事件]。
Working around both Chrome bugs requires (using async
/ await
for brevity): 解决这两个 Chrome错误需要(为了简短起见,请使用async
/ await
):
let negotiating = false;
pc.onnegotiationneeded = async e => {
try {
if (negotiating || pc.signalingState != "stable") return;
negotiating = true;
/* Your async/await-using code goes here */
} finally {
negotiating = false;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.