Appklony
Appklony

Reputation: 3

Is RTCPeerConnection can be used for live cam services or i need a media server?

I am trying to setup a web-based Live webcam streaming service where a user can broadcast live via webcam (Web based Only) i.e:

User A Starts a Live Broadcast at

http://www.example.com/user-a

User B,C,D,E and count goes on visits that url i.e:

http://www.example.com/user-a

and will be able to watch the live broadcast/stream. I was playing around with RTCPeerConnection to create peer to peer to share stream to save bandwidth and server load and i am able to use the following code to go with standard createOffer and createAnswer by sending OfferToReceiveAudio: true, OfferToReceiveVideo: true constraints. It is working all the way fine.

Now i have managed to create new peer connection for every connected peer (viewer). This way any one can broadcast from webcam and others (peers) will be able to watch broadcast. I am confused if this is a scalable process means what if user-a is broadcasting and viewers are 500+, and user-b is broadcasting and viewers are 1000. Is this reliable for live broadcasting. I don't want to setup a media server thats why i am using webrtc thing.

Client

var 
    ws = new WebSocket('wss://www.example.com:443'),
    isRemote = false,
    iStream = null,
    iDescription = null,
    iAnswer = null,
    video = document.querySelector('video'),
    peerConnCfg = {'iceServers': 
     [{'url': 'stun:stun.services.mozilla.com'}, {'url': 'stun:stun.l.google.com:19302'}]
    },
    mediaConstraints = {
        OfferToReceiveAudio: true,
        OfferToReceiveVideo: true
    },
    peer;

    ws.onmessage = function(message){
        var json = JSON.parse(message.data);
        if(isRemote==true){
            if(json.result=="ok"){
                StartWatch(json.sdp, json.ice); 
            }else{
                console.log("Watch start failed...");
            }
        }else{
            if(json.result=="ok" && json.action=="rsdp"){
                peer.setRemoteDescription(new RTCSessionDescription(json.sdp), function () {

                    peer.addIceCandidate(new RTCIceCandidate(json.ice)).then(function(){
                        console.log("added remote ice");
                    }).catch(function(e){
                        console.log(e);
                    });


                }, function(error){
                    console.log("Offer remote failed error : " + error);
                });
            }
            console.log("Remote is false...");
        }
    };

function golive(){
    peer = new RTCPeerConnection(peerConnCfg);
    peer.onicecandidate = function(event){
            console.log("Got candidate...");
         if(event.candidate != null) {
            ws.send(JSON.stringify({'mod' : 'golive', 'channel': 'channel-xyz', 'sdp' : iDescription, 'ice' : event.candidate}));
        }
    };

    navigator.getUserMedia({ audio: true, video: { width: 1280, height: 720 } },
          function(stream) {
             iVideoStream = stream;
             video.src = window.URL.createObjectURL(stream);
             video.onloadedmetadata = function(e){
               video.play();
             };
             peer.addStream(stream);
             peer.createOffer(function(description){
                 //success
                console.log("Got description...");
                peer.setLocalDescription(description, function () {
                    iDescription = description;
                }, function() {console.log('set description error')});
             }, function(){
                 //error
             });
          },
          function(err) {
             console.log("The following error occurred: " + err.name);
          }
    );

}   

function watch(){
    ws.send(JSON.stringify({'mod' : 'watch', 'channel': 'channel-xyz'}));
}   

function StartWatch(sdp, ice){
    peer = new RTCPeerConnection(peerConnCfg);
    peer.onicecandidate = function(event){
            console.log("Got candidate...");
            if(event.candidate != null) {
                ws.send(JSON.stringify({'mod' : 'goliveupdate', 'channel': 'channel-xyz', 'sdp' : iAnswer, 'ice' : event.candidate}));
            }
    };
    peer.onaddstream = function(event){
        peer.addStream(event.stream);
        video.src = window.URL.createObjectURL(event.stream);
        video.onloadedmetadata = function(e){ 
            video.play();
        };
    };

    peer.setRemoteDescription(new RTCSessionDescription(sdp), function () {
                peer.createAnswer(function(answer){
                    peer.setLocalDescription(answer, function() {
                        iAnswer = answer;
                        peer.addIceCandidate(new RTCIceCandidate(ice)).then(function(){
                            console.log("added");
                        }).catch(function(e){
                            console.log(e);
                        });


                    }, function (error) { 
                      console.log("Local desc error: " + error);
                    });
                }, function(error){
                    console.log("Answer Error: " + error);
                });

    }, function(error) { console.log('set description error: ' + error)});

}

//i.e waitForSocketConnection
function _init(){
    setTimeout(function(){
        if (ws.readyState === 1) {
            console.log('Connected to WS');
            <?php 
                if(!isset($_GET['v'])){
                    echo 'golive();';
                }else{
                    echo 'isRemote = true; watch();';
                }
            ?>
            return;
        }else{
            _init();
        }
    }, 5);
}

_init();

Socket Server for Signaling

"use strict";
var fs = require('fs');
var cfg = {
    ssl: true,
    port: 443,
    ssl_key: '/home/csr/example_in.key',
    ssl_cert: '/home/csr/example_in.crt'
};
var app = null;
var httpServ = ( cfg.ssl ) ? require('https') : require('http');
var processRequest = function( req, res ) {
    res.writeHead(200);
    res.end("All glory to WebSockets!\n");
};
if ( cfg.ssl ) {
    app = httpServ.createServer({
            // providing server with  SSL key/cert
            key: fs.readFileSync( cfg.ssl_key ),
            cert: fs.readFileSync( cfg.ssl_cert )

    }, processRequest ).listen( cfg.port );
} else {
    app = httpServ.createServer( processRequest ).listen( cfg.port );
}

var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({server: app, host:'www.example.in'}),

example = {
    channels: [],
    run: function(){
        wss.on('connection', function(ws) {
            //example.channels.push(ws);
            ws.on('message', function(message) {
              //example.respond(ws, message);
              example.respond(ws, JSON.parse(message));
            });
        });

        wss.on('close', function() {
            example.closeChannel(ws);
        });
    },

    closeChannel(ws){
        for (var channel in example.channels) {
            var _channel = example.channels[channel];
            if (_channel[i] == ws){ delete _channel[i]; }
            example.channels[channel] = example.swapArray(_channel);
            if(example.channels && example.channels[channel] && !example.channels[channel].length)
                delete example.channels[channel];
        }
    },

    swapArray: function(arr) {
        var swapped = [],
        length = arr.length;
        for (var i = 0; i < length; i++) {
            if (arr[i])
                swapped[swapped.length] = arr[i];
        }
        return swapped;
    },

    respond : function(ws, message){
        //ws.send(message.mod); return;
        switch(message.mod){
            case "golive":
                example.channels[message.channel] = { 
                  name : message.channel, 
                  socket : ws,
                  sdp : message.sdp,
                  rsdp: null,
                  ice: message.ice,
                  rice: null
                };
                ws.send(JSON.stringify({'result' : 'ok', 'action' : 'connected', 'channel': message.channel}));
            break;
            case "goliveupdate":
                example.channels[message.channel].rsdp = message.sdp;
            example.channels[message.channel].rice = message.ice;
                example.channels[message.channel].socket.send(JSON.stringify({'result' : 'ok', 'action' : 'rsdp', 'sdp' : message.sdp, 'ice' : message.ice, 'message': 'A peer connected'}));
            break;
            case "watch":
                if(example.channels[message.channel]){  
                    example.channels[message.channel].socket.send(JSON.stringify({'result' : 'ok', 'message': 'A peer connected'}));
                    ws.send(JSON.stringify({
                      'result' : 'ok', 
                      'action' : 'connected',
                      'message': 'Connected to ' + message.channel,
                      'sdp' : example.channels[message.channel].sdp,
                      'ice' : example.channels[message.channel].ice
                    }));
                }else{
                    ws.send(JSON.stringify({'result' : 'ok', 'message': 'Unable to connect to ' + message.channel}));
                }
            break;
        }
    }
};

example.run();

Upvotes: 0

Views: 531

Answers (1)

jib
jib

Reputation: 42500

A one-to-one peer connection is inherently not "broadcast". Unless the particular user is in Google Fiber City with a beefy box, serving 500+ video peer connections from one user's upload link isn't going to scale obviously. For that amount of traffic you need a media server.

Upvotes: 0

Related Questions