How to Develop a WebRTC Video Conferencing Server Using PowerMedia XMS, Free In An Hour

How to Develop a WebRTC Video Conferencing Server Using PowerMedia XMS, Free In An Hour

  • Comments 10
  • Likes

I’m a telephony guy at heart but with the WebRTC revolution, the line that once clearly divided web versus telephony programmers is quickly vanishing. The acronyms I often referenced like ACD, ISDN and PBX seem to be obsolete and replaced with new, hip acronyms like MEAN.io, LAMP and WYSIWYG. The rate at which web development changes is exponentially faster than anything telephony developers have ever experienced. That said, time for this dog to learn a new trick…. this is the first installment of (hopefully) a long-lasting developer series which will focus on the rumblings of a telephony guy attempting to understand the web world.

To start, this is my version of “hello world” where together we’ll be building a simple WebRTC video conferencing server on top of the Dialogic PowerMedia XMS (eXtended Media Server) platform. I’ll be using Node.js to build the web service on the back-end (yes, using node.js for serving static files may be overkill but would like this to be a starting point for more to come), HTML5/CSS/JS for the front-end UX and NetAnn for the media controls to media server. NetAnn is a basic control interface intended for creating simple conferences or announcements. For those looking for more including recording, video playback, etc you may want to consider a RESTful based control.

The guide below is a step-by-step process starting with absolutely nothing to building a useable WebRTC video conferencing server in less than an hour. I tried not to make any assumptions in my guide and walk through each step of the way but for those looking to skip straight to the end I’ve uploaded the entire project to GitHub (NetAnn_video_conferencing_server directory).

Where we take this developer series from here is entirely up to you – please post your comments / concerns / questions / recommendations below or private message me. I’d appreciate any social shout out’s and shares if you like the code.

Thanks and enjoy!

~Vince

@vfpuglia

 

Programming Experience: Novice

Time to implement: ~1 hour

Prerequisites:

* Bare metal server OR VMware w/ adequate processor/memory/storage (see server requirements in PART 1 below)

* (1) IP address to be assigned for PowerMedia XMS

Putty

* Your favorite editor (vi, emacs, notepad++)

* PC client(s) w/ microphone, camera and Chrome installed

PART 1: Download and Install PowerMedia XMS (~30 mins)

1.) Download the latest PowerMedia XMS ISO here

Note – I used PowerMedia XMS 2.2 SU2 for these instructions

2.) Follow the "PowerMedia XMS Installation and Configuration Guide" for proper system requirements and installation

Note - the PowerMedia XMS install comes with a (4) port fully enabled license. Contact me or the Dialogic sales team to discuss licensing options beyond the base four ports.

 

PART 2: Installing Node.js & other necessary package modules (~10 mins)

1.) Using Putty, SSH into the PowerMedia XMS Server

2.) Login using root and default password powermedia

3.) First, change directories to /usr/src directory - the usual place to hold software sources: cd /usr/src

4.) Next, download the latest compressed source archive from Node.js website (reference the Node.js downloads page for latest version information): wget http://nodejs.org/dist/v0.10.30/node-v0.10.30.tar.gz

5.) Uncompress the source files: tar zxf node-v0.10.30.tar.gz

6.) And move into that directory: cd node-v0.10.30

7.) Prepare the compile by executing the configure script: ./configure

8.) Compile the source node code (can take 5-10 mins): make

9.) Once compiled, make the node components available system-wide: make install

10.) You should be able to then run node --version and npm --version to retrieve the installed version information.

i.e.

[root@xms node-v0.10.30]# node --version

v0.10.30

 

[root@xms node-v0.10.30]# npm --version

1.4.21

 

11.) Install forever (A simple CLI tool for ensuring that a given node script runs continuously): npm -g install forever

12.) Create a new directory to store our web project(s): mkdir projects

13.) And move into that new directory: cd projects

14.) Create a new directory to store our code: mkdir conferencing_server

15.) And move into that new directory: cd conferencing_server

16.) Install bootstrap (popular front-end framework for developing projects on the web): npm install bootstrap

NOTE – At this point you may choose to move on to PART 3 that steps through the HTML/JS/CSS code required for building the conferencing server OR you may copy the project zip associated with this guide into your projects directory and skip to PART 6.

 

PART 3: Building the Node Server logic (~2 mins) 

1.) First create the server file to be used by node: touch server.js

2.) Using your favorite editor (I like to remote connect using Notepad++), open the server.js file for editing

3.) Copy the below contents into the server.js file - I won't go into much detail regarding the Javascript below being used by node as it's fairly generic and there are plenty of tutorials in this area but be aware we're designating port 3000 for the server and will be pointing to conference.html file. 

var http = require("http"),

    url = require("url"),

    path = require("path"),

    fs = require("fs")

    port = process.argv[2] || 3000;

http.createServer(function(request, response) {

  var uri = url.parse(request.url).pathname,

     filename = path.join(process.cwd(), uri);

     console.log(url);

     path.exists(filename, function(exists) {

     if(!exists) {

      response.writeHead(404, {"Content-Type": "text/plain"});

      response.write("404 Not Found\n");

      response.end();

      return;

    }

    if (fs.statSync(filename).isDirectory()) filename += '/conference.html';

    fs.readFile(filename, "binary", function(err, file) {

      if(err) {       

        response.writeHead(500, {"Content-Type": "text/plain"});

        response.write(err + "\n");

        response.end();

        return;

      }

      response.writeHead(200);

      response.write(file, "binary");

      response.end();

    });

  });

}).listen(parseInt(port, 10));

console.log("Node server running on port:" + port + "/\nCTRL + C to shutdown");

  

PART 4: Building the HTML front-end for the user interface (~5 mins)

1.) As mentioned in part 3, our Node services will be pointing to a file called conference.html. As such, we'll need to create this new file and build the HTML front-end for the user interface. While still in the /projects/conferencing_server directory, create a new HTML file to be used: touch conference.html

2.) Open the conference.html for editing - copy and paste the entire the entire content below. The HTML code defines (3) <div> sections: 

  • loginPanel: input for the "username" variable and calls the "conferenceLoginClickHandler" function upon onclick
  • videoPanel: defines the "remoteVideo" element used to display the inbound WebRTC video feed from PowerMedia XMS. Also defines the "localVideo" element which is used to obtain the local users video feed but will be hidden from HTML display
  • conferencePanel: input for the "confID" variable and calls the "conferenceClickJoinHandler" function upon onclick

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>XMS Node NetAnn Conferencing Demo</title>

    <!-- Bootstrap CSS -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">
    
     <!--- Javascript --->
     <script src="js/webrtc.js"></script>
     <script src="js/conference.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="form-signin" role="form">
        
          <div id="loginPanel">
               <h2 class="form-signin-heading">Login: </h2>
               <input type="text" class="form-control" id="username" placeholder="username" required autofocus>
               <button class="btn btn-lg btn-primary btn-block" onclick="conferenceLoginClickHandler()">Enter</button>
          </div>
        
          <div id="videoPanel" hidden>
               <video id="remoteVideo" height="300" autoplay controls>
               </video>
               <video id="localVideo" height="300" style='visibility:hidden' autoplay controls muted>
               </video>
          </div>
        
          <div id="conferencePanel" hidden>
               <h2 class="form-signin-heading">Conference ID:</h2>
               <input type="text" class="form-control" id="confID" placeholder="i.e. 1234" required autofocus>
               <button class="btn btn-lg btn-primary btn-block" onclick="conferenceClickJoinHandler()">Join</button>
          </div>
                
      </div>
    </div> <!-- /container -->

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
   
  </body>
</html>

 

3.) Create a css directory for our storing style sheets: mkdir css

4.) Move into the css directory: cd /usr/src/node-v0.10.30/projects/conferencing_server/css/

5.) Create a new CSS file used to style the HTML elements: touch style.css 

6.) Open the style.css file for editing

7.) Copy the below CSS contents into style.css 

 

body {

  padding-top: 40px;

  padding-bottom: 40px;

  background-color: #eee;

}

.form-signin {

  max-width: 330px;

  padding: 15px;

  margin: 0 auto;

}

.form-signin .form-signin-heading,

.form-signin .checkbox {

  margin-bottom: 10px;

}

.form-signin .checkbox {

  font-weight: normal;

}

.form-signin .form-control {

  position: relative;

  height: auto;

  -webkit-box-sizing: border-box;

     -moz-box-sizing: border-box;

          box-sizing: border-box;

  padding: 10px;

  font-size: 16px;

}

.form-signin .form-control:focus {

  z-index: 2;

}

 

8.) Save the changes and exit the editor

 

PART 5: Building the Javascript to interface with PowerMedia XMS and the HTML front-end (~10 mins)

1.) First create a js directory that will be used for storing our javascript code: mkdir /usr/src/node-v0.10.30/projects/conferencing_server/js/ 

2. Then import the Dialogic WebRTC Javascript Library into the project:  cp /var/www/rtcweb/html/js/webrtc.js /usr/src/node-v0.10.30/projects/conferencing_server/js/webrtc.js

3.) Next change directory to where the javascript files are stored: cd /usr/src/node-v0.10.30/projects/conferencing_server/js/ 

4.) Create the javascript conference file: touch conference.js

5.) Open the conference.js for editing

6.) First, we'll need to define the functions being called in PART 4 - let’s start with "conferenceLoginClickHandler()" where the user enters their username and selects login. In this function, we'll create a new instance of the Dialogic Javascript library and assign to myConf.  Once the instance is created, we'll need to retrieve the 'username' element from our HTML form which will then be passed to the PowerMedia XMS server IP address along with the username. Note - be sure to change the "my_xms" variable to the proper IP address you set in PART 1. Note - the "handlers" variable being passed with myConf.setHandlers is defined in step 8 below. 

 

var my_xms = "XXX.XXX.XXX.XXX";

var myConf = null;

function conferenceLoginClickHandler() { 

     console.log("*** conferenceLoginClickHandler - ENTER ***");    

     // Create a new instance of Dialogic JavaScript library here

     myConf = new Dialogic();

     myConf.setHandlers(handlers);

        //Retrieve username info from HTML and pass for registration

     var username = document.getElementById("username");

     var my_xms_ws = "ws://" + my_xms + ":1080"; 

     myConf.register(username.value, my_xms_ws, '');    

}

 

7.) Next, we need to define "conferenceClickJoinHandler()" where the user enters the conference id and selects join. In this function, we'll first need to convert the "confID" variable into appropriate NetAnn syntax: 

conf=YYYY@XXX.XXX.XXX.XXX 

 

Where YYYY is the conference ID and XXX.XXX.XXX.XXX is the IP address of the PowerMedia XMS server. Once converted, we'll use myConf.call to pass the variable to PowerMedia XMS. 

 

function conferenceClickJoinHandler () {

     console.log("*** conferenceClickJoinHandler - ENTER ***");

     var confID = document.getElementById("confID");

     confID = "conf=" + confID.value + "@" + my_xms;

     console.log("confID:" + confID);        

     var ret = myConf.call(confID, 'video'); 

     if (ret == 'ok') {

          document.getElementById("conferencePanel").hidden = true;

          document.getElementById("videoPanel").hidden = false;

     } else {

          console.log("Error attempting conferenceClickJoinHandler");

     }

}

  

8.) The last function we need to define is "initialize()" which gets called as a result of a successful registration (see step 9 below). As part of this function, we will retrieve the local and remote video elements and pass them as part of our mediaContraints. 

 

function initialize () {

     console.log("*** initialize - ENTER ***");

     var localVideo = document.getElementById("localVideo");

     var remoteVideo = document.getElementById("remoteVideo");

         

     var spec = { 'localVideo' : localVideo, 'remoteVideo' : remoteVideo, 'remoteAudio' : null, };

     var ret = myConf.initialize(spec);

     var mediaConstraints = { 'audio': true, 'video': true };

    

     myConf.acquireLocalMedia(mediaConstraints);

    

     if (ret != 'ok') {

          console.log("Error initializing user media");

     }

}

 

9.) Next, we'll need to create the associated functions from the handlers. In this case we only need to add logic to the registerSuccessHandler which will issue the "initialize()" function and change the HTML form inputs from the "loginPanel" to the "conferencePanel" 

var registerSuccessHandler = function () {
     console.log("*** registerSuccessHandler ***");
     
     document.getElementById("loginPanel").hidden = true;
     document.getElementById("conferencePanel").hidden = false;
     
     initialize();
};

var registerFailHandler = function () {
     console.log("*** registerFailHandler ***");
};


var ringingHandler = function () {
     console.log("*** ringingHandler ***");
};

var incomingCallHandler = function (name) {
     console.log("Incoming call from: "+name);
};

var callHangupHandler = function () {
     console.log("*** callHangupHandler ***");
};

var disconnectHandler = function () {
     console.log("*** disconnectHandler ***");
};

var userMediaSuccessHandler = function () {
     console.log("*** userMediaSuccessHandler ***");
};

var userMediaFailHandler = function (){
     console.log("*** userMediaFailHandler ***");
}

var remoteStreamAddedHandler = function () {
     console.log("remoteStreamAddedHandler");
};

var messageHandler = function () {
     console.log("*** messageHandler ***");
};

var infoHandler = function () {
     console.log("*** infoHandler ***");
};

 

10.) Lastly, as mentioned in step 6, we'll need to define the handlers variable that is used to store the various events to be returned as part of joining the conference. Below is a stock list of handlers that can be implemented - note the handlers being registered are associated with function handles which we'll define in step 9. 

 

var handlers = {

     'onRegisterOk': registerSuccessHandler,

     'onRegisterFail': registerFailHandler,

     'onRinging': ringingHandler,

     'onConnected': null,

     'onInCall': incomingCallHandler,

     'onHangup': callHangupHandler,

     'onDisconnect': disconnectHandler,

     'onUserMediaOk': userMediaSuccessHandler,

     'onUserMediaFail': userMediaFailHandler,

     'onRemoteStreamOk': remoteStreamAddedHandler,

     'onMessage': messageHandler,

     'onInfo': infoHandler,

     'onDeregister': null

};

  

11.) Save the changes and close the conference.js file

  

PART 6: Starting node server and testing your new WebRTC Conferencing Server ~3 mins)

1.) Before starting the node conference service, we'll need to add port 3000 to the #iptables list. To do so, from command line insert a new rule to allow tcp traffic on port 3000: iptables -I INPUT 5  -i eth0 -p tcp --dport 3000 -m state --state NEW,ESTABLISHED -j ACCEPT

2.) Save the iptable changes: service iptables save

3.) Restart the services to take effect: service iptables restart

4.) From the /usr/src/node-v0.10.30/projects/conferencing_server/ directory - start the node conference service using the forever module: forever start server.js

5.) Exit out of the SSH/CLI and open a Chrome browser session

6.) Navigate to: http://<IP address of your XMS>:3000

7.) Enter your name select <enter>

8.) Select "Allow" for the browser to access the microphone and camera

9.) Enter the conference ID <enter>

10.) BOOM – we’re done! Share the URL of your new video server and Enjoy!

Views: 25396
Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hii.

    Nice post.

    Is there any ways to find out,who and all is there in the particular conference ID ?

  • Hi - thanks.... unfortunately not using the NETANN API.... for something like this you'd need to use one of the other, more feature-rich API's like RESTFuL

    Hope this helps.

    Vince

  • Thank you for the response.

    Is it possible to achieve all functionalities in webrtc.js using rest api?

  • Added to previous post. Can we make a outbound call using rest api alone?  The thing is that I am able to make a call, but audio streaming is not enabled between source and destination.

  • Yes - here is the restful api guide which includes all the capabilities of the API: www.dialogic.com/.../XMS_RESTfulAPIUser.pdf

    Also, here is a good youtube playlist that gets into detail on the design of the application: www.youtube.com/playlist

    Hope this helps.

  • Call is getting connected,but video streaming is not taking place through webrtc.js .Any possible reason for this?

  • Tough to say without seeing a trace and it may be that you're using an older version of the webrtc.js but I did make an updated version of this blog that may help blog.dialogic.com/.../how-to-make-a-webrtc-app-step-by-step-guide and associated video: www.youtube.com/watch

    Hope this helps

    Vince

  • Hi Vince,

    Your latest blog for video Conference is good.

    Still video streaming is not taking place.I am using 1080p web cam . Should i use lower resolution to achieve video call.

  • The 1080 web cam should not make a difference here - can you post a chrome console trace? This will give some insight where it's failing.

    Vince

  • fewdewded