chrome中的“无法在稳定状态下创建答案”

时间:2019-01-02 13:33:11

标签: javascript stream video-streaming webrtc

我正在实现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);
}

1 个答案:

答案 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);
}

对我来说看起来很奇怪,但至少现在可以正常工作了。