Tag Archives: p2p

Peer Connection Tutorial – Part1

Wow! I’ve had a huge break from blogging for a while now especially about WebRTC. I’m sure you all have been tracking how much WebRTC has been evolving over the past few months. The most recent and exciting news which you have got to know is the interop between Chrome and Mozilla has landed and it is undeniably super cool. You could check out the demo by following up this link. So, without any further delay let me start with the peer connection tutorial which I am sure is a component everyone is dying to try.

I have realized that a peer connection tutorial has got to have a minimum of two parts so that I do at least some justice to it. So here in part1, lets start with the basics of what you have got to know to get started with establishing your own peer connection i.e learn how a basic call is established.
Assumptions : I assume that you can understand the basics of HTML and JavaScript so that you can understand the other parts of what is happening and that you have already ready my tutorial on gUM or you know the basic usage of gUM. A little bit of going through the WebRTC blogs (I’ve listed a few in the last) would be really helpful as well.

I will be using the code from Anant’s demo to walk you through the steps of setting up a call. It provides an easy way of testing the peer connection component (especially when you are not hosting your app). This page basically has two video elements, one called Remote and the other called Local. The video captured by your local machine (using gUM) is displayed by your Local video component. The basic idea is to establish a call using WebRTC’s peer connection component between the Remote and Local video components on the page. Thus finally when the call is established the video captured by your local machine is streamed to the Remote Video element on that page through its connection with the Local Video element. I highly recommend you try out this demo yourself on Peer Connection Test Page before getting started with this tutorial so that you have a clear idea of what you are trying to build. (I’m not sure if it works on windows yet :|)

Note: For the demo to work you have to have the latest version of Mozilla Nightly. Now that the peer connection preferences are turned on by default I don’t think you need to tinker with your browser preferences.

Lets start of from the very beginning. When I want to initiate a call what should I do? ( what to do in the code when I click on the start button in the pc test page ) – The first and foremost step is to create the peer connection objects based on the number of connections that are required. For our example, we require only two, so we create pc1 and pc2 as follows:

pc1 = new mozRTCPeerConnection();
pc2 = new mozRTCPeerConnection();

The peer connection objects has several in built methods which can be used quite effectively as you will notice in this tutorial. The next important step is to capture the local video by making use of gUM as follows:

navigator.mozGetUserMedia({video:true}, function(video1) {

In the above function we have set the captured media by the gUM function to video1 i.e. currently video1 is the local machine’s captured video. The next part deals with setting the local video source as video1. Once the video begins to play (thanks to invoking play()) we add this video to the Stream attribute of the peer connection object by making use of the method addStream(video)

// Add stream obtained from gUM to <video> to start media flow.
localvideo.mozSrcObject = video1;
localvideo.play();
pc1.addStream(video1);

So now, we have the local video running. We will also create fake audio streams for both pc1 and pc2 (The reason we have pc1’s audio as a fake stream is due to the echo problem) . These can be used later on in case some captured audio has to be added to the stream.

navigator.mozGetUserMedia({audio:true, fake:true}, function(audio1) {
pc1.addStream(audio1);
pc2.addStream(audio1);

Similarly we also add a fake video stream to the pc2 object, so that once the call is established the video from pc1 can be added to this stream.

navigator.mozGetUserMedia({video:true, fake:true}, function(video2) {
pc2.addStream(video2);

You might be wondering if I’ve forgotten the method onaddstream. Don’t worry I don’t forget that easily 😛 So, this function is invoked everytime the addStream function is called. Let me explain this with respect to pc1.onaddstream. In this method we set the source stream for the pc2 video and we play it using the play method. Since, intially there is only a fake video stream nothing will play, but later on once the call is established – voila! pc2 will be playing the local video from pc1 i.e it plays the remote stream when a video call is established.

pc1.onaddstream = function(obj) {
log("pc1 got remote stream from pc2 " + obj.type);
pc2video.mozSrcObject = obj.stream;
pc2video.play();
}

The pc2.onaddstream does something similar, but this is for the case where pc2 is the local stream and pc1 is the remote stream.

pc2.onaddstream = function(obj) {
log("pc2 got remote stream from pc1 " + obj.type);
pc1video.mozSrcObject = obj.stream;
pc1video.play();
}

Now that we have everything ready, the only thing left is to establish a call. In WebRTC, calls are established through a process called Signalling which can roughly be defined as – “For a caller to be able to make a call he first has to make an offer to the callee (general procedure) and for the call to be established it is required that the callee accepts the offer and return an answer which is in turn accepted by the callee (yeah, yeah its the preliminary handshake). “ Signalling in itself is a huge topic to cover and is achieved through a protocol called Interactive Connectivity Establishment (ICE) and I plan to explain it in the next blogpost. For now, all you need to know about signalling is a higher level abstraction of it which is pretty much clearly stated in the definition. So the first step to signalling takes place here (creating an offer):

pc1.createOffer(step1, failed);

Here, tha basic idea is to make pc1 create an offer to pc2 as mentioned before. The offer will contain the Local Description of pc1 object and this Description is generated in the method setLocalDescription(..) in the function step1 as follows:

// pc1.createOffer finished, call pc1.setLocal
function step1(offer) {
pc1_offer = offer;
pc1.setLocalDescription(offer, step2, failed);
}

Now that pc1 has set its description, we have to set the Remote Description of pc2. This is done through the method setRemoteDescription(..) using the previously generated offer in the function step2 which is invoked while setting the Local Description of pc1 above. The function step2 looks like this:

// pc1.setLocal finished, call pc2.setRemote
function step2() {
pc2.setRemoteDescription(pc1_offer, step3, failed);
};

Now that the Local and Remote Descriptions are set for pc1 and pc2 respectively, it is required for pc2 to create an Answer which it will then send to pc1. This answer is created using the method pc2.createAnswer(..) in the function step3 invoked while creating the remote Description of pc2.

// pc2.setRemote finished, call pc2.createAnswer
function step3() {
pc2.createAnswer(step4, failed);
}

After creating the answer now pc2 also creates a Local Description using this answer. This is done through the method pc2.setLocalDescription(answer, step5, failed) as shown in the function step4 which is invoked previously while creating the answer:

function step4(answer) {
pc2_answer = answer;
pc2.setLocalDescription(answer, step5, failed);
}

Finally, the last step that remains is to set the Remote Description of pc1 using the answer generated previously by pc2. This is done in the method pc1.setRemoteDescription(pc2_answer, step6, failed) in the function step5 invoked while setting the Local Description of pc2:

// pc2.setLocal finished, call pc1.setRemote
function step5() {
pc1.setRemoteDescription(pc2_answer, step6, failed);
}

At long last our call is established – and your eyes will be filled with tears of joy 😛

// pc1.setRemote finished, media should be running!
function step6() {
log("HIP HIP HOORAY");
}

Now look back at all the steps – not so bad eh? – Doable? – Of course. This is the power of WebRTC – peerConnection established in a few simple steps. Though it does get a little tricky when it comes to real world apps (which I realized very recently), its still manageable right?! For those of you who think its not, there are a few great APIs (one of which is AddLive’s API) available and more coming 😉

I almost forgot – still a few final steps left.
In case of an error you can add the required error handlers and handle the errors differently/better depending upon your application/usage.But this is not a part that we shall cover here.

function failed(code) {
log("Failure callback: " + code);
}

Finally when you want to end the call you just close the peer connection using the syntax pc.close()

function stop() {
pc1.close();
pc2.close();

button.innerHTML = "Start!";
button.onclick = start;
}

That’s about it for this PeerConnection Overview.

In the next tutorial, I will try to explain what is going on underneath this neat JavaScript code. So it is enough if you have an abstract idea of how this works for now 😉 If possible I will also try to give you guys a brief idea of the underlying ICE protocol in the next blogpost. Hope you find this helpful. If you have any queries, feel free to comment here or ask on Twitter 🙂