2016-09-24 1 views
0

私はリアルタイムのオーディオ/ビデオグループコールを実装しようとしていますが、今は2人の参加者にしか聞きたいことはありません。WebRTCエラー:リモートセッションの説明の作成に失敗しました。間違った状態で呼び出されました

私は実際に2つの異なるアカウントで同時にテストしていますが、いくつかのエラーメッセージが表示されますが、とにかく動作しますが、テストすると本当の異なるネットワークでの友人は、同じエラーメッセージが表示されますが、この場合には、我々は)聞いたり、お互いを見ることができません。

私は、Linux(Ubuntuの16.04)にクローム53を使用しています。

エラーメッセージがあります次のように、オファーを送信した同輩には、ブラウザコンソールに6つのエラーがあります。 1位:

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

第二、第三、第四、第五および:第六

addIceCandidate error: OperationError: Error processing ICE candidate 

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() 
    } 
    } 
} 

最後に、コードは、シグナリングに関連しています。しかし、最初に私はRailsの5と、このウェブサイトおよびActionCable経由WebSocketを持つシグナリングをやって明確にしたいので、チャネルのための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} 

とRubyの1(サーバー側):

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

多人数参加を希望する場合は、参加者のペアごとに1つのピア接続が必要です。あなたは現在、1つを使用しています。

公式のWebRTCサンプルからthis exampleを参照してください。

+0

回答ありがとうございました:) はい、私はすでに本でそれを見ましたが、私が今までに言ったように、私は2人の参加者が必要で、エラーは2だけでテストしています。より多くの人々のためにそれを作ってくれるでしょう。なぜなら、シグナリングサーバでは、より多くの人々のために用意されていますが、クライアント側ではまだ見えません。なぜなら、私がそれを2のために働かせなければ、より多くの合併症を追加する。 – Marta

+0

テストしたい場合、これは[ウェブサイト](https://lanformon.herokuapp.com/)です。これらのコールのいずれかに参加して2人で試すことができます(異なるブラウザまたは同じブラウザシークレットでは1つのウィンドウで2つのアカウントを使用できます)。私はちょうど2人のユーザーを作成しました。電子メールは "[email protected]"と "[email protected]"です。両方ともパスワード: "password" – Marta

+0

2人のユーザーを必要とするかどうかは関係ありません。 5.それぞれに1つのpeerConnectionが必要です。 –

関連する問題