← View all posts
November 2, 2016

Fiddle of the week: Easy-peasy WebRTC Signaling

Contributed by Jan-Ivar Bruaroey,

If you think WebRTC signaling is too complicated, then this blog is for you (unless you dislike newfangled JavaScript).

I often hear this complaint: “Why isn’t WebRTC as simple as, say, Web Sockets?”, specifically that “negotiation is hard”. While it’s inherently more complicated to connect to another client than a server, I think this question has some merit with regard to the RTCPeerConnection API. Let’s see if we can make things simpler.

The WebRTC specification has undergone changes lately, exposing even more surface for professionals to fine-tune real-world use. This is great, but it adds complexity. However, to “advance WebRTC” also means making it accessible to non-professionals. This blog attempts to do that, using compact ES6 JavaScript, because new things go together.

The basics

First, the basics. I learn by seeing the smallest possible working code doing something observable. Here’s the “Hello World” of WebRTC: One peer connection sending video to another on the same page (click “Result” to run it):

If you’re familiar with WebRTC, this may be less code than you’re used to. On the other hand, if you’re new, your reaction to everything but the first three lines should be: “What is all this?” The answer is: signaling, which WebRTC has outsourced to you (unless you reacted to the promises and arrow functions, in which case, come back here after).

This isn’t even a useful example, because we’ve short-circuited the signaling to the same page. Enter Web Sockets, except this blog has no server, so we’re going to cheat and simulate Web Sockets with localStorage. First open this blog in a new window so you can see them both, then click “Result” in both windows to run the following code, before finally clicking the “Connect” button in one of them (it won’t work unless they’re both ready):

We are now a bit closer to what real code looks like (I’ve only left out ICE servers). We’re sending video to someplace else. But now there’s even more signaling, and we had to invent a protocol (I chose {ice} and {sdp}).

Surprisingly, if we want to add or remove tracks mid-call, this signaling is needed even after a connection has been established, even though peer connections have data channels that could do this!

When asked why WebRTC doesn’t automate this, the answer (which has merit) from the working group was that it didn’t meet the “minimal API” test, since it could easily be polyfilled, plus they didn’t want to specify a protocol.

So lets polyfill it!

The RTCSimpleConnection polyfill

I call it RTCSimpleConnection. Our two examples above now become:

and cross-tab:

As you can see, I’ve moved all signaling into RTCSimpleConnection, and we just pass in our socket to its constructor. So simple, I had to shrink the video to fit in the smaller view!

The best part is we haven’t even explored the real benefit yet. RTCSimpleConnection comes with two built-in data channels for chat and signaling (because why not?)

To show the full power of this, here’s a final cross-tab chat demo that connects before video is added, and automatically renegotiates over data channels. After connecting, you can chat instantly back and forth. Either side can hit the “Add Camera” button to add their video mid-call (works both ways):

I hope you found this experiment useful, and that you got the demos working side-by-side for full effect. They all use adapter.js and should work in both Firefox and Chrome. However, it won’t work cross-browser, due to my localSocket hack.

You can find the RTCSimpleConnection polyfill here. I wrote it with this blog in mind, so I haven’t fully field-tested it. I’m sure there are improvements I could make. Hopefully, I’ve given you a springboard to using data channels for signaling. See you on the web!