2016-09-24 204 views
0

我試圖實現一個實時音頻/視頻組呼叫,但現在我只想讓它只有兩個參與者。WebRTC錯誤:無法創建遠程會話描述。調用狀態錯誤

它不工作,我不明白爲什麼:(實際上當我用兩個不同的帳戶在同一時間測試它時,我看到一些錯誤消息,但它仍然有效,但是當我測試它時在現實不同網絡的朋友,相同的錯誤消息出現,但在這種情況下,我們無法聽到或看到對方)。

我使用Chrome 53在Linux(Ubuntu的16.04)。

錯誤消息對於發送報價的同行來說,瀏覽器控制檯中有6個錯誤: 1st:

Failed to create remote session description: OperationError: Failed to set remote offer sdp: Called in wrong state: STATE_SENTOFFER 

第2,第3,第4和第5:

addIceCandidate error: OperationError: Error processing ICE candidate 

6:

Failed to set local session description: OperationError: CreateAnswer failed because remote_description is not an offer 

而對於誰收到的報價和發送,並回答有1個錯誤的瀏覽器控制檯同行:

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

在你想看到控制檯中的所有消息的情況下,在誰發送的報價在同行中的人在這裏:WebRTC error from offer peer。其他同級瀏覽器控制檯中的人員位於:WebRTC error from answer peer

,在HTML文件中重要的代碼如下(有使用JavaScript代碼的其他文件,我稍後會顯示):

<div class='row'> 
    <div class='col-xs'> 
    <div class='box center-xs middle xs'> 
     <h1>Call CallNameExample</h1> 
    </div> 
    </div> 
</div> 
<div class='row'> 
    <div class='col-xs'> 
    <div class='box center-content'> 
     <button class='btn btn-info btn-37 no-padding circle' id='btnChangeCamStatus'> 
     <i class='material-icons' id='iconCamOff'> 
      videocam_off 
     </i> 
     <i class='material-icons hidden' id='iconCamOn'> 
      videocam 
     </i> 
     </button> 
     <button class='btn btn-info btn-37 no-padding circle' id='btnChangeMicStatus'> 
     <i aria-hidden='true' class='fa fa-microphone-slash' id='iconMicOff'></i> 
     <i aria-hidden='true' class='fa fa-microphone hidden' id='iconMicOn'></i> 
     </button> 
    </div> 
    </div> 
</div> 
<div class='row'> 
    <div class='col-xs'> 
    <div class='box center-xs middle xs'> 
     <video autoplay height='200px' id='bigRemoteVideo' width='200px'></video> 
    </div> 
    </div> 
</div> 
<script> 
    var room = "1" 
    var localVideo = document.getElementById("localVideo") 
    var bigRemoteVideo = document.getElementById("bigRemoteVideo") 

    document.getElementById("btnChangeCamStatus").addEventListener("click", function() { 
    if (localStream.getVideoTracks()[0].enabled) { 
     disableCam() 
     $("#iconCamOff").addClass("hidden") 
     $("#iconCamOn").removeClass("hidden") 
    } else { 
     enableCam() 
     $("#iconCamOff").removeClass("hidden") 
     $("#iconCamOn").addClass("hidden") 
    } 
    }, false); 
    document.getElementById("btnChangeMicStatus").addEventListener("click", function() { 
    if (localStream.getAudioTracks()[0].enabled) { 
     disableMic() 
     $("#iconMicOff").addClass("hidden") 
     $("#iconMicOn").removeClass("hidden") 
    } else { 
     enableMic() 
     $("#iconMicOff").removeClass("hidden") 
     $("#iconMicOn").addClass("hidden") 
    } 
    }, false); 

    function setLocalVideo(stream) { 
    localVideo.src = window.URL.createObjectURL(stream) 
    } 

    function setRemoteVideo(stream) { 
    bigRemoteVideo.src = window.URL.createObjectURL(stream) 
    } 

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

    bigRemoteVideo.addEventListener('loadedmetadata', function() { 
    console.log('Remote video videoWidth: ' + this.videoWidth + 
     'px, videoHeight: ' + this.videoHeight + 'px'); 
    }); 

    // Starts the party: 
    (function(){ 
    enableUserMedia() 

    window.createOrJoin(room) 
    console.log("Attempted to create or join room: " + room) 

    }()) 
</script> 

的其他JavaScript文件包含一個代碼(所有文件一起在這裏):

var localStream 
var mediaConstraints = {video: true, audio: true} 

function enableUserMedia(){ 
    console.log('Getting user media with constraints', mediaConstraints); 
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia 

    if (navigator.getUserMedia) { 
    navigator.getUserMedia(mediaConstraints, gotStream, gotError) 
    } 

    window.URL = window.URL || window.webkitURL 

    function gotStream(stream) { 
    console.log('Adding local stream.'); 
    setLocalVideo(stream) 
    localStream = stream; 
    //sendMessage('got user media'); 
    console.log('got user media'); 
    attachLocalMedia(); 
    } 
    function gotError(error) { 
    console.log("navigator.getUserMedia error: ", error); 
    } 
} 

function disableCam(){ 
    localStream.getVideoTracks()[0].enabled = false 
} 

function disableMic(){ 
    localStream.getAudioTracks()[0].enabled = false 
} 

function enableCam(){ 
    localStream.getVideoTracks()[0].enabled = true 
} 

function enableMic(){ 
    localStream.getAudioTracks()[0].enabled = true 
} 

function disableUserMedia(){ 
    localStream.getVideoTracks()[0].stop(); 
    localStream.getAudioTracks()[0].stop(); 
} 

window.onbeforeunload = function() { 
    sendMessage("bye"); 
}; 

function hangup() { 
    console.log("Hanging up."); 
    stop(); 
    sendMessage("bye"); 
} 

function handleRemoteHangup() { 
    console.log("Session terminated."); 
    stop(); 
} 

function stop() { 
    disableUserMedia(); 
    pc.close(); 
    console.log("PC STATE: " + pc.signalingState || pc.readyState); 
    console.log("PC ICE STATE: " + pc.iceConnectionState) 
    pc = null; 
} 

var isInitiator = false 
var justJoinedRoom = false 

var sdpConstraints = { // Set up audio and video regardless of what devices are present. 
    'mandatory': { 
    'OfferToReceiveAudio': true, 
    'OfferToReceiveVideo': true 
    } 
} 

function sendMessage(message){ 
    App.call.message(message); 
} 

function doCall() { 
    console.log("Sending offer to peer"); 
    pc.createOffer(sdpConstraints) 
    .then(setLocalAndSendMessage) 
    .catch(handleCreateOfferError); 
    //pc.createOffer(setLocalAndSendMessage, handleCreateOfferError); 
} 

function doAnswer() { 
    console.log("Sending answer to peer."); 
    pc.createAnswer() 
    .then(setLocalAndSendMessage) 
    .catch(onSetLocalSessionDescriptionError); 
} 

function setLocalAndSendMessage(sessionDescription) { 
    console.log("setLocalAndSendMessage sending message" + JSON.stringify(sessionDescription)); 
    pc.setLocalDescription(sessionDescription) 
    .then(
     function(){ 
     onSetLocalSuccess(); 
     sendMessage(sessionDescription); 
     } 
    ) 
    .catch(onSetLocalSessionDescriptionError); 
} 

function onSetLocalSuccess() { 
    console.log('setLocalDescription complete'); 
} 

function onSetRemoteSuccess() { 
    console.log('setRemoteDescription complete'); 
    doAnswer(); 
} 

function onSetLocalSessionDescriptionError(error) { 
    console.error('Failed to set local session description: ' + error.toString()) 
} 

function handleCreateOfferError(event) { 
    console.error("createOffer() error: " + JSON.stringify(event)) 
} 

function onSetRemoteSessionDescriptionError(error) { 
    console.error("Failed to create remote session description: " + error.toString()) 
} 

function handleReceivedOffer(message) { 
    console.log("handleReceivedOffer: " + JSON.stringify(message)); 
    pc.setRemoteDescription(new RTCSessionDescription(message)) 
    .then(onSetRemoteSuccess) 
    .catch(onSetRemoteSessionDescriptionError) 
} 
function handleReceivedAnswer(message) { 
    console.log("handleReceivedAnswer: " + JSON.stringify(message)); 
    pc.setRemoteDescription(new RTCSessionDescription(message)) 
    .then(onSetRemoteSuccess) 
    .catch(onSetRemoteSessionDescriptionError) 
} 
function handleReceivedCandidate(label, candidate) { 
    pc.addIceCandidate(
    new RTCIceCandidate({ 
     sdpMLineIndex: label, 
     candidate: candidate 
    }) 
).then(successAddingIceCandidate).catch(errorAddingIceCandidate) 
} 

function successAddingIceCandidate() { console.log("addIceCandidate successfully") } 
function errorAddingIceCandidate(error) { console.error("addIceCandidate error: " + error.toString()) } 

var remoteStream 
var pc 
var pcConfig = { 
    'iceServers': [{ 
    'url': 'stun:stun.l.google.com:19302' 
    }, { 
    'url': 'turn:192.158.29.39:3478?transport=udp', 
    'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=', 
    'username': '28224511:1379330808' 
    }] 
} 

function connectionStateCallback(){ 
    var state; 
    if (pc) { 
    state = pc.connectionState 
    console.log("PC CONNECTION state change callback, state: " + state) 
    } 
} 

function signalingStateCallback() { 
    var state; 
    if (pc) { 
    state = pc.signalingState || pc.readyState; 
    console.log("PC SIGNALING state change callback, state: " + state); 
    } 
} 
function iceStateCallback() { 
    var iceState; 
    if (pc) { 
    iceState = pc.iceConnectionState; 
    console.log('PC ICE connection state change callback, state: ' + iceState); 
    } 
} 

function createPeerConnection() { 
    try { 
    pc = new RTCPeerConnection(pcConfig); 
    signalingStateCallback(); 
    pc.onsignalingstatechange = signalingStateCallback; 
    console.log("PC ICE STATE: " + pc.iceConnectionState); 
    pc.oniceconnectionstatechange = iceStateCallback; 
    pc.onconnectionstatechange = connectionStateCallback; 
    pc.onicecandidate = handleIceCandidate; 
    pc.onaddstream = handleRemoteStreamAdded; 
    pc.onremovestream = handleRemoteStreamRemoved; 
    console.log('Created RTCPeerConnnection'); 
    attachLocalMedia(); 
    } catch (e) { 
    console.error("Failed to create PeerConnection, exception: " + e.toString()) 
    return; 
    } 
} 

function handleIceCandidate(event) { 
    console.log("icecandidate event: " + JSON.stringify(event)); 
    if (event.candidate) { 
    sendMessage({ 
     type: "candidate", 
     label: event.candidate.sdpMLineIndex, 
     id: event.candidate.sdpMid, 
     candidate: event.candidate.candidate 
    }); 
    } else { 
    console.log("End of candidates."); 
    } 
} 

function handleRemoteStreamAdded(event) { 
    console.log("Remote stream added."); 
    setRemoteVideo(event.stream); 
    remoteStream = event.stream; 
} 

function handleRemoteStreamRemoved(event) { //In real life something should be done here but since the point of this website is to learn, this function is not a priority right now. 
    console.log("Remote stream removed. Event: " + event); 
} 

function attachLocalMedia() { 
    if (pc && localStream) { 
    pc.addStream(localStream) 
    console.log("Added localStream to pc") 
    if (justJoinedRoom) { 
     console.log("call to DOCALL() from attachLocalMedia()") 
     doCall() 
    } 
    } 
} 

最後是代碼相關的信號。但首先,我想澄清一下,我做這個網站,導軌5和通過ActionCable的WebSockets的信號,因此對於通道的CoffeeScript文件(客戶端)是這一個:

window.createOrJoin = (roomID) -> 
    App.call = App.cable.subscriptions.create { channel: "CallChannel", room: roomID }, 
    connected: -> 
     # Called when the subscription is ready for use on the server 
     createPeerConnection() 

    disconnected: -> 
     # Called when the subscription has been terminated by the server 

    received: (data) -> 
     # Called when there's incoming data on the websocket for this channel 
     if (data["kindOfData"] == "created") 
     console.log('Created room ' + data["room"]) 
     window.isInitiator = true # ESTO ME SIRVE SOLO PARA 2 PERSONAS!! # CREO QUE YA NI LO USO 
     attachLocalMedia() 
     else if (data["kindOfData"] == "full") 
     console.log('Room ' + data["room"] + ' is full') 
     else if (data["kindOfData"] == "join") 
     console.log('Another peer made a request to join room ' + data["room"]) 
     console.log('This peer is the initiator of room ' + data["room"] + '!') 
     window.justJoinedRoom = false 
     else if (data["kindOfData"] == "joined") 
     console.log('joined: ' + data["room"]) 
     window.justJoinedRoom = true 
     attachLocalMedia() 
     else if (data["kindOfData"] == "log") 
     console.log(data["info"]) 
     else if (data["kindOfData"] == "message") # This client receives a message 
     console.log("Client received message: " + JSON.stringify(data["message"])); 
     if (data["message"] == "bye") 
      handleRemoteHangup() 
     else if (data["message"]["type"] == "offer") 
      handleReceivedOffer(data["message"]) # obj with "type" and "sdp" 
     else if (data["message"]["type"] == "answer") 
      handleReceivedAnswer(data["message"]) # obj with "type" and "sdp" 
     else if (data["message"]["type"] == "candidate") 
      handleReceivedCandidate(data["message"]["label"], data["message"]["candidate"]) 


    message: (data) -> 
     console.log("Client sending message: " + JSON.stringify(data)); 
     @perform "message", {message: data, room: roomID} 

和紅寶石一(在服務器端):

class CallChannel < ApplicationCable::Channel 
    def subscribed # Action automatically called when a client is subscribed to the channel 
    stream_from "calls" # calls is a channel in common for everyone # ONLY FOR TESTING!!! 
    stream_from "calls_room#{params[:room]}_person#{current_user.id}" 
    @@hashUsersByRoom ||= Hash.new() # { |h,k| h[k] = Set.new } 
    @@hashRoomsByUser ||= Hash.new() # { |h,k| h[k] = Set.new } 
    result = createOrJoin(params[:room]) 
    end 

    def unsubscribed 
    # Any cleanup needed when channel is unsubscribed 
    end 

    def message(data) 
    if data["message"].eql? "bye" 
     if @@hashUsersByRoom[ data["room"] ] && @@hashUsersByRoom[ data["room"] ].include?(current_user.id) 
     @@hashUsersByRoom[ data["room"] ].delete(current_user.id) 
     if @@hashUsersByRoom[ data["room"] ].length() == 0 
      @@hashUsersByRoom.delete(data["room"]) 
      Call.find(data["room"]).update_column("active", false) 
     end 
     end 
     if @@hashRoomsByUser[ current_user.id ] && @@hashRoomsByUser[ current_user.id ].include?(data["room"]) 
     @@hashRoomsByUser[ current_user.id ].delete(data["room"]) 
     if @@hashRoomsByUser[ current_user.id ].length() == 0 
      @@hashRoomsByUser.delete(current_user.id) 
     end 
     end 
    end 
    ActionCable.server.broadcast "calls_room#{data["room"]}", kindOfData: "log", info: "Client #{current_user.id} said: #{data["message"]}" 
    ActionCable.server.broadcast "calls_room#{data["room"]}", kindOfData: "message", message: data["message"] 
    end 

    private 

    def createOrJoin(room) 
     ActionCable.server.broadcast "calls", kindOfData: "log", info: "Received request to create or join room #{room}" 
     @@hashUsersByRoom[room] ||= Set.new() 
     ActionCable.server.broadcast "calls", kindOfData: "log", info: "Room #{room} now has #{@@hashUsersByRoom[room].length()} + client(s)" 
     if @@hashUsersByRoom[room].length == 0 
     stream_from "calls_room#{room}" # Join the room 
     @@hashUsersByRoom[ room ] << current_user.id 
     @@hashRoomsByUser[ current_user.id ] ||= Set.new() 
     @@hashRoomsByUser[ current_user.id ] << room 
     ActionCable.server.broadcast "calls", kindOfData: "log", info: "Client ID #{current_user.id} created room #{room}" 
     ActionCable.server.broadcast "calls_room#{room}_person#{current_user.id}", kindOfData: "created", room: room, user: current_user.id 
     Call.find(room).update_column("active", true) 
     elsif (@@hashUsersByRoom[room].length() < Call.where(:id => room).pluck(:maximumNumberOfParticipants)[0]) || (@@hashUsersByRoom[ data["room"] ].include?(current_user.id)) 
     ActionCable.server.broadcast "calls", kindOfData: "log", info: "Client ID #{current_user.id} joined room #{room}" 
     ActionCable.server.broadcast "calls_room#{room}", kindOfData: "join", room: room 
     stream_from "calls_room#{room}" # Join the room 
     @@hashUsersByRoom[ room ] << current_user.id 
     @@hashRoomsByUser[ current_user.id ] ||= Set.new() 
     @@hashRoomsByUser[ current_user.id ] << room 
     ActionCable.server.broadcast "calls_room#{room}_person#{current_user.id}", kindOfData: "joined", room: room, user: current_user.id 
     ActionCable.server.broadcast "calls_room#{room}", kindOfData: "ready" 
     else # full room 
     ActionCable.server.broadcast "calls_room#{room}_person#{current_user.id}", kindOfData: "full", room: room 
     end 
    end 

end 

搜索在互聯網上,我看到有類似問題的人,但每個人是出於不同的原因,其中沒有一個是我的情況是有用的,但我看到的地方,「STATE_INPROGRESS」手段「提供/答覆交換完成「,因此我無法理解提供/答覆交換是否完成......當我嘗試向我們提供時,爲什麼它不起作用與朋友一起嗎?爲什麼它要在這種情況下設置更多的遠程會話描述(當提議/應答交換應該完成時)? 所以基本上我的主要問題是:發生了什麼,我該如何解決?

如果您已經達到了這個問題的部分,謝謝,我很感激! :)

回答

0

如果你想做多方,你需要每對參與者一個對等連接。你目前正在使用一個。

請參閱官方WebRTC示例中的this example

+0

感謝您的回答:) 是的,我已經在一本書中看到過,但正如我之前所說的,現在我只想讓它參與2個參與者,並且錯誤僅用2來測試。將爲更多的人提供服務,這就是爲什麼你可以在信令服務器中看到爲更多人準備的信息,但不是在客戶端,因爲如果我沒有讓它工作到2個,那就不是這個時刻以增加更多的併發症。 – Marta

+0

如果你想測試它,這是[網站](https://lanformon.herokuapp.com/),你可以加入任何這些電話,並嘗試2人(使用不同的瀏覽器或使用相同的但隱身窗口中的一個窗口,因此您可以使用2個帳戶)。我剛剛爲SO創建了兩個用戶,電子郵件爲「[email protected]」和「[email protected]」,均使用密碼:「password」 – Marta

+0

我認爲如果您想要2個用戶或5.每一個你需要一個peerConnection。 –

相關問題