Click here to Skip to main content
13,705,549 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

1.9K views
2 bookmarked
Posted 22 Apr 2018
Licenced CPOL

WebRTC Based Multiparty Video Conferencing in 4 Steps

, 22 Apr 2018
Rate this:
Please Sign up or sign in to vote.
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.

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.

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

Putting it a little figuratively would yield this:

-----------------------|- 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.

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' .. ).

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.

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:

// 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();
}

A working demo is (hopefully!) running in https://lokkhi.io/crowdcast/demo/. 
Check that out for more information (screen cast is implemented there, but there is no UI element to initiate it and also no ice config specified).

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)

Share

About the Author

Mukit, Ataul
Chief Technology Officer Rational Technologies
Bangladesh Bangladesh
If you are not in - you are out !
- Chapter 1

You may also be interested in...

Comments and Discussions

 
QuestionWhere's the project? Pin
Dewey22-Apr-18 15:54
memberDewey22-Apr-18 15:54 
AnswerRe: Where's the project? Pin
Mukit, Ataul23-Apr-18 21:59
memberMukit, Ataul23-Apr-18 21:59 
GeneralRe: Where's the project? Pin
Dewey25-Apr-18 20:34
memberDewey25-Apr-18 20:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180920.1 | Last Updated 22 Apr 2018
Article Copyright 2018 by Mukit, Ataul
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid