但是当我把这些代码移到我们的实际应用程序中时,我还没有能够让它运行起来。当然代码必须得到显着改变 - 不同的后端,不同的框架和支持代码,支持多个同时连接,这类事情 - 但核心逻辑非常相似。但我无法让它发挥作用。
我在这里整理了一个示例应用程序来说明问题:
https://bitbucket.org/smithkl42/signalr.webrtc
核心WebRTC逻辑就在这个TypeScript文件中:
它有几百行,所以我不打算在这里发帖,但你可以点击上面的链接看到它。
运行时会产生如下输出:
12:17:58.531 WebRTCController.call():准备完成后调用7d9e0d39-5047-4afe-86e5-e6e01b9f5955
12:17:58.533 WebRTCController.prepareForCall():准备呼叫:localSessionId =' 39d2df53-6854-415a-8748-b5230eda2eb1&#39 ;; remoteSessionId =' 7d9e0d39-5047-4afe-86e5-e6e01b9f5955'
12:18:0.139对象。():用户已授予媒体设备访问权限,因此继续准备呼叫
12:18:0.141 Connection.createPeerConnection():创建对等连接;使用stunServer stun:stun1.l.google.com:19302
12:18:0.144():准备完成。创建和发送JSEP产品。 util.js中:21
12:18:0.272 Connection.handleIceCandidate():STUN服务器找到了一个ICE候选者(event.type =' icecandidate')。
12:18:0.282 Connection.handleIceCandidate():STUN服务器找到了一个ICE候选者(event.type =' icecandidate')。
(更像那样)
12:18:0.655 WebRTCController.handleJsepAnswer():从7d9e0d39-5047-4afe-86e5-e6e01b9f5955处理JsepAnswer
12:18:0.694对象。():将ICE候选者发送到远程机器:{" sdpMLineIndex":0," sdpMid":" audio" ,"候选人":" a =候选人:2999745851 1 udp 2113937151 192.168.56.1 62978 typ host generation 0 \ r \ n"}
12:18:0.706对象。():将ICE候选者发送到远程机器:{" sdpMLineIndex":0," sdpMid":" audio" ,"候选人":" a =候选人:2999745851 2 udp 2113937151 192.168.56.1 62978 typ host generation 0 \ r \ n"}
(更像那样)
但是它永远不会连接,即来自另一方的视频永远不会开始播放。在信令层,我可以通过日志和逐步通过代码看出第一个浏览器正在发送JSEP报价;第二个浏览器正在接收它,存储它并发送回适当的JSEP答案;并且第一台机器正在存储该答案。然后每个peerConnection找到ICE候选者并将它们发送到远程机器;并且每个peerConnection正在接收并且显然正在尝试那些ICE候选者;并且peerConnections甚至引发了onaddstream
事件。但视频从未开始播放。
peerConnection对象的状态如下所示:
(iceGatheringState=new; iceState=starting; readyState=active)
令人沮丧的是,每隔一段时间,也许是20次中的一次, 工作,即两个视频都显示出来。所以我没有所有错误。这听起来像是某种时间问题 - 但我无法弄清楚它是什么。据我所知,WebRTC对象(特别是RTCPeerConnection)中没有太多可以告诉你出了什么问题。
我不想让别人为我做故障排除,但是......好吧,我的选项已经用完了。有没有人看到我做错了什么?
更新2012-12-19 :我取得了一些进展。我意识到我正在同步调用peerConnection.setLocalDescription()
,即没有指定回调。所以现在我有一些看起来像这样的代码:
// Answer the call by sending a JsepAnswer message.
connection.peerConnection.createAnswer(
answer => {
connection.peerConnection.setLocalDescription(answer, () => {
var signalState: mData.SignalState = {
FromSessionId: connection.localSessionId,
ToSessionId: connection.remoteSessionId,
Message: JSON.stringify(answer)
};
me.roomHub.server.jsepAnswer(signalState);
mUtil.log("Sent JSEP answer: " + signalState.Message);
connection.readyForIceCandidates.resolve();
},
error => {
mUtil.error("Error setting local description from created answer: " + error + "; answer=" + JSON.stringify(answer));
});
},
error => {
mUtil.error("Error creating answer: " + error);
}, me.mediaConstraints);
setLocalDescription()
错误回调显示此错误:
16:14:42.439 WebRTCController.handleJsepOffer():从创建的答案设置本地描述时出错:SetLocalDescription失败。 answer = {" sdp":" v = 0 \ r \ no = - 439659381 2 IN IP4 127.0.0.1 \ r \ ns = - \ r \ nt = 0 0 \ r \ na = group:BUNDLE audio video \ r \ nn = msid-semantic:WMS u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf \ r \ nm = audio 1 RTP / SAVPF 103 104 111 0 8 107 106 105 13 126 \ r \ nc = IN IP4 0.0.0.0 \ r \ nn = rtcp:1 IN IP4 0.0.0.0 \ r \ nn = na = ice-ufrag:vOKflTJ56gV0R9i0 \ r \ nn = na = ice-pwd:9nuXPMDvQ2mZATFCQyEzPRQz \ r \ nn = sendrecv \ r \ n \ na = mid:audio \ r \ n = na = rtcp- mux \ r \ nNa = crypto:1 AES_CM_128_HMAC_SHA1_80 inline:m9q9pmLgLuFnfFC09KXKW5p8TjsKk + VdqX0OWv77 \ r \ na = rtpmap:103 ISAC / 16000 \ r \ na = rtpmap:104 ISAC / 32000 \ r \ nn = rtpmap:111 opus / 48000/2 \ r \ n = na = rtpmap:0 PCMU / 8000 \ r \ n = na = rtpmap:8 PCMA / 8000 \ r \ n = na = rtpmap:107 CN / 48000 \ r \ na = rtpmap:106 CN / 32000 \ r \ nn = rtpmap :105 CN / 16000 \ r \ n = na = rtpmap:13 CN / 8000 \ r \ nn = rtpmap:126电话事件/ 8000 \ r \ n = na = ssrc:548068416 cname:IXg8QRisWrd7 + 7f8 \ r \ na = ssrc:548068416 msid:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf a0 \ r \ na = ssrc:548068416 mslabel:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf \ r \ na = ssrc:548068416 label:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjfa0 \ r \ nm = video 1 RTP / S AVPF 100 116 117 \ r \ nc = IN IP4 0.0.0.0 \ r \ nn = rtcp:1 IN IP4 0.0.0.0 \ r \ n = na = ice-ufrag:vOKflTJ56gV0R9i0 \ r \ na = ice-pwd:9nuXPMDvQ2mZATFCQyEzPRQz \ r \ na = sendrecv \ r \ nn = mid:video \ r \ n = na = rtcp-mux \ r \ n \ nn = crypto:1 AES_CM_128_HMAC_SHA1_80 inline:m9q9pmLgLuFnfFC09KXKW5p8TjsKk + VdqX0OWv77 \ r \ na = rtpmap:100 VP8 / 90000 \ r \ na = rtpmap :116 red / 90000 \ r \ n = na = rtpmap:117 ulpfec / 90000 \ r \ n = na = ssrc:1460425980 cname:IXg8QRisWrd7 + 7f8 \ r \ na = ssrc:1460425980 msid:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf v0 \ r \ na = ssrc:1460425980 mslabel :u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf \ r \ n = na = ssrc:1460425980标签:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjfv0 \ r \ n","输入":"回答"}
现在我只需要弄清楚为什么直接来自createAnswer()
方法的特定SDP失败了。
更新2012-12-20 :我已在此处创建了问题的在线演示:http://srdemo.alanta.com/。我还打开了Chrome调试日志记录,结果我发现了一堆看起来像这样的错误:
[6584:7308:1220/091356:ERROR:rtc_peer_connection_handler.cc(84)] Native session description is null.
[6584:7308:1220/091356:ERROR:rtc_peer_connection_handler.cc(84)] Native session description is null.
[6584:7308:1220/091356:ERROR:rtc_peer_connection_handler.cc(84)] Native session description is null.
[6584:7308:1220/091356:ERROR:rtc_peer_connection_handler.cc(84)] Native session description is null.
[6584:7308:1220/091356:ERROR:rtc_peer_connection_handler.cc(84)] Native session description is null.
不确定他们与我的问题有什么关系,但我还在继续调查。
*编辑2012-12-20:我已经管理(我认为)以缩小问题范围。有关更多详细信息,请参阅this question。
答案 0 :(得分:4)
想出来。事实证明,SignalR 1.0 RC1中有一个错误,它将字符串中的任何“+”更改为空格。因此,SDP中的行看起来像这样:
a=ice-pwd:qZFVvgfnSso1b8UV1SUDd2+z
变成了这个:
a=ice-pwd:qZFVvgfnSso1b8UV1SUDd2 z
但是因为不是每个SDP在关键线上都有“+”,所以有时它会起作用。一切都解释了。
已向使用SignalR的好人报告了该错误(请参阅https://github.com/SignalR/SignalR/issues/1194),与此同时,围绕相关字符串的简单encodeURIComponent()
和decodeURIComponent()
修复了该错误。