我正在实现WebRTC对等调用,它在所有浏览器中都能正常运行,除非chrome是调用初始化程序(调用邀请函数)。
如果chrome是呼叫初始化程序,则ICE连接状态更改为connected并且视频出现在另一个对等方中,然后发生错误“ DOMException:“无法在稳定状态下创建应答””,并且视频停止传输到另一个同行。
如果接收器也是镶边的,则错误消息为“无法在'RTCPeerConnection'上执行'createAnswer':PeerConnection无法在具有have-remote-off或have-local-pranswer之外的状态下创建应答。” >
以下是handleVideoOfferMsg函数和handleGetUserMediaError函数(触发错误)代码:
function handleVideoOfferMsg(msg) {
// debugger;
// $("#ring")[0].play();
var localStream = null;
targetUsername = msg.from;
// Call createPeerConnection() to create the RTCPeerConnection.
log("Starting to accept invitation from " + targetUsername);
createPeerConnection();
// We need to set the remote description to the received SDP offer
// so that our local WebRTC layer knows how to talk to the caller.
var desc = new RTCSessionDescription(msg.sdp);
myPeerConnection.setRemoteDescription(desc).then(function () {
log("Setting up the local media stream...");
return navigator.mediaDevices.getUserMedia(responserConstraints);
})
.then(function(stream) {
$("#video-container").removeClass("hidden");
log("-- Local video stream obtained");
localStream = stream;
if(responserConstraints.hasOwnProperty('video'))
{
$("#localVideo").removeClass('hidden')[0].srcObject = localStream;
}
log("-- Adding outgoing tracks to the RTCPeerConnection");
localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
})
.then(function() {
log("------> Creating answer");
// Now that we've successfully set the remote description, we need to
// start our stream up locally then create an SDP answer. This SDP
// data describes the local end of our call, including the codec
// information, options agreed upon, and so forth.
return myPeerConnection.createAnswer();
})
.then(function(answer) {
log("------> Setting local description after creating answer");
// We now have our answer, so establish that as the local description.
// This actually configures our end of the call to match the settings
// specified in the SDP.
return myPeerConnection.setLocalDescription(answer);
})
.then(function() {
var msg = {
name: myUsername,
target: targetUsername,
type: "video-answer",
sdp: myPeerConnection.localDescription
};
// We've configured our end of the call now. Time to send our
// answer back to the caller so they know that we want to talk
// and how to talk to us.
log("Sending answer packet back to other peer");
sendToServer(msg);
})
.catch(
handleGetUserMediaError);
}
function handleGetUserMediaError(e) {
log(e);
switch(e.name) {
case "NotFoundError":
alert("Unable to open your call because no camera and/or microphone" +
"were found.");
break;
case "SecurityError":
case "PermissionDeniedError":
// Do nothing; this is the same as the user canceling the call.
break;
default:
//the error occur here
alert("Error opening your camera and/or microphone: " + e.message);
console.log("----------");
console.log(e);
break;
}
// Make sure we shut down our end of the RTCPeerConnection so we're
// ready to try again.
closeVideoCall();
}
邀请功能:
function invite() {
log("Starting to prepare an invitation");
if (myPeerConnection) {
alert("You can't start a call because you already have one open!");
} else {
var clickedUsername = $("input[name=teacherId]").val();
console.log('user id is '+ clickedUsername);
// Don't allow users to call themselves, because weird.
if (clickedUsername === myUsername) {
alert("I'm afraid I can't let you talk to yourself. That would be weird.");
return;
}
// Record the username being called for future reference
targetUsername = clickedUsername;
log("Inviting user " + targetUsername);
// Call createPeerConnection() to create the RTCPeerConnection.
log("Setting up connection to invite user: " + targetUsername);
createPeerConnection();
// Now configure and create the local stream, attach it to the
// "preview" box (id "video"), and add it to the
// RTCPeerConnection.
log("Requesting webcam access...");
isInviter = true;
navigator.mediaDevices.getUserMedia(mediaConstraints)
.then(function(localStream) {
$("#video-container").removeClass("hidden");
console.log(localStream);
log("-- Local video stream obtained");
if(responserConstraints.hasOwnProperty('video'))
{
var localVideo = $('#localVideo');
localVideo.removeClass('hidden')[0].srcObject = localStream;
localVideo[0].mute;
}
else
{
var videoEl = document.getElementById("video");
videoEl.muted = true;
videoEl.srcObject = localStream;
}
log("-- Adding incoming tracks to the RTCPeerConnection");
localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
})
.catch(handleGetUserMediaError);
}
}
控制台响应如下:
[3:28:31 PM] *** ICE gathering state changed to: gathering
opensocket.js:66 [3:28:31 PM] Outgoing ICE candidate: candidate:202810205 1 udp 2122260223 192.168.1.21 39265 typ host generation 0 ufrag 76SW network-id 1 network-cost 10
opensocket.js:66 [3:28:31 PM] Sending 'new-ice-candidate' message: {"type":"new-ice-candidate","candidate":{"candidate":"candidate:202810205 1 udp 2122260223 192.168.1.21 39265 typ host generation 0 ufrag 76SW network-id 1 network-cost 10","sdpMid":"audio","sdpMLineIndex":0,"usernameFragment":"76SW"},"hash":"m0wk8Kaz8JFejuJGte7kAmNtYDiWPZNBGl7fKA5b"}
opensocket.js:66 [3:28:31 PM] -- Local video stream obtained
opensocket.js:66 [3:28:31 PM] -- Adding outgoing tracks to the RTCPeerConnection
opensocket.js:66 [3:28:31 PM] ------> Creating answer
opensocket.js:363 stable
opensocket.js:66 [3:28:31 PM] *** Negotiation needed
opensocket.js:66 [3:28:31 PM] ---> Creating offer
opensocket.js:363 stable
opensocket.js:66 [3:28:31 PM] *** Negotiation needed
opensocket.js:66 [3:28:31 PM] ---> Creating offer
opensocket.js:66 [3:28:31 PM] InvalidStateError: Failed to execute 'createAnswer' on 'RTCPeerConnection': PeerConnection cannot create an answer in a state other than have-remote-offer or have-local-pranswer.
我找不到解决方法!错误仅在chrome中发生!
有什么帮助吗?
编辑:这是我的handleNegotiationNeededEvent函数:
function handleNegotiationNeededEvent() {
console.log(myPeerConnection.signalingState);
log("*** Negotiation needed");
log("---> Creating offer");
myPeerConnection.createOffer().then(function(offer) {
log("---> Creating new description object to send to remote peer");
return myPeerConnection.setLocalDescription(offer);
})
.then(function() {
log("---> Sending offer to remote peer");
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
})
.catch(reportError);
}
答案 0 :(得分:0)
好吧,我找到了一个解决方案,这真的很奇怪!
Chrome两次触发onnegotiationneeded
!
我不知道为什么chrome会出现这种怪异的行为!
因此createOffer
被触发两次,如日志所示:
opensocket.js:66 [3:28:31 PM] *** Negotiation needed
opensocket.js:66 [3:28:31 PM] ---> Creating offer
opensocket.js:363 stable
opensocket.js:66 [3:28:31 PM] *** Negotiation needed
opensocket.js:66 [3:28:31 PM] ---> Creating offer
所以我的解决方案是将handleNegotiationNeededEvent
更改为以下内容:
var Negotiation = 0;
function handleNegotiationNeededEvent() {
if(Negotiation === 0 )
{
Negotiation++;
}
else
{
return;
}
// if (myPeerConnection.signalingState === "stable") return;
console.log(myPeerConnection.signalingState);
log("*** Negotiation needed");
log("---> Creating offer");
myPeerConnection.createOffer().then(function(offer) {
log("---> Creating new description object to send to remote peer");
return myPeerConnection.setLocalDescription(offer);
})
.then(function() {
log("---> Sending offer to remote peer");
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
})
.catch(reportError);
}
对我来说看起来很奇怪,但至少现在可以正常工作了。