The specific WebRTC (currently being tested), born with the intent to offer web developers a tool to manage the exchange of data flow between two devices directly connected (peer-to-peer).
With the term data flows we want to indicate first of all multimedia communication, such as video and audio ; but not only, as we will see further on, the specifications also contemplate the transmission of different types of data, as could be those deriving from the oscilloscope present on modern smartphones, or data designed for a particular application.
The recipe for obtaining a result like this is certainly complex and involves many components and themes, ranging from networking to the appropriate management of audio and video codecs for signal handling and microphone echo cancellation; fortunately, the bees we are about to discover hide and manage a part, but not all, of this complexity.
WebRTC consists, to date, of 3 APIs:
API | Description |
---|---|
MediaStream | It allows to obtain an audio/video stream coming from the camera and the microphone installed on the user’s device |
RTCPeerConnection | Manages the connection and communication of data streams between two devices in a peer-to-peer connection |
RTCDataChannel | Enables the exchange of arbitrary data streams between two devices in a peer-to-peer connection |
Before continuing, it is important to remember that we are talking about experimental features, whose specifications may change over the next few months and whose support is not currently extended to all browsers.
To be precise in December 2012 the MediaStream API are implemented by Chrome, Opera and Firefox Nightly (even if after activating a flag ), the RTCPeerConnection API only work on Chrome and Firefox Nightly and, to finish the RTCDataChannel API are operative only on Firefox Nightly.
Well, having said that we can start our experimentation starting from the MediaStream API.
MediaStream API
The Media Stream API (also called getUserMedia ) give the possibility, with the consent of the user, to access the video stream coming from the webcam and the audio stream coming from the microphone located on the device that the user is currently using.
The syntax is very simple and is summarized in this small working example, a very light variation of the code of this reference post published on html5rocks.com:
<pre class=”brush: php; html-script: true”>
// I uniform the call to getUserMedia with respect to the various experimental prefixes
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
// uniform the call to window.URL always with respect to experimental prefixes
window.URL = window.URL || window.webkitURL;
// I invoke the request to access both the video and the audio stream, the function
// passed as a second parameter is launched in case of success, the one like
// parameter in case of error or denied authorization
navigator.getUserMedia ({video: true}, function (localMediaStream) {
// I create a HTML5 video element and set the stream as source
// coming from the camera.
var video = document.createElement (“video”);
video.autoplay = true;
video.src = window.URL? window.URL.createObjectURL (localMediaStream): localMediaStream;
// hook the video element to the body of the HTML page.
document.body.appendChild (video);
},
function (error) {
console.log (error);
});
</pre>
If you have a browser compatible with Media Stream API support you can test the script you just saw.

If you want updates on WebRTC: Real Time Communications for the Web enter your e-mail in the box below:
RTCPeerConnection
These APIs are more complicated than the previous ones, in essence their function is to put in touch two devices in a peer-to-peer mode. The procedure that allows us to implement this link is as follows:
- We have two devices, Alice and Bob. Alice wants to contact Bob and open a peer-to-peer channel.
- Alice creates an RTCPeerConnection object and listens for the event onicecandidate; which is launched when the object is ready to transmit.
- As soon as the oncecedidate event is intercepted, Alice communicates to Bob the unique identifier of her peer-to-peer channel (actually composed of a series of objects according to the ICE framework specifications, used as the basis of WebRTC communications).
- Bob uses the addIceCandidate method on his RTCPeerConnection object to add Alice as a recipient.
- Alice generates with the createOffer method an SDP string: Session Description Protocol, which describes the constraints to the audio/video stream (codec, etc.) of your device. This string is sent to Bob.
- Bob receives this string and generates a response SDP with the createAnswer method. This string is sent to Alice.
- Both Alice and Bob receive the add stream event that contains the partner’s data flow and can decide to upload it to a video element as we saw in the previous example.
To test these APIs we have to use Chrome because the support on other browsers is, as we said, limited only to the Nightly version of Firefox. To manage the exchange of messages between Alice and Bob before the opening of the P2P channel we use the websocket protocol; First of all, let’s have a websocket server that can put two browsers in communication for the time necessary to open the peer-to-peer channel.
Fortunately, we can meet this prerequisite with a few lines in Ruby script. If we have never installed this interpreter, we follow the instructions. Once the installation is complete, launch the command from the terminal gem install em-websocketand create a file webserver.rbcontaining:
<pre class=”brush: php; html-script: true”>
require ‘rubygems’
require ’em-websocket’
EventMachine.run do
@channels = {}
EventMachine :: WebSocket.start (
: host => “0.0.0.0”,: port => 8080) do | ws |
ws.onmessage do | msg |
command, params = msg.split (“:”, 2)
if (command == “c”)
@channels [msg [2 ..- 1]] || = EM :: Channel.new
@channels [msg [2 ..- 1]]. subscribe {| msg | </font><font style=”vertical-align: inherit;”>ws.send msg}
else
room, message = params.split (“:”, 2)
@channels [room] .push message
end
end
end
end
</pre>
Although we are probably observing code in an unknown language, we can still try to understand its behavior:
- The server listens on the door 8080; each connection ws is valued with a reference to the client;
- Whenever a client sends a message to the server, the block of code associated with it ws.onmessageis executed;
- If the message is in the form c:x, where it xis an arbitrary number, a communication channel is searched (or created, if it does not exist) associated with the key x. The client is then added to this communication channel;
- If the message is in the form m:x:txt, where x is an arbitrary number and txt is text, the message txtis sent through the communication channel associated with the key xto all the clients connected to it.
We run the server by typing from the command line ruby webserver.rband, keeping the server running, let’s get ready for the second part of the project.
A simple peer-to-peer chat
We call index.html the file that will contain the code of the client part of our project and start and define the markup; to manage the bilateral communication, two video elements will be sufficient, one containing the stream of our webcam and one with the webcam of the peer:
<pre class=”brush: php; html-script: true”>
<! doctype html>
<Html>
<Head>
<meta charset = “utf-8”>
<title> A simple video chat p2p </ title>
</ Head>
<Body>
<video width = “300” id = “video_locale” autoplay = “autoplay”> </ video>
<video width = “300” id = “video_remoto” autoplay = “autoplay”> </ video>
<script src = “https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js”> </ script>
<script src = “js / application.js”> </ script>
</ Body>
</ Html>
</pre>
Now let’s move on to the JavaScript part: let’s create a file application.jsin a folder jsand let’s stop for a while to plan the flow that will have to follow the whole connection:
- The user who wants to start a peer-to-peer chat accesses index.html from a browser;
- the web page opens a websocket connection and generates a random key as a connection identifier. For convenience this key is automatically added at the end of the URL in the address bar (ex:) http://miowebserver/webrtc/index.html#403;
- the page sends through the websocket connection the message c: x, where x is the random key of the previous point. In this way the websocket creates a communication channel identified by x and adds the client to it.
- the page listens for any incoming messages through the newly created websocket channel.
Now the user must invite a partner to the chat, to do so can send via e-mail the address of the web page, including the random key. The partner follows the link received and lands on the same index.htmluser with the difference that in this case the URL already contains a key.
The web application can then use and send to the websocket server the identifier already present in the URL instead of creating a new one. This operation effectively puts both clients on the same websocket communication channel and ultimately enables them to exchange messages.
Let’s see the code (to be inserted in application.js) to realize this first part:
<pre class=”brush: php; html-script: true”>
// — management of the experimental prefix —
window.RTCPeerConnection = window.webkitRTCPeerConnection;
navigator.getUserMedia = navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;
// — initial variables —
var video_locale = document.getElementById (“video_locale”),
video_remoto = document.getElementById (“video_remoto”),
ws = new WebSocket (‘ws: // __ IP_SERVER_WEBSOCKET__: 8080’),
peer;
if (location.hash === “”) {
// in this case the URL does not contain the key and I have to create one
var stanza = Math.round (Math.random () * 1000),
caller = 0;
location.hash = room;
} Else {
// in this case the URL already contains the key
var stanza = location.hash.substr (1),
caller = 1;
}
// — init from the application when opening the websocket channel —
ws.addEventListener (‘open’, function () {
// send the identifier of the channel to the websocket server
The communication I want to connect to.
ws.send (“c:” + room);
inizializza_video (); // we still have to write this function.
}, false);
</pre>
The calling variable is valued based on the presence or absence of the unique key and is used in the next part of the code to distinguish whether the user is opening a chat or is trying to access an existing chat. In the first case all that is required by the API is to wait for the connection request from the partner, in the second case the application must first create a set of ICE candidates and an SDP offer and send it all through the channel of communication through the websocket server.
Now let’s define inizializza_video; the tasks of this function are multiple:
- Get, through the getUserMedia call, access to the webcam and microphone of the user;
- view the video stream coming from the user’s webcam in the element with id video_locale;
- instantiating an RTCPeerConnection and associating a function with the icecandidate event of the same;
- associate the function with a function that displays the remote stream in the element with video_remoto id;
- add the local stream to the RTCPeerConnection instance and, if the application already has a key to connect to (caller = 1), invoke the function to create the SDP offer to send to the partner.
And here is the function code, to be added inside application.js
<pre class=”brush: php; html-script: true”>
// — configurazione —
var mediaConstraints = {‘mandatory’: {‘OfferToReceiveAudio’:true, ‘OfferToReceiveVideo’:true }};
var peer_config = {“iceServers”: [{“url”: “stun:stunserver.org”}]};
// — richiesta accesso webcam e microfono e init della connessione P2P —
function inizializza_video() {
navigator.getUserMedia( {‘audio’:true, ‘video’:true},
function(stream) {
video_locale.src = URL.createObjectURL(stream);
peer = new RTCPeerConnection(peer_config);
peer.onicecandidate = onIceCandidate;
peer.onaddstream = function(event){
video_remoto.src = URL.createObjectURL(event.stream);
};
peer.addStream(stream);
if (chiamante)
peer.createOffer(sdpcreato, null, mediaConstraints);
}
);
}
</pre>
Well, at this point we have to create the two functions that we have defined as callbacks in the previous listing: onIceCandidatee sdpcreato. The first of the two functions must send the new ICE candidate to the partner while the second one must always send the product SDP descriptor to the partner, here is the code to be queued in application.js:
<pre class=”brush: php; html-script: true”>
// — invio l’SDP al peer —
function sdpcreato(sdp) {
peer.setLocalDescription(sdp);
messaggio_da_inviare(sdp);
}
// — invio il candidato ICE al peer —
function onIceCandidate(event) {
if (event.candidate) {
messaggio_da_inviare(event.candidate);
}
}
</pre>
Now, to conclude, it is necessary to define the function message_to_send that, remember, must send the message through the websocket server in the format m:x:txtwhere it xis the key of the communication channel through which the message must transit. The same txt message is also structured in fragments in the format y:jsonwhere it yassumes the same value as the calling variable while json is the JSON serialization of the message, which from time to time could be an ICE candidate or SDP descriptor.
Here is the function, always to add to application.js
<pre class=”brush: php; html-script: true”>
// — invio messaggi al websocket —
function messaggio_da_inviare(msg) {
var msgjson = JSON.stringify(msg);
ws.send(“m:”+ stanza + “:” + chiamante + “:” + msgjson);
}
</pre>
If you want updates on WebRTC: Real Time Communications for the Web enter your e-mail in the box below: