Failed to set session description: OperationError: Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS

Failed to set session description: OperationError: Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS

我使用了 Angular JS:- 从服务器端获取空间 Id/Token 以连接网络套接字 应用程序中使用了以下代码 -

app.controller("videoCallingController",
["$scope", "$location", "$rootScope", "$localStorage", 'AuthenticationService', "CommonService", "videoService",
function ($scope, $location, $rootScope, $localStorage, AuthenticationService, CommonService, videoService) {

$scope.UserInfo = getLoggedInUserDetails();

if ($scope.UserInfo == null) {
    window.location.replace("/login.html#/");
}

$scope.offerConversationDetails = JSON.parse(localStorage.getItem('offerConversationDetails')); //$rootScope.offerConversationDetails;
$scope.IsCallBtn = false;
$scope.IsCallEnd = false;
$scope.IsRemoteStreaming = false;

//------- start custom Web socket ----------------
var ws;

function initWS(groupname) {

    //  var groupname = "TestRoom";


    ws = new WebSocket("wss://" + location.host + "/api/" + "api/Values/Get?id=" + groupname);


    ws.onopen = function () { };

    ws.onmessage = function (evt) {
        var signal = null;
        if (!pc1) { answerCall(); }
        signal = JSON.parse(evt.data);
        if (signal.sdp) {
            console.log("Received SDP from remote peer.");
            pc1.setRemoteDescription(new RTCSessionDescription(signal.sdp));

            createAndSendAnswer(signal.sdp);
        }
        else if (signal.candidate) {
            console.log("Received ICECandidate from remote peer.");
            //pc1.addIceCandidate(new RTCIceCandidate(signal.candidate));
            addIceCandidate(signal);
        } else if (signal.closeConnection) {
            console.log("Received 'close call' signal from remote peer.");
            //endCall();
        }
    };

    ws.onerror = function (evt) {
        console.log(evt.message);
    };
    ws.onclose = function () {
        console.log("disconnected");
    };
}

function sendWS(msg) {
    if (ws.readyState == WebSocket.OPEN) {
        ws.send(msg);
    }
}

function closeWS() {
    ws.close();
}

//------- end custom Web socket ----------------

//---- start Html element selectors ------------
//var callButton = document.getElementById('callButton');
//callButton.onclick = initiateCall;

var startTime;
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');

localVideo.addEventListener('loadedmetadata', function () {
    trace('Local video videoWidth: ' + this.videoWidth +
        'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.addEventListener('loadedmetadata', function () {
    trace('Remote video videoWidth: ' + this.videoWidth +
        'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.onresize = function () {
    trace('Remote video size changed to ' +
        remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
    // We'll use the first onsize callback as an indication that video has started
    // playing out.
    if (startTime) {
        var elapsedTime = window.performance.now() - startTime;
        trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
        startTime = null;
    }
};

//---- end Html element selectors ------------


var localStream;
var pc1;
// var pc2;
var offerOptions = {
    offerToReceiveAudio: 1,
    offerToReceiveVideo: 1
};

function getName(pc) {
    return 'pc1'; //(pc === pc1) ? 'pc1' : 'pc2';
}

function getOtherPc(pc) {
    return pc1; //(pc === pc1) ? pc2 : pc1;
}

function gotStream(stream) {
    trace('Received local stream');
    //localVideo = attachMediaStream(localVideo, stream);
    // attachMediaStream(localVideo, stream);

    console.log(attachMediaStream(localVideo, stream));
    localStream = stream;
    // callButton.disabled = false;
    muteAudio(stream);
}

function muteAudio(stream) {
    var AudioTrack = stream.getAudioTracks()[0];
    //var localAudioBtn = document.getElementById('localAudioBtn');
    var muteBtn = document.getElementById('localAudioMuteBtn');
    var unMuteBtn = document.getElementById('localAudioUnMuteBtn');

    //localAudioBtn.style.visibility = 'visible';
    unMuteBtn.onclick = MuteUnMute;
    muteBtn.onclick = MuteUnMute;

    function MuteUnMute() {
        AudioTrack.enabled = !AudioTrack.enabled;
        muteBtn.className = 'btn btn-info btn-lg' + (AudioTrack.enabled ? '' : ' hidden');
        unMuteBtn.className = 'btn btn-warning btn-lg' + (AudioTrack.enabled ? ' hidden' : '');
        //localAudioBtn.className = 'button button-mute' + (AudioTrack.enabled ? '' : ' muted');
    };
}

function gumFailed(e) {
    alert('getUserMedia() error: ' + e.name);
}

function start() {
    trace('Requesting local stream');
    //  startButton.disabled = true;
    var constraints = {
        audio: true,
        video: { width: 1280, height: 720 } //true
    };
    if (typeof Promise === 'undefined') {
        navigator.getUserMedia(constraints, gotStream, gumFailed);
    } else {
        navigator.mediaDevices.getUserMedia(constraints)
            .then(gotStream)
            .catch(gumFailed);
    }
}

var Room = {};

Room.createNewRoom = function () {
    if ($scope.offerConversationDetails.type == 'owner') {
        videoService.CreateNewRoom($scope.offerConversationDetails.offerId, function (resp) {
            if (resp.status === iresponseStatus.success) {
                console.log("CreateNewRoom");
                console.log(resp.result);
                $scope.roomDetails = resp.result;
                $rootScope.offerRoomId = $scope.roomDetails.RoomId;
                Room.getRoomStatusById($scope.roomDetails.RoomId);
            }
            else if (resp.status === iresponseStatus.error) {
                if (resp.error.code == 5) {
                    callDateTimeExpired(resp.error.message);
                }
                displayNoty("error", resp.error.message);
                console.log(resp.error);
                setTimeout(function () { Room.createNewRoom(); }, 10000);
            }
        });
    }
}

Room.getRoomToken = function () {
    if ($scope.offerConversationDetails.type == 'winner') {
        videoService.GetRoomToken($scope.offerConversationDetails.offerId, function (resp) {
            if (resp.status === iresponseStatus.success) {
                console.log("Get Room Token");
                console.log(resp.result);
                $scope.roomDetails = resp.result;
                $rootScope.offerRoomId = $scope.roomDetails.RoomId;
                initWS($scope.roomDetails.RoomToken);
            }
            else if (resp.status === iresponseStatus.error) {
                console.log(resp.error);
                if (resp.error.code == 5) {
                    callDateTimeExpired(resp.error.message);
                }
                setTimeout(function () { Room.getRoomToken(); }, 1000);
            }
        });
    }
}

Room.getRoomStatusById = function (roomId) {
    if ($scope.offerConversationDetails.type == 'owner') {
        videoService.GetRoomStatusById(roomId, function (resp) {
            if (resp.status === iresponseStatus.success) {
                if (resp.result == 2)  //Active
                {
                    initWS($scope.roomDetails.RoomToken);
                    $scope.IsCallBtn = true;
                }
                else {
                    //setTimeout(Room.getRoomStatusById(roomId), 100);
                    setTimeout(function () { Room.getRoomStatusById(roomId); }, 100);
                }

            }
            else if (resp.status === iresponseStatus.error) {
                console.log(resp.error);
                if (resp.error.code == 5) {
                    callDateTimeExpired(resp.error.message);
                }
                //setTimeout(Room.getRoomStatusById(roomId), 100);
                setTimeout(function () { Room.getRoomStatusById(roomId); }, 100);
            }
        });
    }
}

Room.updateRoomConsumption = function () {
    if ($scope.offerConversationDetails.type == 'owner') {
        updateRoomConsumption_interval = setInterval(UpdateRoomConsumption(), 5000);
    }
}

var updateRoomConsumption_interval = null;
function UpdateRoomConsumption() {
    if ($scope.offerConversationDetails.ChatDurationSeconds > $scope.seconds) {
        videoService.UpdateRoomConsumption($scope.roomDetails.RoomId, $scope.seconds, function (resp) {
            if (resp.status === iresponseStatus.success) {
                // $scope.seconds += 5;
                //setTimeout(function () { Room.updateRoomConsumption(); }, 5000);
            }
            else if (resp.status === iresponseStatus.error) {
                console.log(resp.error);
            }
        });
    }
    else {
        EndCall();
        clearInterval(updateRoomConsumption_interval);
    }
    $scope.seconds = $scope.seconds + 5;
}

//initWS();
start();

Room.createNewRoom();
Room.getRoomToken();


function prepareCall() {

    // callButton.disabled = true;

    trace('Starting call');
    startTime = 4410; //window.performance.now();
    var videoTracks = localStream.getVideoTracks();
    var audioTracks = localStream.getAudioTracks();
    if (videoTracks.length > 0) {
        trace('Using video device: ' + videoTracks[0].label);
    }
    if (audioTracks.length > 0) {
        trace('Using audio device: ' + audioTracks[0].label);
    }
    //  var servers = null;
    var servers = {
        iceServers: [
            //{ url: "stun:23.21.150.121" },
            { url: "stun:stun.1.google.com:19302" }
            //{ url: !isFirefox ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121' }
        ]
    };


    pc1 = new RTCPeerConnection(servers);
    trace('Created local peer connection object pc1');
    pc1.onicecandidate = function (e) {
        onIceCandidate(pc1, e);
    };
    // pc2 = new RTCPeerConnection(servers);
    //trace('Created remote peer connection object pc2');
    //pc2.onicecandidate = function (e) {
    //    onIceCandidate(pc2, e);
    //};
    pc1.oniceconnectionstatechange = function (e) {
        onIceStateChange(pc1, e);
    };
    //pc2.oniceconnectionstatechange = function (e) {
    //    onIceStateChange(pc2, e);
    //};
    pc1.onaddstream = gotRemoteStream;
};

// run start(true) to initiate a call
$scope.initiateCall = function () {
    prepareCall();

    pc1.addStream(localStream);
    trace('Added local stream to pc1');

    trace('pc1 createOffer start');
    pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError,
        offerOptions);

};

function answerCall() {
    prepareCall();

    pc1.addStream(localStream);
    trace('Added local stream to pc1');

    //createAndSendAnswer();
};


function onCreateSessionDescriptionError(error) {
    trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
    trace('Offer from pc1\n' + desc.sdp);
    trace('pc1 setLocalDescription start');

    pc1.setLocalDescription(desc, function () {
        onSetLocalSuccess(pc1);
        sendWS(JSON.stringify({ "sdp": desc }));
    }, onSetSessionDescriptionError);


}

function createAndSendAnswer(desc) {

    // trace('pc2 setRemoteDescription start');
    pc1.setRemoteDescription(desc, function () {
        onSetRemoteSuccess(pc1);
    }, onSetSessionDescriptionError);
    trace('pc2 createAnswer start');
    //Since the 'remote' side has no media stream we need
    // to pass in the right constraints in order for it to
    //  accept the incoming offer of audio and video.
    pc1.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);

};

function onSetLocalSuccess(pc) {
    trace(getName(pc) + ' setLocalDescription complete');
}

function onSetRemoteSuccess(pc) {
    trace(getName(pc) + ' setRemoteDescription complete');
}

function onSetSessionDescriptionError(error) {
    trace('Failed to set session description: ' + error.toString());
}

function gotRemoteStream(event) {
    // remoteVideo = attachMediaStream(remoteVideo, e.stream);

    if (event != null && event != undefined && event.stream != null && event.stream != undefined) {
        $scope.offerConversationDetails.ChatDurationSeconds = $scope.offerConversationDetails.ChatDuration * 60;

        $scope.IsDisplayTimer = true; $scope.IsRemoteStreaming = true; $scope.IsCallBtn = false;
        $scope.$apply();
        initializeClock('clockdiv', $scope.offerConversationDetails.ChatDurationSeconds);

        $scope.seconds = 0;
        Room.updateRoomConsumption($scope.seconds);

        //event.stream.oninactive = function ()
        //{
        //    if ($scope.IsCallEnd == false)
        //        hangup();
        //        alert("remote streaming get stop");
        //}
    }

    console.log(attachMediaStream(remoteVideo, event.stream));
    trace('pc2 received remote stream');
}

function onCreateAnswerSuccess(desc) {
    //trace('Answer from pc2:\n' + desc.sdp);
    //trace('pc2 setLocalDescription start');
    //pc2.setLocalDescription(desc, function () {
    //    onSetLocalSuccess(pc2);
    //}, onSetSessionDescriptionError);
    trace('pc1 setRemoteDescription start');
    pc1.setLocalDescription(desc, function () {
        onSetRemoteSuccess(pc1);
        sendWS(JSON.stringify({ "sdp": desc }));
    }, onSetSessionDescriptionError);
}

function onIceCandidate(pc, event) {

    if (!event || !event.candidate) return;
    sendWS(JSON.stringify({ "candidate": event.candidate }));

    //var candidate = event.candidate;

    //if (candidate) {



    //    getOtherPc(pc).addIceCandidate(new RTCIceCandidate(event.candidate),
    //        function () {
    //            onAddIceCandidateSuccess(pc);
    //        },
    //        function (err) {
    //            onAddIceCandidateError(pc, err);
    //        }
    //    );
    //    trace(getName(pc) + ' ICE candidate: \n' + event.candidate.candidate);
    //}
}

function addIceCandidate(event) {
    pc1.addIceCandidate(new RTCIceCandidate(event.candidate),
        function () {
            onAddIceCandidateSuccess(pc1);
        },
        function (err) {
            onAddIceCandidateError(pc1, err);
        }
    );
    trace(pc1 + ' ICE candidate: \n' + event.candidate.candidate);
}

function onAddIceCandidateSuccess(pc) {
    trace(getName(pc) + ' addIceCandidate success');
}

function onAddIceCandidateError(pc, error) {
    trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
}

function onIceStateChange(pc, event) {
    if (pc) {
        trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState);
        console.log('ICE state change event: ', event);
        if (pc.iceConnectionState == 'disconnected' || pc.iceConnectionState == 'failed') {
            EndCall();
        }
    }
}

function EndCall() {
    hangup();
    console.log("chatduration =" + $scope.offerConversationDetails.ChatDurationSeconds);
    console.log("consumption =" + $scope.seconds);
    //if ($scope.IsManuallyEnded != true)
    //{
    alertify.alert('Mepleez Conversation', 'Your conversation got finished. Please give conversation feedback to us.',
        function () {
            //hangup();
            window.location.replace("/video.html#/experience");
        });
    //}
}

function callDateTimeExpired(message) {
    alertify.alert('Mepleez Conversation', message,
        function () {
            // window.location.replace("/video.html#/experience");
            window.location.replace("/index.html#/");
        });
}


$scope.onEndCallBtnClick = function () {
    //alertify.prompt('Cancel call', 'Please enter the reason for cancelling call', 'Busy with other stuff '
    //   , function (evt, value) {
    //                      }
    //   , function () {  });
    alertify.confirm('End Call', 'Are you sure?', function () {
        $scope.IsManuallyEnded = true;
        hangup();
        window.location.replace("/video.html#/experience");
    }
        , function () { });
}

function hangup() {
    $scope.IsCallEnd = true;
    trace('Ending call');
    pc1.close();
    // pc2.close();
    pc1 = null;
    // pc2 = null;
    //hangupButton.disabled = true;
    // callButton.disabled = false;

    localStream.getTracks().forEach(function (track) {
        track.stop();
    });
    localVideo.src = "";
    remoteVideo.src = "";
    closeWS();
    // window.location.replace("/video.html#/experience");
}

$scope.dragOptions = {
    start: function (e) {
        console.log("STARTING");
    },
    drag: function (e) {
        console.log("DRAGGING");
    },
    stop: function (e) {
        console.log("STOPPING");
    },
    container: 'container'
}

SDP序列化后会有一些特殊字符进入。 所以要删除它使用下面的代码

// Workaround 
    function maybeAddLineBreakToEnd(sdp) {
        var endWithLineBreak = new RegExp(/\n$/);
        if (!endWithLineBreak.test(sdp)) {
            return sdp + '\n';
        }
        return sdp;
    }

function gotDescription(desc) {
            var offer = desc;
            offerSdpTextarea.value = desc.sdp;
            var sdp = offerSdpTextarea.value;
            sdp = maybeAddLineBreakToEnd(sdp);
            console.log(sdp);
            sdp = sdp.replace(/\n/g, '\r\n');
            offer.sdp = sdp;
            pc1.setLocalDescription(offer,
                onSetOfferSDPSuccess,
                onSetSDPError);
            trace('Modified Offer from localPeerConnection \n' + sdp);

            // sendWS(JSON.stringify({ "sdp": desc }));
            sendWS(JSON.stringify(offer));

   }