Skip to main content

WebRTC Introduction - Client Side Signalling

Shenzhen, China

WIP

Resources:

WebRTC Introduction - Video Chat

Get Video Source

Get Video from Webcam with getUserMedia():

chat.js

const chatLobby = document.getElementById('chat-lobby')
const chatRoom = document.getElementById('room-name')
const chatJoin = document.getElementById('join')
const chatLocal = document.getElementById('video-local')

let constraints = {
audio: false,
video: { width: 320, height: 180 },
}

const browserSupportsMedia = () => {
return navigator.mediaDevices.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.mzGetUserMedia
}

const fetchLocalStream = () => {
navigator.mediaDevices
.getUserMedia(constraints)
.then(function (stream) {
userStream = stream;
chatLobby.style = "display:none";
chatLocal.srcObject = stream;
chatLocal.onloadedmetadata = function (e) {
chatLocal.play();
}
})
.catch(function (err) {
/* handle the error */
alert('ERROR :: ' + error.name);
})
}

chatJoin.addEventListener('click', function() {
if (chatRoom.value == "") {
alert('INFO :: Please enter a room name first!')
} else {
fetchLocalStream()
}
})

Signalling with Websockets

Use socket.io to manage connections:

index.js

const express = require("express");
const socket = require("socket.io");

const port = 6969

var app = express();

var server = app.listen(port, function () {
console.log('INFO :: Webserver is running on :: http://localhost:' + port)
});

app.use(express.static("public"));

var io = socket(server);

io.on("connection", function (socket) {
console.log("INFO :: Websocket connection established :: ", socket.id)

socket.on("join", function (roomName) {
// check if is already full = 2 participants
let rooms = io.sockets.adapter.rooms;
let room = rooms.get(roomName);
// if room doesn't exists create it
if(room == undefined) {
socket.join(roomName)
console.log('INFO :: Participant created room ::', roomName)
// if room exists and only has 1 participant - join
} else if(room.size == 1) {
socket.join(roomName)
console.log('INFO :: Participant joined room ::', roomName)
} else {
console.log('WARNING :: Room', roomName, 'is full!')
}
})
});

Use the websocket connection to emit the join event when a user clicked the join button:

chat.js

const socket = io.connect("http://localhost:6969")

chatJoin.addEventListener('click', function() {
if (chatRoom.value == "") {
alert('INFO :: Please enter a room name first!')
} else {
socket.emit("join", chatRoom.value)
fetchLocalStream()
}
})

Start the application and open the application frontend from three different devices:

npm start

> webrtc-intro@1.0.0 start
> node index.js

INFO :: Webserver is running on :: http://localhost:6969
INFO :: Websocket connection established :: _I-Guhml-JEa9VJwAAAF
INFO :: Participant created room :: test
INFO :: Websocket connection established :: TAaYXAYJDbm4lDP5AAAH
INFO :: Participant joined room :: test
INFO :: Websocket connection established :: lAgg07QE2DSltIwsAAAJ
WARNING :: Room test is full!

Scaffolding the Signalling Server

index.js

io.on("connection", function (socket) {
console.log("INFO :: Websocket connection established :: ", socket.id)

socket.on("join", function (roomName) {
// check if is already full = 2 participants
let rooms = io.sockets.adapter.rooms;
let room = rooms.get(roomName);
// if room doesn't exists create it
if(room == undefined) {
socket.join(roomName)
socket.emit('created')
} else if(room.size == 1) {
socket.join(roomName)
socket.emit('joined')
} else {
socket.emit('occupied')
}
})

// emit ready state to room when created
socket.on('ready', function(roomName) {
socket.broadcast.to(roomName).emit('ready')
console.log('INFO :: Room is ready')
})

// establish connection between candidates (ICE)
socket.on('candidate', function(candidate, roomName) {
socket.broadcast.to(roomName).emit('candidate', candidate)
console.log('INFO :: Candidate ready to establish connection')
})

// make offer to other participant
socket.on('offer', function(offer, roomName) {
socket.broadcast.to(roomName).emit('offer', offer)
console.log('INFO :: Offer to establish connection')
})

// send answer to connection offer
socket.on('answer', function(answer, roomName) {
socket.broadcast.to(roomName).emit('answer', answer)
console.log('INFO :: Answer to connection offer')
})

});

Client Side Signalling Events

The server now emits each of those events through the websocket connection. We now need to handle those events on the client side:

Created, Joined and Occupied

chat.js

let creator = false

chatJoin.addEventListener('click', function() {
if (chatRoom.value == "") {
alert('INFO :: Please enter a room name first!')
} else {
socket.emit("join", chatRoom.value)
}
})

// Room created event -> fetch video
socket.on('created', function() {
creator = true
fetchLocalStream()
})

// Room joined event -> fetch video
socket.on('joined', function() {
creator = false
fetchLocalStream()
})

// Room is full event
socket.on('occupied', function() {
alert("WARNING :: Room occupied. Please try again later.")
})