1

I'm pulling my hair out with this one. A month or so ago, I was able to put together a proof-of-concept WebRTC demo, using some sample code from the good folks at SignalR. The demo is located here, the source for it is here, and it does what it's supposed to do.

But when I took that code and moved it into our actual application, I haven't been able to get it to work. Of course the code had to be changed significantly - different backends, different set of frameworks and supporting code, supporting multiple simultaneous connections, that sort of thing - but the core logic is very similar. But I can't get it to work.

I've put together a sample app here that demonstrates the problem:

https://bitbucket.org/smithkl42/signalr.webrtc

The core WebRTC logic is all in this TypeScript file:

https://bitbucket.org/smithkl42/signalr.webrtc/src/tip/SignalR.WebRTC/Scripts/Media/WebRTC.ts?at=default

It's several hundred lines long, so I won't bother posting it here, but you can see it by clicking on the link above.

When it runs, it produces output like this:

12:17:58.531 WebRTCController.call(): Calling 7d9e0d39-5047-4afe-86e5-e6e01b9f5955 when preparations have finished

12:17:58.533 WebRTCController.prepareForCall(): Preparing for call: localSessionId='39d2df53-6854-415a-8748-b5230eda2eb1'; remoteSessionId='7d9e0d39-5047-4afe-86e5-e6e01b9f5955'

12:18:0.139 Object.(): The user has granted media device access, so proceeding to prepare for call

12:18:0.141 Connection.createPeerConnection(): Creating peer connection; using stunServer stun:stun1.l.google.com:19302

12:18:0.144 (): Preparations finished. Creating and sending JSEP offer. Util.js:21

12:18:0.272 Connection.handleIceCandidate(): STUN server has found an ICE candidate (event.type='icecandidate').

12:18:0.282 Connection.handleIceCandidate(): STUN server has found an ICE candidate (event.type='icecandidate').

(More like that)

12:18:0.655 WebRTCController.handleJsepAnswer(): Handling JsepAnswer from 7d9e0d39-5047-4afe-86e5-e6e01b9f5955

12:18:0.694 Object.(): Sending ICE candidate to the remote machine: {"sdpMLineIndex":0,"sdpMid":"audio","candidate":"a=candidate:2999745851 1 udp 2113937151 192.168.56.1 62978 typ host generation 0\r\n"}

12:18:0.706 Object.(): Sending ICE candidate to the remote machine: {"sdpMLineIndex":0,"sdpMid":"audio","candidate":"a=candidate:2999745851 2 udp 2113937151 192.168.56.1 62978 typ host generation 0\r\n"}

(More like that)

But then it never connects, i.e., the video from the other side never starts playing. At the signaling layer, I can tell by the logs and by stepping through the code that the first browser is sending a JSEP offer; the second browser is receiving it, storing it and sending back an appropriate JSEP answer; and the first machine is storing that answer. Each peerConnection is then finding the ICE candidates and sending them to the remote machine; and each peerConnection is receiving and apparently trying those ICE candidates; and the peerConnections are even raising the onaddstream event. But the video never starts playing.

The state of the peerConnection object all the way through looks like this:

(iceGatheringState=new; iceState=starting; readyState=active)

The frustrating bit is that every so often, maybe one time out of 20, it does work, i.e., both videos show up. So I'm not doing everything wrong. It sounds like a timing issue of some sort - but I can't figure out what it is. And so far as I can tell, there's not much in the WebRTC objects (specifically RTCPeerConnection) to tell you what's going wrong.

I hate to ask anybody else to do my troubleshooting for me, but... well, I'm running out of options. Does anybody else see anything I'm doing obviously wrong?

Update 2012-12-19: I'm making some progress. I realized I was calling peerConnection.setLocalDescription() synchronously, i.e., without specifying callbacks. So now I've got some lines of code that look like this:

// 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);

And the setLocalDescription() error callback is showing this error:

16:14:42.439 WebRTCController.handleJsepOffer(): Error setting local description from created answer: SetLocalDescription failed.; 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\na=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\na=rtcp:1 IN IP4 0.0.0.0\r\na=ice-ufrag:vOKflTJ56gV0R9i0\r\na=ice-pwd:9nuXPMDvQ2mZATFCQyEzPRQz\r\na=sendrecv\r\na=mid:audio\r\na=rtcp-mux\r\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:m9q9pmLgLuFnfFC09KXKW5p8TjsKk+VdqX0OWv77\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:111 opus/48000/2\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:107 CN/48000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:126 telephone-event/8000\r\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/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=ice-ufrag:vOKflTJ56gV0R9i0\r\na=ice-pwd:9nuXPMDvQ2mZATFCQyEzPRQz\r\na=sendrecv\r\na=mid:video\r\na=rtcp-mux\r\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:m9q9pmLgLuFnfFC09KXKW5p8TjsKk+VdqX0OWv77\r\na=rtpmap:100 VP8/90000\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\na=ssrc:1460425980 cname:IXg8QRisWrd7+7f8\r\na=ssrc:1460425980 msid:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf v0\r\na=ssrc:1460425980 mslabel:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjf\r\na=ssrc:1460425980 label:u9fhVrWeLLweqb5ubLkw61Ijsh6BM6vZLhjfv0\r\n","type":"answer"}

Now I just need to figure out why that particular SDP - which comes straight from the createAnswer() method - is failing.

Update 2012-12-20: I've created an online demonstration of the problem here: http://srdemo.alanta.com/. I've also turned on Chrome debug logging, with the result that I see a bunch of errors that look like this:

[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.

Not sure what relationship they have to my problem, but I'm continuing to look into it.

*Edit 2012-12-20: I've managed (I think) to narrow the problem down. See this question for more precise details.

Community
  • 1
  • 1
Ken Smith
  • 20,305
  • 15
  • 100
  • 147

1 Answers1

4

Figured it out. Turns out that SignalR 1.0 RC1 has a bug in it that changes any "+" in a string into a space. So lines in the SDP that looked like this:

a=ice-pwd:qZFVvgfnSso1b8UV1SUDd2+z

Were getting changed into this:

a=ice-pwd:qZFVvgfnSso1b8UV1SUDd2 z

But because not every SDP had a "+" in it on a critical line, sometimes it would work. Everything explained.

The bug has been reported to the good folks working on SignalR (see https://github.com/SignalR/SignalR/issues/1194), and in the meantime, a simple encodeURIComponent() and decodeURIComponent() around the strings in question fixed it.

Ken Smith
  • 20,305
  • 15
  • 100
  • 147
  • I need to create the webrtc for android.is it possible to create webrtc in android? If possible can you share some knowledge with us regarding webrtc.as it have limited link in google regarding webrtc in android – ios developer Jul 18 '13 at 06:38
  • @IOSDeveloper - Yes, it's possible to use WebRTC in Android, in at least two ways: directly through the SDK, and indirectly through the latest version of the Google Chrome beta browser for Android. Google around - you'll figure it out. – Ken Smith Jul 18 '13 at 14:49