【问题标题】:WebRTC RTCPeerConnection not establishedWebRTC RTCPeerConnection 未建立
【发布时间】:2016-08-13 14:09:09
【问题描述】:

我的简单 WebRTC javascript 代码无法按预期工作。事实上,音频通话并没有建立(请注意,我对 WebRTC 的了解最少,我是通过查看互联网上的示例来创建的)。该页面应在两个参与者之间发起音频呼叫。作为参与者之间的信号服务器,我使用了 websocket 服务器。该服务器仅在参与者之间中继消息。在呼叫发起期间,真正通过 websocket 发送消息(一个报价、多个候选人、一个答案和其他候选人)。
尽管如此,Firefox 还是给了我“ICE 失败,请参阅 about:webrtc 了解更多详细信息”。两个参与者都在普通路由器后面。
我会尽快添加我的 websocket-server 的日志和 about:webrtc 的示例(当然缩短了)。

为什么这段代码不起作用?我忽略了什么?

我的代码是(请记住,这只适用于 Firefox):

ws = new WebSocket("ws://" + location.hostname + ":9000");
navigator.getUserMedia = function(a, b){ return navigator.mozGetUserMedia(a, b, error);};
offerOptions = {offerToRecieveAudio: 1, offerToRecieveVideo: 1};

var pc = new RTCPeerConnection({"iceServers": [
{url:'stun:stun.l.google.com:19302'},
{url:'stun:stunserver.org'},
]});
pc.onaddstream = function(obj) {
  if (obj.stream instanceof LocalMediaStream) return;
  var audio = document.createElement("audio");
  audio.controls = "true";
  audio.autoplay = "true";
  document.body.appendChild(audio);
  audio.srcObject = obj.stream;
}
pc.onicecandidate = function(evt){
        if (!evt.candidate) return;
        console.log(evt.candidate);
        ws.send(JSON.stringify(evt.candidate));
}

// Helper functions
function endCall() {
  var audios = document.getElementsByTagName("audio");
  for (var i = 0; i < audios.length; i++) {
    audios[i].pause();
  }

  pc.close();
}

function error(err) {
  endCall();
}

function startCall(){
        navigator.getUserMedia({audio: true}, function(stream) {
        pc.onaddstream({stream: stream});
       pc.addStream(stream);

        pc.createOffer(function(offer) {
                pc.setLocalDescription(new RTCSessionDescription(offer),function() {
                ws.send(JSON.stringify(offer));
                }, error, offerOptions);
        }, error);
        });
}

ws.onmessage = function(message){
        var m = JSON.parse(message.data);
        console.log(m);

        if (m.type){
                if (m.type == "offer"){
                        navigator.getUserMedia({audio: true}, function(stream) {
                        pc.onaddstream({stream: stream});
                        pc.addStream(stream);

                        pc.setRemoteDescription(new RTCSessionDescription(m), function() {
                                pc.createAnswer(function(answer) {
                                pc.setLocalDescription(new RTCSessionDescription(answer), function() {
                                        ws.send(JSON.stringify(answer));
                                }, error);
                                }, error, offerOptions);
                        }, error);
                        });
                 }
                if (m.type == "answer"){
                        pc.setRemoteDescription(new RTCSessionDescription(m), function() { }, error);
                }
        }
        if (m.candidate){
                pc.addIceCandidate(new RTCIceCandidate(m));
        }
};

要发起呼叫,您应该只调用正确命名的startCall()-函数。

两个之间的websocket-communication(ip被删除):

Client connecting: tcp:ip1:62322
Client connecting: tcp:ip2:50075
Text message received: {"type":"offer","sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-45.0.2 8467526262723029465 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 8E:FC:F3:42:12:68:95:13:98:CC:B0:8D:41:F6:4E:39:19:60:70:5A:4B:4A:9D:93:4C:A0:53:CF:58:AB:3F:A1\r\na=ice-options:trickle\r\na=msid-semantic:WMS *\r\nm=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8\r\nc=IN IP4 0.0.0.0\r\na=sendrecv\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=ice-pwd:0a7123357b97345c6f9a9474aabf0c27\r\na=ice-ufrag:5d02cec2\r\na=mid:sdparta_0\r\na=msid:{512da0cd-689a-4981-9d88-9e857ba62803} {f7e81293-42b4-44e1-9d5b-80afe2857cf8}\r\na=rtcp-mux\r\na=rtpmap:109 opus/48000/2\r\na=rtpmap:9 G722/8000/1\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=setup:actpass\r\na=ssrc:2285633480 cname:{90d0392b-c8f1-4be9-af57-8c34d08567cd}\r\n"}
Text message received: {"candidate":"candidate:0 1 UDP 2122187007 localip2 51858 typ host","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:7 1 UDP 2122252543 ipv6-2 51859 typ host","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:0 2 UDP 2122187006 ip2 51860 typ host","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:7 2 UDP 2122252542 ipv6-2 52724 typ host","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:2 1 UDP 1685987327 ip2 51858 typ srflx raddr localip2 rport 51858","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:2 2 UDP 1685987326 ip2 51860 typ srflx raddr localip2 rport 51860","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"type":"answer","sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-45.0.2 1868980908691513816 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 04:2A:EB:AD:90:95:A3:A8:B8:3A:76:FE:3A:E7:DA:1F:D6:77:30:8A:87:BB:B9:3A:30:B4:9B:3D:E5:8F:58:04\r\na=ice-options:trickle\r\na=msid-semantic:WMS *\r\nm=audio 9 UDP/TLS/RTP/SAVPF 109\r\nc=IN IP4 0.0.0.0\r\na=sendrecv\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=ice-pwd:b5665ac44d79e66c392408e08bdc32ee\r\na=ice-ufrag:1a8f59a4\r\na=mid:sdparta_0\r\na=msid:{358aa02b-b035-41a8-acf6-2b787a192c60} {28e8765a-4a56-42e3-8f45-897ca7c99c05}\r\na=rtcp-mux\r\na=rtpmap:109 opus/48000/2\r\na=setup:active\r\na=ssrc:3277858225 cname:{8cee2fed-1a9e-46b9-90d7-3cad80050f3b}\r\n"}
Text message received: {"candidate":"candidate:0 1 UDP 2122252543 localip1 59706 typ host","sdpMid":"sdparta_0","sdpMLineIndex":0}
Text message received: {"candidate":"candidate:1 1 UDP 1686052863 ip1 59706 typ srflx raddr localip1 rport 59706","sdpMid":"sdparta_0","sdpMLineIndex":0}

【问题讨论】:

    标签: javascript html webrtc audio-streaming


    【解决方案1】:

    改变这一行:

        if (m.candidate){
                pc.addIceCandidate(new RTCIceCandidate(m));
        }
    

    到:

        if (m.candidate){
                pc.addIceCandidate(new RTCIceCandidate(m.candidate))
                .catch(e => console.error(e));
        }
    

    一般来说,你做错的不是像err 这样记录错误。变化:

    function error(err) {
      endCall();
    }
    

    到:

    function error(err) {
      console.error(err);
      endCall();
    }
    

    浏览器试图帮助你,并且经常会告诉你哪里出了问题。这样,下次出现问题时,您就不必再询问 SO。

    更新:

    InvalidStateError: Cannot add ICE candidate in state stable 表示被调用者在收到报价之前正在接收候选人。这是 WebRTC 的时间敏感部分。一旦在调用者端调用了setLocalDescription,ice 候选者就开始流动。例如电线看起来像这样:

    offer, candidate, candidate, candidate
    

    因此,在接收端,您应该立即致电setRemoteDescription,否则该对等连接尚未准备好接收候选人。您的代码正在等待getUserMedia,是问题所在。

    将您的代码更改为在getUserMedia 之前调用setRemoteDescription,它应该会更好地工作。

    【讨论】:

    • 你完全正确,我应该从一开始就这样做。但我仍然需要SO的帮助。在您进行第一次改进之后,一个异常会在被调用者的控制台中记录两次。 DOMException: [InvalidStateError: Cannot add ICE candidate in state stable, code: 11, nsresult: 0x8053000b]
    • @Sirac 我已更新答案以解决您看到的错误。希望它有所帮助。
    • 您的更新对我帮助很大。非常感谢你。在实际使用offer 生成答案和设置描述之前,我一直在尝试addIceCandidate。关于如何为我处理 ICE/Signalling/PeerConnection 的长期课程。
    • 我的总结:首先从A得到offer,在B上设置为remoteDescription,现在在B上创建answer并设置为localDescription,发送这个localD的B到 A 并将其设置为 A 的 remoteDescription。对你们所有人来说可能都很明显,但我在没有这些知识的情况下挣扎。 offer 具有 A 的所有可用编解码器等信息。收到此 B 后,将通过列表并通过其 answer 生成兼容列表。在两端设置好这些描述后,执行addIceCandidates,聚会开始。
    • @Nein 很棒的总结,除了最后一句话。 ICE 候选者通常会在双方的提议/答案交换的同时开始滴流,只要在一方设置了本地描述。 IE。期望pc.onicecandidate 在您的setLocalDescription 成功回调之后立即开始触发。例如。您想在您的信号通道上看到offer、candidate、candidate、canddiate...,反之亦然。
    【解决方案2】:

    您尚未配置任何 TURN 服务器,因此您看到候选人列表中没有中继候选人。很可能两个参与者处于无法使用 WebRTC 进行点对点的场景中,这需要中继连接。因此配置 TURN 服务器可能会解决您的问题。要验证是否是这种情况,您可以尝试在两个参与者都位于同一子网或网络之后进行呼叫,在该子网或网络中应该可以进行点对点,如果可行,那么您的代码是好的,配置 TURN 服务器将解决您的问题。

    【讨论】:

    • 我记得用不同的配置测试过它,其中一个包含 TURN 服务器。此外,调用自己(从不同的选项卡)也可以正常工作。我将再次使用 TURN-servers 对其进行测试。
    • 昨天我在没有 TURN 服务器的情况下对其进行了测试,它神奇地工作(不要问我为什么)。今天,在相同的网络和相同的浏览器下使用相同的计算机,它不再工作了......
    • @jib 的回答也帮助我解决了我的问题。需要注意的事实是,当我收到错误消息时,我也配置了 TURN 服务器:“无法在稳定状态下添加 ICE 候选者”。
    猜你喜欢
    • 2021-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-08
    • 1970-01-01
    • 1970-01-01
    • 2019-05-23
    • 1970-01-01
    相关资源
    最近更新 更多