WebRTC is a standard being put forth by the W3C to create an API to deliver realtime data, audio, and video between browsers in a peer to peer fashion.
Unfortunately, there is a fair amount of complexity involved in sending descriptions of the data, audio and video channels from peer to peer.
Fortunately, a few JavaScript libraries have popped up to aide in this. One, in particular that we'll go over is PeerJS.
The first step is to do our normal getUserMedia, gaining access to a stream from the user's camera and microphone:
/* Get User Media */ let my_stream = null; // Constraints - what do we want? let constraints = { audio: false, video: true } window.addEventListener('load', function() { // Prompt the user for permission, get the stream navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { /* Use the stream */ // Attach to our video object var videoElement = document.getElementById('myvideo'); videoElement.srcObject = stream; // Global for stream my_stream = stream; // Wait for the stream to load enough to play videoElement.onloadedmetadata = function(e) { videoElement.play(); }; }) .catch(function(err) { /* Handle the error */ alert(err); }); });This requires that a video element be present on the page with the id of "myvideo":
<video id="myvideo" width="320" height="240"></video>Also, it should be run inside an onload event as the video element isn't available until it is rendered.
The next step is to include the PeerJS JavaScript library. (Download it from here: PeerJS GitHub, either get the peer.min.js from the dist directory or download a release and get it from the same sample place.)
<script src="peer.min.js"></script>and to create an instance of a "peer" and connect to the PeerJS server
// We'll use a global variable to hold on to our id from PeerJS var peer_id = null; // I setup a peer server on a Digital Ocean instance for our use, you can use that with the following constructor: var peer = new Peer({host: 'liveweb-new.itp.io', port: 9000, path: '/'}); // Get an ID from the PeerJS server peer.on('open', function(id) { console.log('My peer ID is: ' + id); peer_id = id; }); peer.on('error', function(err) { console.log(err); });
This should be enough for us to receive a "call"
peer.on('call', function(incoming_call) { console.log("Got a call!"); incoming_call.answer(my_stream); // Answer the call with our stream from getUserMedia incoming_call.on('stream', function(remoteStream) { // we receive a getUserMedia stream from the remote caller // And attach it to a video object var ovideoElement = document.getElementById('othervideo'); ovideoElement.src = window.URL.createObjectURL(remoteStream) || remoteStream; ovideoElement.setAttribute("autoplay", "true"); ovideoElement.play(); }); });
PeerJS runs a server which aides in the transmission of audio/video/data channel information but we'll still need to run a server to introduce the users to each-other. We can use a normal WebSocket server as we have been doing and use it to share the PeerJS ID value between users.
Here is a basic server that relays the peer_id's:
// HTTPS Portion - Required for getUserMedia var https = require('https'); var fs = require('fs'); // Using the filesystem module var url = require('url'); // Include your key and certificate in the same directory var options = { key: fs.readFileSync('my-key.pem'), cert: fs.readFileSync('my-cert.pem') }; function handleIt(req, res) { var parsedUrl = url.parse(req.url); var path = parsedUrl.pathname; if (path == "/") { path = "index.html"; } fs.readFile(__dirname + path, // Callback function for reading function (err, fileContents) { // if there is an error if (err) { res.writeHead(500); return res.end('Error loading ' + req.url); } // Otherwise, send the data, the contents of the file res.writeHead(200); res.end(fileContents); } ); // Send a log message to the console console.log("Got a request " + req.url); } var httpServer = https.createServer(options, handleIt); httpServer.listen(8080); // WebSocket Portion // WebSockets work with the HTTP server var io = require('socket.io').listen(httpServer); var connectedSockets = []; // Register a callback function to run when we have an individual connection // This is run for each individual user that connects io.sockets.on('connection', // We are given a websocket object in our function function (socket) { console.log("We have a new client: " + socket.id); // Add to the connectedSockets Array connectedSockets.push(socket); socket.on('peer_id', function(data) { console.log("Received: 'peer_id' " + data); // We can save this in the socket object if we like socket.peer_id = data; console.log("Saved: " + socket.peer_id); // We can loop through these if we like for (var i = 0; i < connectedSockets.length; i++) { console.log("loop: " + i + " " + connectedSockets[i].peer_id); } // Tell everyone my peer_id socket.broadcast.emit('peer_id',data); }); socket.on('disconnect', function() { console.log("Client has disconnected"); var indexToRemove = connectedSockets.indexOf(socket); connectedSockets.splice(indexToRemove, 1); // Remove 1 from position, indexToRemove }); } );
To use the server, we'll use our normal Socket.io code on the client side:
/* Socket Server - Socket.io */ var socket = io.connect(); socket.on('connect', function() { console.log("Connected"); // Our socket is connected, send out our peer id here console.log("sending out our peer id"); socket.emit("peer_id",peer_id); }); // Receive other folks peer ids socket.on('peer_id', function (data) { console.log("Got a new peer: " + data); // Call them with our stream console.log("Calling peer: " + data); var call = peer.call(data, my_stream); call.on('stream', function(remoteStream) { console.log("Got remote stream"); var ovideoElement = document.createElement('video'); ovideoElement.src = window.URL.createObjectURL(remoteStream) || remoteStream; ovideoElement.setAttribute("autoplay", "true"); ovideoElement.play(); }); });Putting these pieces all together, we get something like this: webrtc_new.zip.