Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / video

WebRTC Based Multiparty Video Conferencing in 4 Steps

5.00/5 (4 votes)
22 Apr 2018CPOL3 min read 12.2K  
Easy multiparty video conferencing in 4 steps

Introduction

Simple-peer is an excellent library which makes developing WebRTC solutions a piece of cake. The best part about it is how elegantly it hides all the intricacies and gives you an easy to use interface for WebRTC based implementations for video conferencing and data transfer. Since screen sharing is almost the same as passing video data, you can use this for screen casting as well.

Let me break it down in a few simple steps.

Step 1

Get hold of simplepeer.min.js from https://github.com/feross/simple-peer and include it in your HTML file.

Step 2

Find a websocket library for passing data to and fro for initial communication.
In my case, I used easyrtc (https://github.com/priologic/easyrtc).

Step 3

Write a wrapper around the websockets layer so that in main codes, the socket library is not exposed. This way, you can change inner websocket library anytime without changing the codes for WebRTC communication establishment.

Here is some wrapper I came up with called hub (hub.js). Inside hub.js, the .on method is used to listen to custom/user-defined events. Whenever a new peer joins in, I throw the app.peer event with peer id (example in step 4). Similarly, there are some other events invoked by the inner websocket library which is duly passed to the hub.

JavaScript
var hub = { 

    msgMap: {}, 
    
    connect:function connect(url){
        easyrtcConnect(url);
        },
    
    send: function(peerid, msgType, content){
        easyrtc.sendDataWS(peerid, msgType,  content);
    },

    sendToAll: function(msgType, content){
        easyrtc.sendDataWS({targetRoom:"default"}, msgType, content);        
    },

    on: function(type, callback){
        this.msgMap[type] = callback;
    },
    
    event: function(peerid, type, msg){
        var callback = this.msgMap[type];
        if(callback){
            callback(peerid, msg);
        };      
    },

    peerMap: {},

    setPeer: function(peerid, peer){
        this.peerMap[peerid] = peer;
    },
    
    getPeer: function(peerid){
        return this.peerMap[peerid];
    },

    removePeer: function(peerid){
        delete this.peerMap[peerid];
    },

    iteratePeers: function(callback){
        var value;
        for (var key in this.peerMap) {
                value = this.peerMap[key];
                callback(key, value);
        }
    }    
};

Step 4

As discussed, I notify the hub when a new peer/user joins in (i.e., gets online/connects to socket) from the inner layer. Example:

hub.event(easyrtcid, "app.peer", userid);

In order to have a multiparty conference, the best way is to let the newest online user send a hello message to all other peers when he/she logs in. 
The easyrtc library gives me the list of connections who are online, the moment I join in. From inside the inner layer, I throw the app.peer event with the easyrtc id (namely the socket.id). For multiple connections (read users), the hub.event(easyrtcid, "app.peer", userid) is thrown multiple times.

JavaScript
hub.on('app.peer', function(peerid, userid){
    
    hub.send(peerid, 'hello'); // sending hello
});

Putting it a little figuratively would yield this:

JavaScript
-----------------------|- HELLO -> User A
User D-->Sends-->------|- HELLO -> User B
-----------------------|- HELLO -> User C

Once a call request comes, the users accept by default.

And while accepting, we create the SimplePeer object used for WebRTC connection.
Please go through the documentation of simple-peer to understand the initiation and events.

JavaScript
hub.on('hello', function (peerid, msg) {         
    var peer = new SimplePeer({ initiator: false, stream: localStream });       
    hub.setPeer(peerid, peer);
    peer.on('signal', function (data) {        
        hub.send(peerid, 'signal', data);    
    });
    peerCreated(peerid, peer);
    hub.send(peerid, 'ack', '1'); // sending acknowledgement  
});

Note that the above event handler is used to receive the hello from the newly connected user and in return, we send an 'ack'.

ON HELLO User A ---- SEND ACK ----> USER D

ON HELLO User B ---- SEND ACK ----> USER D

ON HELLO User C ---- SEND ACK ----> USER D

The rest is simple, when the user originating the communication receives an ack, he/she forms a SimplePeer object as initiator. All that is left is passing the SDP Offer info to the other party when simple-peer library notifies elegantly with its signal event: peer.on('signal' .. ).

JavaScript
hub.on('ack', function (peerid, msg) {
    //debugger;
    if(msg == "1"){
        var peer = new SimplePeer({ initiator: true, stream: localStream});    
        hub.setPeer(peerid, peer);          
        peer.on('signal', function (data) {
            hub.send(peerid, 'signal', data);
        });
        peerCreated(peerid, peer);
    }
});

Inside the peerCreated method, we do the necessary measures as per the simple-peer documentation.

JavaScript
function peerCreated(peerid, peer){
    
    peer.peerid = peerid; // you can choose to skip this

    //debugger;

    peer.on('connect', function () {
        console.log('CONNECT')
        peer.send('call established .. ' + selfID);
    });
    
    peer.on('error', function (err) { console.log('error', err) });
    

    peer.on('data', function (data) {
        console.log('data: ' + data)
    });

    peer.on('stream', function (stream) {
        console.log('new stream arrived .. ', this.peerid); 
        
        createRemoteVideoElement(peerid, stream);            
    });

    peer.on('track', function (track, stream) {
        console.log('new track arrived .. ', this.peerid);  
        
        createRemoteVideoTrackElement(peerid, track, stream);            
    });

    peer.on('removestream', function (stream) {
        //removeRemoteVideoElement(peerid); 
        console.log("stream removed .. ", peerid); // hardly called
    });

    peer.on('close', function () {
        console.log("connection closed .. ", peerid);
        removeRemoteVideoElement(peerid);    
    });   
}

However, to start of proceedings, we need to get the camera and microphone access (user media stream) and store it in a localStream variable:

JavaScript
// get video/voice stream
navigator.getUserMedia({ video: true, audio: true }, gotMedia, function () {})

// This method starts of proceedings
function gotMedia (ownstream) {

    localStream = ownstream;
        
    connectToSocket(); // once socket connects we receive "app.peer" on hub

    var video = document.getElementById('me');
        video.srcObject = ownstream;
        video.play();
}

 

History

  • 23rd April, 2018: Article uploaded

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)