Using PubNub with XirSys (Part 2) – Collaborative URL Sharing

Introduction

The guys at PubNub have created a WebRTC SDK geared toward using the PubNub services as a WebRTC signaling platform. The SDK is very comprehensive and provides a great insight into how to develop a WebRTC framework. You can view the demo of this SDK, here, and download the source files for this guide, here.

Using PubNub with XirSys (Part 2) – Collaborative URL Sharing

If you read part one of this series you’ll need to have a Xirsys and Pubnub account to create this demo, if you are already set up then you’re good to go.

Like part one, you will need your Xirsys ident and secret key, as well as domain, application and room names, you will also need your Pubnub publish and subscribe keys to complete the handshake.

My Tools: Text editor: Sublime Text v3

NOTE: In addition to Sublime, I will also be using JQuery 2.1, Bootstrap 3, 
as well as some cool Google fonts for the demo source files. 
This of course is preference and I will not be going over any of the style or design aesthetics, 
so if you prefer to code in plain javascript and html go for it, it’s all good.

We can start by first creating a basic html file called index.html. In the Body tag add 2 video tags within their own respective div’s. Give one of them the id attribute of “videoSelf”, add muted=”true” and autoplay to the attributes as well. This video object will display the local user’s video camera. The muted=’true’ attribute is there so the user is not hearing their own voice echoing back at them.

<div>
  <video id='videoSelf' muted='true' autoplay></video>
</div>

For the second video window, add an id attribute of “videoRemote” and add another autoplay attribute to it. As you may have guessed, this is used for the Remote User’s camera stream; leave out the muted attribute since this feed we do want to hear.

<div>
  <video id='videoRemote' autoplay></video>
</div>

The point of the app is to collaborate and share URLs with a remote person, so we will need to build a path that we can send to others so they can directly connect to us. I want to show this path in a container that we can easily copy and paste from. To keep it simple, I will just use text in a simple div container, I don’t want to use the anchor tag because I don’t want the user to accidentally click and open it every time they want to copy it. Give your container an id of “myCallLink”.

<div id="myCallLink">
  http://
</div>

Time to add our URL field. This field will be used to allow the user to type in the URL string they want to share with the remote user. Within a div add a text input field and give it an id of “urlInput”.

<div>
   <input type="text" id="urlInput" />
</div>

For the final required element we’re going to use an iFrame to display the url being shared with the other user. Give the iFrame an id of “sharedWin”. Add a blank source path for the src attribute just to show that the starting state needs to be blank.

<div>
   <iframe id="sharedWin" src=""></iframe>
</div>

Ok now that our main elements are laid out, we can move forward to the javascript logic. We will need some service SDK’s to make this easier, add these below your visual elements in their respective script tags:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdn.pubnub.com/pubnub-dev.js"></script>
<script src="http://cdn.pubnub.com/webrtc/webrtc.js"></script>

JQuery SDK – The jQuery SDK helps build code that is more reliable across different browsers. This is preference and you can code this in pure JS if you wish.

PubNubDev SDK – A very helpful SDK to use the PubNub service for the real-time interaction.

PubNub WebRTC SDK – Another very helpful SDK that PubNub provides to create a simple user-to-user communication using WebRTC, this works seamlessly with your Xirsys account to add STUN and TURN to ensure reliable WebRTC connectivity.

Finally, we’ll create our own js file called sharebrowse.js to house our custom script for this demo. You can code this under the html file as well if that’s your preference. I will add the js file to a js folder in the same location. Your script tag should work like this and should be located under the SDK’s loaded above:

<script src="js/sharebrowse.js"></script>

Within a self-initiating function in sharebrowse.js we’ll begin adding our custom code. I will start with first creating an instance of a room by giving it a random id number. This can be replaced by a database if you wanted to have some security involved in this.

(function(){
 
    var urlargs         = urlparams();
    var number        = urlargs.number || Math.floor(Math.random()*999+1);
  
    var my_link = location.href.split('?')[0] + '?call=' + number;
    $("#myCallLink").text(my_link);
 
    // Get URL Params
    function urlparams() {
        var params = {};
        if (location.href.indexOf('?') < 0) return params;
 
        PUBNUB.each(
            location.href.split('?')[1].split('&'),
            function(data) { var d = data.split('='); params[d[0]] = d[1]; }
        );
 
        return params;
    }

})();

The “number” container holds the random room number that we will use to uniquely create a connection point with the local user, it’s our room id or “phone number”, if you will.

There is also a “urlargs” container which we’ll use to detect a “call=” parameter with our room id for when a remote user attempts to connect directly.

Now let’s create our connection workflow using the PubNub WebRTC SDK.

//Connect Local user
    function login() {
 
        var phone = window.phone = PHONE({
            number        : number,
            publish_key   : 'pub-c-your-pubnub-publish-key',
            subscribe_key : 'sub-c-your-pubnub-subscribe-key',
            onaddstream   : addRemoteVideo
        });
 
    }
 
    //Adds the remote users video feed to our remote video window.
    function addRemoteVideo( obj ){
        $("#videoRemote").attr( "src", window.URL.createObjectURL(obj.stream) );
    }
 
   //If we have our Room Number, let's log in with it.
    if( number ){
        login();
    }

The login function will be our initialization method for setting up our call service. The variable container “phone” will hold the reference to the PHONE object that the WebRTC SDK creates. This will contain our connection logic for our conversation with the other user. You will need to pass the number variable which we created at the top of our app, along with your publish and subscribe keys from your PubNub accounts. Finally, we need to add a callback method for “onaddstream” which will be called by the SDK when the remote user connects directly to us. Pass it a reference to a function called “addRemoteVideo” which sits below the login function. The “addRemoteVideo” function simply receives the remote user’s stream information object from the SDK and passes the stream object to the “videoRemote” window to visually show the remote user’s live video feed.

Now going back to our login function, we want to add more callbacks to handle our camera and url sharing events.

function login() {
 
        var phone = window.phone = PHONE({
            number        : number,
            publish_key   : 'pub-c-your-pubnub-dash-publish-key',
            subscribe_key : 'sub-c-your-pubnub-dash-subscribe-key',
            onaddstream   : addRemoteVideo
        });
 
        //called when the local user connection is established with the service. 
        phone.ready( function(){
            // Auto Call - if the URL has a call argument to another user, initiate the call right away.
            if ('call' in urlargs) {
                var id = urlargs['call'];
                makeCall(id);
            }
            //set local user camera.
            $("#videoSelf").attr( "src", window.URL.createObjectURL(phone.mystream) );
        });
 
    }
 
    //Adds the remote users video feed to our remote video window.
    function addRemoteVideo( obj ){
        $("#videoRemote").attr( "src", window.URL.createObjectURL(obj.stream) );
    }
 
   //If we have our Room Number, let's log in with it.
    if( number ){
        login();
    }

Add a method to the phone object called “ready” which the SDK will call exclusively when the local user successfully connected to the PubNub service. Within the ready function body, add a call to set our local camera view so we can see the camera feed of our sexy self.

In the same method, add a condition that checks the “urlargs” container we made above to detect the call parameter and verify if the user is calling someone directly or not. If the call is found, we can go ahead and connect the local user directly to the id number in the call parameter. I send the ID to another function called “makeCall” which will actually initiate the connection to the remote user at the number provided, here’s the code:

// Request fresh TURN/STUN servers from XirSys
    function get_xirsys_servers() {
        var servers;
        $.ajax({
            type: 'POST',
            url: 'https://service.xirsys.com/getIceServers',
            data: {
                room: 'default',
                application: 'default',
                domain: 'www.thedomainyoucreated.com',
                ident: 'yourxirsysident',
                secret: 'secret-token-from-xirsys-dash',
            },
            success: function(res) {
                res = JSON.parse(res);
                if (!res.e) servers = res.d.iceServers;
            },
            async: false
        });
        return servers;
    }
 
    //Request to connect to Remote User
    function makeCall( remoteId ){
        if (!window.phone) alert("Login First!");
        else if( !remoteId ) alert("The call id is missing or invalid!");
        else phone.dial( remoteId, get_xirsys_servers() );
    }

Within the “makeCall” function, we add a couple of fail safe conditions: one to look for the phone object that the WebRTC SDK makes, and one to look for a valid “roomId”. If the required info is there, then we can safely create the connection. To create the connection, you must call the “dial” method from the “makeCall” function and pass it 2 arguments – the “remoteId” we got from the “urlargs” container that had the call parameter, and your Xirsys STUN and TURN servers, to guarantee that we can initiate a handshake with the remote user. The Xirsys servers are acquired from a function called “get_xirsys_servers” which return your STUN and TURN URLs for the SDK. You can learn more on this from part 1.

Going back to our “login” method, we’re going to add the callbacks for the direct connection process.

function login() {
 
        var phone = window.phone = PHONE({
            number        : number,
            publish_key   : 'pub-c-your-pubnub-publish-key',
            subscribe_key : 'sub-c-your-pubnub-subscribe-key',
            onaddstream   : addRemoteVideo
        });
 
        //called when the local user connection is established with the service. 
        phone.ready( function(){
            // Auto Call - if the URL has a call argument to another user, initiate the call right away.
            if ('call' in urlargs) {
                var id = urlargs['call'];
                makeCall(id);
            }
            //set local user camera.
            $("#videoSelf").attr( "src", window.URL.createObjectURL(phone.mystream) );
        });
 
        //When the remote user is ready to chat we set the callback functions 
        phone.receive(function(session){
            session.ended( sessionEnd );
            session.message(onReceiveMsg);
        });
 
        // When we connect to the other user start the session. 
        phone.callstatus(function(obj){
            var status = obj.status;
           switch( status ){
                case "connected":
                    sessionStart();
                    break;
            } 
        });
 
    }
 
    //Adds the remote user's video feed to our remote video window.
    function addRemoteVideo( obj ){
        $("#videoRemote").attr( "src", window.URL.createObjectURL(obj.stream) );
    }
 
   //If we have our Room Number, let's log in with it.
    if( number ){
        login();
    }

Add 2 callback methods, the “receive” and the “callstatus” method. The “receive” method is called exclusively when the service session with the remote user is established, and the “callstatus” tells us that the WebRTC session has begun. I wait for the call status so I can clear out anything or resolve anything on the screen that needs to change in order to set the application up for sharing. I’m calling a custom function called “sessionStart” to handle this for us.

In the “receive” method, we need to send the SDK our custom function for the “ended” callback. The SDK will call this function when the session has been ended, either by the remote user closing their browser on the other end or us closing the connection through an SDK call. We will call this function “sessionEnd”.

The next function we need to provide is for the “message” callback. The SDK will call this function when a message has been sent from the remote user. The SDK will send an object which contains the custom data that was passed over through the service from the other user. I’ll call this function “onReceiveMsg”, this function will be our URL sharing bridge.

Now let’s create these happy functions.

//We are now in a session, this will enable all the interface to allow sharing of URL's
    function sessionStart(){
        $("#sharedWin").css( "height", $("#winContainer").css("height") );
    }
 
    //Our session has ended, this will hide and disable all sharing capabilities and show our connection URL again.
    function sessionEnd(){
        $("#urlInput").val("");
        setSharedView("");
    }
 
    //When we receive the URL message display the domain path that is being shared.
    function onReceiveMsg( session, msg ) {
        if('url' in msg){
            var path = msg.url;
            if(path.indexOf("http://") == -1 && path.indexOf("https://") == -1 ) path = "http://"+path;
            setSharedView(path);
        }
    }
 
    //updates the iframe with the url path.
    function setSharedView( path ){
        $("#sharedWin").attr('src', path);
    }

As I explained earlier, the “sessionStart” simply helps me resolve anything on the screen that needs to be updated to allow for sharing. In this case, I wanted to make sure my iFrame was set to the correct height of my div container after it’s resolved on the screen. But you can add any bells and whistles to notify the user that they’re about to have some sharing fun.

The “sessionEnd” is basically a cleanup function after the session ended. It clears the string in the “urlInput” field we created and also resets our iFrame’s src path to blank by calling another function I added called “setSharedView”. Like the “sessionStart” function, you can add any bells and whistles to notify the user about the end of their session.

For the “onReceiveMsg” I receive a “msg” object and look for a property called “url” within it. This is a custom object we are going to pass through the service that holds the URL string that the other user will share. I also added a simple failsafe to detect if http or https was used and if not, we’ll need to add it. Once we get our URL we simply pass it to our “setSharedView” to resolve it in our iFrame.

Bam almost done! But we are missing the other end of the message delivery which is our Send message call and our Mouselistener for our “urlInput” field.

//Send a message to the other user.
    function sendMsg(objMsg){
        phone.send(objMsg);
        return true;
    }
 
    //Listener for our URL field to share.
    $("#urlInput").keypress(function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if( keycode == "13"){
            var urlPath = $(this).val();
            if( urlPath.indexOf("http://") == -1 && urlPath.indexOf("https://") == -1 ) urlPath = "http://"+urlPath;
            var b = sendMsg({url:urlPath});
            if( b ) setSharedView( urlPath );
        }


Add a function called “sendMsg”, this simply sends the actual object you saw above that are receiving in our “onReceiveMsg” function. To send you have to simply call the “send” method from the SDK.

Using jQuery I’m going to listen to the enter key on the “urlInput” field we added earlier. If the key pressed is the enter/return key on the user’s keyboard, we grab the text in the field, analyze it and package it in an object containing a “url” property with our path in the object. In addition, we want to also display what we sent so be sure that if our URL was sent successfully, show the path by calling “setSharedView” for ourselves. The SDK will not deliver your message back to you in the receive calls, that’s our responsibility.

IMPORTANT: Things to keep in mind as you test. There are many sites that do not like to be embedded outside of their domain like google.com and yahoo.com, they will block this app from doing that. However, some websites like Youtube will not allow you to embed youtube.com but do provide embed paths to their videos directly.
Example From Youtube:

Just be aware of which sites are friendly and which sites are not.

There you go, you are done baby! Now upload this beast and have some URL sharing fun!!! Many thanks to our PubNub buds for providing the SDKs for us. If you have any questions or suggestions feel free to post or contact us.

We are still cooking up more posts to feed that WebRTC hunger so keep checking up.