You can use any signaling implementation with any WebRTC Experiment; whether it is XMPP/SIP or PHP/MySQL or Socket.io/WebSockets or WebSync/SignalR or PeerServer/SignalMaster or other gateway.
=
Your server side code can be as simple as possible like this:
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
socket.broadcast.emit('message', data);
});
});
You can even use existing services like (for server side code only!):
- https://github.com/andyet/signalmaster
- https://github.com/peers/peerjs-server
- https://github.com/SignalR/SignalR
- http://millermedeiros.github.io/js-signals/
- https://github.com/sockjs/sockjs-client
=
There are dozens of WebRTC Experiments and Libraries; you can use any existing signaling server with any WebRTC Experiment/Library!
You just need to understand how signaling is implemented in WebRTC Experiments:
- All WebRTC Experiments has
openSocket
method; that can be defined in the HTML page; which allows you override/use any signaling implementation there! - All WebRTC Libraries has a public method i.e.
openSignalingChannel
; which can also be overridden/defined in the HTML page; also you can override it to easily use any signaling implementation exists out there!
Now you understood how default implementations can be overridden; it is time to understand how to override for any signaling implementation exists out there!
=
This array-like object will store onmessage
callbacks.
var onMessageCallbacks = {};
var websocket = new WebSocket('wss://something:port/');
var socket = io.connect('https://domain:port/');
var firebase = new Firebase('https://user.firebaseio.com/' + connection.channel);
For socket.io; you can pass default channel as URL parameter:
var socket = io.connect('https://domain:port/?channel=' + connection.channel);
Capture server messages:
websocket.onmessage = function (event) {
onMessageCallBack(event.data);
};
socket.on('message', function (data) {
onMessageCallBack(data);
});
firebase.on('child_added', function (snap) {
onMessageCallBack(snap.val());
snap.ref().remove(); // for socket.io live behaviour
});
and onMessageCallBack
:
function onMessageCallBack(data) {
data = JSON.parse(e.data);
if (data.sender == connection.userid) return;
if (onMessageCallbacks[data.channel]) {
onMessageCallbacks[data.channel](data.message);
};
}
connection.openSignalingChannel = function (config) {
var channel = config.channel || this.channel;
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
return {
send: function (message) {
websocket.send(JSON.stringify({
sender: connection.userid,
channel: channel,
message: message
}));
},
channel: channel
};
};
Read more here.
=
openSignalingChannel
for RTCMultiConnection.js and DataChanel.js (Client-Side Code)
Putting above 4-steps together! Here is your browser side code that overrides default signaling implementations:
var onMessageCallbacks = {};
var socketio = io.connect('http://localhost:8888/');
socketio.on('message', function(data) {
if(data.sender == connection.userid) return;
if (onMessageCallbacks[data.channel]) {
onMessageCallbacks[data.channel](data.message);
};
});
connection.openSignalingChannel = function (config) {
var channel = config.channel || this.channel;
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
return {
send: function (message) {
socketio.emit('message', {
sender: connection.userid,
channel: channel,
message: message
});
},
channel: channel
};
};
=
var onMessageCallbacks = {};
var currentUserUUID = Math.round(Math.random() * 60535) + 5000;
var socketio = io.connect('http://localhost:8888/');
socketio.on('message', function(data) {
if(data.sender == currentUserUUID) return;
if (onMessageCallbacks[data.channel]) {
onMessageCallbacks[data.channel](data.message);
};
});
var config = {
openSocket = function (config) {
var channel = config.channel || 'main-channel';
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
return {
send: function (message) {
socketio.emit('message', {
sender: currentUserUUID,
channel: channel,
message: message
});
},
channel: channel
};
}
};
=
// global stuff
var onMessageCallbacks = {};
var currentUserUUID = Math.round(Math.random() * 60535) + 5000;
var websocket = new WebSocket('ws://localhost:8888/');
websocket.onmessage = function(e) {
data = JSON.parse(e.data);
if(data.sender == currentUserUUID) return;
if (onMessageCallbacks[data.channel]) {
onMessageCallbacks[data.channel](data.message);
};
};
// overriding "openSignalingChannel" method
connection.openSignalingChannel = function (config) {
var channel = config.channel || this.channel;
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
// directly returning socket object using "return" statement
return {
send: function (message) {
websocket.send(JSON.stringify({
sender: currentUserUUID,
channel: channel,
message: message
}));
},
channel: channel
};
};
=
-
The object returned by overridden
openSignalingChannel
oropenSocket
method MUST return an object with two things:send
method. Used to send data via signaling gateway.channel
object. Used for video-conferencing. If you skip it; it will make one-to-many instead of many-to-many.
-
onmessage
oron('message', callback)
MUST have same code as you can see a few lines above.
openSocket
method can return socket
object in three ways:
- Directly returning using
return
statement. - Passing back over
config.callback
object. - Passing back over
config.onopen
object.
Second option i.e. config.callback
is preferred.
var config = {
openSocket: function (config) {
var channel = config.channel || location.href.replace(/\/|:|#|%|\.|\[|\]/g, '');
var socket = new Firebase('https://chat.firebaseIO.com/' + channel);
socket.channel = channel;
socket.on("child_added", function (data) {
config.onmessage && config.onmessage(data.val());
});
socket.send = function (data) {
this.push(data);
};
socket.onDisconnect().remove();
// first option: returning socket object using "return" statement!
return socket;
}
};
var config = {
openSocket: function (config) {
var SIGNALING_SERVER = 'wss://wsnodejs.nodejitsu.com:443';
var channel = config.channel || location.href.replace(/\/|:|#|%|\.|\[|\]/g, '');
var websocket = new WebSocket(SIGNALING_SERVER);
websocket.channel = config.channel;
websocket.onopen = function () {
websocket.push(JSON.stringify({
open: true,
channel: config.channel
}));
// second option: returning socket object using "config.callback" method
if (config.callback)
config.callback(websocket);
};
websocket.onmessage = function (event) {
config.onmessage(JSON.parse(event.data));
};
websocket.push = websocket.send;
websocket.send = function (data) {
websocket.push(JSON.stringify({
data: data,
channel: config.channel
}));
};
}
};
var config = {
openSocket: function (config) {
// --------
websocket.onopen = function () {
// --------
// third option: returning socket object using "config.onopen" method
if (config.onopen)
config.onopen(websocket);
};
// --------
}
};
=
How to use WebSync for Signaling?
<script src="fm.js"> </script>
<script src="fm.websync.js"> </script>
<script src="fm.websync.subscribers.js"> </script>
<script src="fm.websync.chat.js"> </script>
// www.RTCMultiConnection.org/latest.js
var connection = new RTCMultiConnection();
// ------------------------------------------------------------------
// start-using WebSync for signaling
var onMessageCallbacks = {};
var client = new fm.websync.client('websync.ashx');
client.setAutoDisconnect({
synchronous: true
});
client.connect({
onSuccess: function () {
client.join({
channel: '/chat',
userId: connection.userid,
userNickname: connection.userid,
onReceive: function (event) {
var message = JSON.parse(event.getData().text);
if (onMessageCallbacks[message.channel]) {
onMessageCallbacks[message.channel](message.message);
}
}
});
}
});
connection.openSignalingChannel = function (config) {
var channel = config.channel || this.channel;
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
return {
send: function (message) {
client.publish({
channel: '/chat',
data: {
username: connection.userid,
text: JSON.stringify({
message: message,
channel: channel
})
}
});
}
};
};
// end-using WebSync for signaling
// ------------------------------------------------------------------
// check existing sessions
connection.connect();
// open new session
document.getElementById('open-new-session').onclick = function() {
connection.open();
};
=
First Step: Create Hub class:
public class WebRtcHub3: Hub {
public void Send(string message) {
Clients.All.onMessageReceived(message);
}
}
Second Step: Client side stuff:
var onMessageCallbacks = {};
var connection = new RTCMultiConnection();
var hub = $.connection.webRtcHub3;
$.support.cors = true;
$.connection.hub.url = '/signalr/hubs';
hub.client.onMessageReceived = function (message) {
var message = JSON.parse(message);
if (onMessageCallbacks[message.channel]) {
onMessageCallbacks[message.channel](message.message);
}
};
// start the hub
$.connection.hub.start();
Third Step: Overriding openSignalingChannel
method:
connection.openSignalingChannel = function (config) {
var channel = config.channel || this.channel;
onMessageCallbacks[channel] = config.onmessage;
if (config.onopen) setTimeout(config.onopen, 1000);
return {
send: function (message) {
message = JSON.stringify({
message: message,
channel: channel
});
hub.server.send(message);
}
};
};
=
new window.Firebase('https://' + connection.firebase + '.firebaseIO.com/' + connection.channel).once('value', function (data) {
var isRoomPresent = data.val() != null;
if (!isRoomPresent) {
connection.open(connection.sessionid);
} else {
connection.join(connection.sessionid);
}
});
var socket = io.connect('/');
socket.on('presence', function (isChannelPresent) {
if (!isChannelPresent)
connection.opne(connection.sessionid);
else
connection.join(connection.sessionid);
});
socket.emit('presence', channel);
Socket.io over Node.js demos can be found here.
var SIGNALING_SERVER = 'wss://wsnodejs.nodejitsu.com:443';
var websocket = new WebSocket(SIGNALING_SERVER);
websocket.onmessage = function (event) {
var data = JSON.parse(event.data);
if (data.isChannelPresent == false) {
connection.open(connection.sessionid);
} else {
connection.join(connection.sessionid);
}
};
websocket.onopen = function () {
websocket.send(JSON.stringify({
checkPresence: true,
channel: connection.channel
}));
};
WebSocket over Node.js demos can be found here.
=
You can find many other good examples here:
http://www.RTCMultiConnection.org/docs/openSignalingChannel/
=
- https://www.webrtc-experiment.com/docs/WebRTC-Signaling-Concepts.html
- http://www.RTCMultiConnection.org/FAQ/
- http://www.RTCMultiConnection.org/docs/sessionid/
- http://www.RTCMultiConnection.org/docs/channel-id/
=
WebRTC Experiments are released under MIT licence . Copyright (c) 2013 Muaz Khan.