Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebRTC video chat with Ajax instead of WebSocket: Possible?

About six months ago, I was able to successfully code my own WebSocket server script in PHP. Through that, I was able to set up a WebRTC video chat service on my local host. I was quite happy until I realized that in order to deploy it, I needed a web server that gave me access to sockets.

Unfortunately, no shared web hosting allows for sockets and all web servers that offer sockets are expensive. While not an effective solution on a large scale, for the sake of setting up a demo to show people, I want to change the signalling method over from WebSocket to Ajax so that I can show off the WebRTC video chat service I made.

To that end, I've been trying to code something for the past few days, but have had no success in getting the WebRTC peers to capture each other's video.

At the moment, when one client connects to the script, I am using Ajax to send a request to a PHP script that checks whether there are any other active users in the DB. If not, the script then creates an offer, and places the offer in the DB. After that, the client polls a separate PHP script every second to check for an answer from another client connecting to the script.

After that, I then connect to the script from another client, which queries the same PHP script and DB, which then realizes that an active user (the first connection) has already posted an offer, which the second client acquires and sets for the remote description. The second client then creates an answer, which is placed in the DB.

At this point, the first client (which is polling the DB every second) detects that an answer is present and sets that answer as the remote description for the first client. Unfortunately, even after successfully doing all of this, the other client's video isn't popping up.

So here's where I'm confused and have three (multipart) questions:

1) I thought that after both clients set their local description and then sent that local description to the other client and the other client set that received description as the remote description that the onaddstream event was supposed to fire, thus allowing me to display the remote video. However, this isn't happening. This worked fine before when I used WebSocket, but it's not working at all with pure Ajax. Is there something in particular I'm missing? Has the WebRTC spec changed radically in the past six months? I've tried looking at the WebRTC specs, but I don't see any major changes.

2) After getting frustrated with not getting things to work with Ajax, I went back to my WebSocket version and loaded it on my local host. I haven't changed the code at all since last using it (which worked fine six months ago), but now, when I try to use it, sometimes it works, and sometimes it doesn't. Sometimes I get errors related to not being able to set the local and/or remote descriptions. What's up with this? Have there been changes to the specs that would cause this to happen? Related to this, even though I can't get the remote videos to pop up with the Ajax version, I have been echoing a lot of stuff out to the console, and it seems like with the Ajax version as well, sometimes the local and remote descriptions for both clients are successfully set up, and sometimes errors occurs when trying to set the local/remote descriptions for whatever reason, even though I'm running the exact same script each time without any changes. I'm using the latest version of Chrome, and I'm starting to wonder if there's a bug it in or something.

3) Is the onicecandidate event handler required to establish a connection? My assumption was that peers could establish a connection with simply a valid offer and answer, and that the onicecandidate event was use to provide alternate routes, etc., which could lead to a better connection (but aren't required). Am I wrong? If the onicecandidate info is required, how do you recommend I handle this with Ajax as the signalling method?

I know that's a lot of info and a lot of questions, but any information/insight that anyone can offer would be very much appreciated. I've been banging my head against my desk for the past couple of days trying to figure this out, and nothing is making sense.

like image 628
HartleySan Avatar asked Jun 18 '13 05:06

HartleySan


People also ask

Does WebRTC use Websockets?

The WebSocket protocol is often used as a signaling mechanism for WebRTC applications, allowing peers to exchange network and media metadata in realtime.

What is difference between WebRTC and socket IO?

While WebRTC uses a peer-to-peer communication method, WebSocket utilizes a server per session method of communicating. WebRTC signaling server can facilitate a direct connection between peers and doesn't have to participate in the communication loop.


2 Answers

My first advice about your app. working/not working sporadically is to look at current online working implementations. There are plenty of WebRTC demos on the Internets.

AJAX

About AJAX: why wouldn't it work? I am currently working on just the same thing as you and it works fine everytime (I cannot disclose the source for the moment). Clients are constantly polling the server at regular intervals and they can send SDP descriptions/ICE candidates to a specific other client that way. The server acts as a simple bridge (this is the basis of signalling).

Be it WebSocket, AJAX or IPoAC, as long as you transfer to the other client everything he needs (and at the right moment, more on this later), it should work. I even made a demo where you manually copy/paste the SDP description and ICE candidates using text areas and click on buttons to move forward in the signalling process and that, of course, worked fine too.

ICE candidates

Now: yes, you need ICE candidates. Look at a sample SDP offer chunk I just generated using createOffer on Chromium 27:

v=0
o=- 3866099361 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS 9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:l8Qu31Vu4VG5YApS
a=ice-pwd:TpyQ5iESUH4HvYGE4ay8JUhe
a=ice-options:google-ice
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:bC5YSe2xCmui0wSxUHWKIi9INbZ2y0VrO1swoZbl
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:107 CN/48000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
a=maxptime:60
a=ssrc:1976175890 cname:/+lKYsttecoiyiu5
a=ssrc:1976175890 msid:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8 9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8a0
a=ssrc:1976175890 mslabel:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8
a=ssrc:1976175890 label:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8a0
m=video 1 RTP/SAVPF 100 116 117
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:l8Qu31Vu4VG5YApS
a=ice-pwd:TpyQ5iESUH4HvYGE4ay8JUhe
a=ice-options:google-ice
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=sendrecv
a=mid:video
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:bC5YSe2xCmui0wSxUHWKIi9INbZ2y0VrO1swoZbl
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack 
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
a=ssrc:3452335690 cname:/+lKYsttecoiyiu5
a=ssrc:3452335690 msid:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8 9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8v0
a=ssrc:3452335690 mslabel:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8
a=ssrc:3452335690 label:9kTlKaNe1exIs6JgEFYfXlu6E5f4B5R3I2D8v0

Do you see anything that could help another client connect to my machine? I don't think so. The purpose of all this ICE mechanism is to gather connection candidates (local ones like 192.168.1.15, "public" ones (the public IP assigned by your ISP) using STUN if you're behind any NAT that's not symmetrical, or TURN ones for symmetrical NATs).

Upon receiving those ICE candidates, the other peer will order them using some predefined metrics for prioritizing and then issue connection tests to find a good candidate. So please share them: the other peer needs them (and you need its too).

So here are some of my ICE candidates:

a=candidate:303249700 1 udp 2113937151 192.168.50.238 43806 typ host generation 0
a=candidate:303249700 2 udp 2113937151 192.168.50.238 43806 typ host generation 0
a=candidate:1552991700 1 tcp 1509957375 192.168.50.238 35630 typ host generation 0

Now those are concrete (albeit only local, because I didn't configure the RTC peer connection with any STUN URL) ways for another peer to connect to my machine.

WebRTC signalling tips

There are a few interesting tips on the bottom of this page. I honnestly could not tell you right now why you should follow those or why they exist in the first place, but I did follow them and had no signalling problems. Here they are:

  1. For answerer: NEVER add ICE candidates until that peer generates/creates answer SDP
  2. Stop adding ICE candidates when remote stream starts flowing
  3. Don't create peer connection for answerer until you get offer SDP

You can manage all that on the client side by having some WebRTC finite-state machine. Please see the referenced page to understand what he means by remote stream starts flowing.

Try to share ICE candidates and add them at the opposite side and at least follow tips #1 and #3 and your application should work again.

Signalling communication

You asked how to transfer an ICE candidate from a peer to another one in the event of ICE candidates being important to share (which they are). To share things using AJAX, whatever those things are, you can use mailboxes. I believe you're already doing this by placing what is due for a client in the database.

Whenever a peer needs to send something to another one, send it ASAP (using AJAX). On the server side, place this "mail" in the destination client's mailbox. When a peer (periodically) polls the server for new mails, give it all its new mails.

When an SDP offer is created, ICE candidates are quickly generated. All those ICE candidates and the SDP description will probably get into the destination mailbox within a few milliseconds. There are good chances that the destination peer will poll everything neede at once. Even if an ICE candidate arrives late, its next polling will get it.

like image 100
eepp Avatar answered Nov 09 '22 23:11

eepp


This doesn't really answer your questions, but for a signalling server you might want to take a look at Socket.io (on Node). I wrote a codelab explaining how to set this up:bitbucket.org/webrtc/codelab. It's very straightforward -- the complete example is here: the signalling server code is about 50 lines.

SimpleWebRTC runs the Signalmaster server, which uses Socket.io.

(Robert Nyman wrote a good blog post explaining this.)

Another option is to use XHR with the Google Channel API, as per the apprtc.appspot.com example: code here.

like image 3
Sam Dutton Avatar answered Nov 09 '22 23:11

Sam Dutton