videoconferencing-client
Version:
Reliable signaling implementation for video-conferencing WebRTC experiment.
287 lines (238 loc) • 9.13 kB
JavaScript
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// Experiments - github.com/muaz-khan/WebRTC-Experiment
// This library is known as multi-user connectivity wrapper!
// It handles connectivity tasks to make sure two or more users can interconnect!
var conference = function(config) {
var self = {
userToken: uniqueToken()
};
var channels = '--',
isbroadcaster;
var isGetNewRoom = true;
var sockets = [];
var defaultSocket = {};
function openDefaultSocket() {
defaultSocket = config.openSocket({
onmessage: onDefaultSocketResponse,
callback: function(socket) {
defaultSocket = socket;
}
});
}
function onDefaultSocketResponse(response) {
if (response.userToken == self.userToken) return;
if (isGetNewRoom && response.roomToken && response.broadcaster) config.onRoomFound(response);
if (response.newParticipant && self.joinedARoom && self.broadcasterid == response.userToken) onNewParticipant(response.newParticipant);
if (response.userToken && response.joinUser == self.userToken && response.participant && channels.indexOf(response.userToken) == -1) {
channels += response.userToken + '--';
openSubSocket({
isofferer: true,
channel: response.channel || response.userToken
});
}
// to make sure room is unlisted if owner leaves
if (response.left && config.onRoomClosed) {
config.onRoomClosed(response);
}
}
function openSubSocket(_config) {
if (!_config.channel) return;
var socketConfig = {
channel: _config.channel,
onmessage: socketResponse,
onopen: function() {
if (isofferer && !peer) initPeer();
sockets[sockets.length] = socket;
}
};
socketConfig.callback = function(_socket) {
socket = _socket;
this.onopen();
};
var socket = config.openSocket(socketConfig),
isofferer = _config.isofferer,
gotstream,
video = document.createElement('video'),
inner = {},
peer;
var peerConfig = {
attachStream: config.attachStream,
onICE: function(candidate) {
socket.send({
userToken: self.userToken,
candidate: {
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: JSON.stringify(candidate.candidate)
}
});
},
onRemoteStream: function(stream) {
if (!stream) return;
video[moz ? 'mozSrcObject' : 'src'] = moz ? stream : webkitURL.createObjectURL(stream);
video.play();
_config.stream = stream;
onRemoteStreamStartsFlowing();
},
onRemoteStreamEnded: function(stream) {
if (config.onRemoteStreamEnded)
config.onRemoteStreamEnded(stream, video);
}
};
function initPeer(offerSDP) {
if (!offerSDP) {
peerConfig.onOfferSDP = sendsdp;
} else {
peerConfig.offerSDP = offerSDP;
peerConfig.onAnswerSDP = sendsdp;
}
peer = RTCPeerConnection(peerConfig);
}
function afterRemoteStreamStartedFlowing() {
gotstream = true;
if (config.onRemoteStream)
config.onRemoteStream({
video: video,
stream: _config.stream
});
if (isbroadcaster && channels.split('--').length > 3) {
/* broadcasting newly connected participant for video-conferencing! */
defaultSocket.send({
newParticipant: socket.channel,
userToken: self.userToken
});
}
}
function onRemoteStreamStartsFlowing() {
if (navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i)) {
// if mobile device
return afterRemoteStreamStartedFlowing();
}
if (!(video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA || video.paused || video.currentTime <= 0)) {
afterRemoteStreamStartedFlowing();
} else setTimeout(onRemoteStreamStartsFlowing, 50);
}
function sendsdp(sdp) {
socket.send({
userToken: self.userToken,
sdp: JSON.stringify(sdp)
});
}
function socketResponse(response) {
if (response.userToken == self.userToken) return;
if (response.sdp) {
inner.sdp = JSON.parse(response.sdp);
selfInvoker();
}
if (response.candidate && !gotstream) {
if (!peer) console.error('missed an ice', response.candidate);
else
peer.addICE({
sdpMLineIndex: response.candidate.sdpMLineIndex,
candidate: JSON.parse(response.candidate.candidate)
});
}
if (response.left) {
if (peer && peer.peer) {
peer.peer.close();
peer.peer = null;
}
}
}
var invokedOnce = false;
function selfInvoker() {
if (invokedOnce) return;
invokedOnce = true;
if (isofferer) peer.addAnswerSDP(inner.sdp);
else initPeer(inner.sdp);
}
}
function leave() {
var length = sockets.length;
for (var i = 0; i < length; i++) {
var socket = sockets[i];
if (socket) {
socket.send({
left: true,
userToken: self.userToken
});
delete sockets[i];
}
}
// if owner leaves; try to remove his room from all other users side
if (isbroadcaster) {
defaultSocket.send({
left: true,
userToken: self.userToken,
roomToken: self.roomToken
});
}
if (config.attachStream) config.attachStream.stop();
}
window.addEventListener('beforeunload', function() {
leave();
}, false);
window.addEventListener('keyup', function(e) {
if (e.keyCode == 116)
leave();
}, false);
function startBroadcasting(transmitRoomOnce) {
defaultSocket && defaultSocket.send({
roomToken: self.roomToken,
roomName: self.roomName,
broadcaster: self.userToken
});
if (transmitRoomOnce) return;
setTimeout(startBroadcasting, 3000);
}
function onNewParticipant(channel) {
if (!channel || channels.indexOf(channel) != -1 || channel == self.userToken) return;
channels += channel + '--';
var new_channel = uniqueToken();
openSubSocket({
channel: new_channel
});
defaultSocket.send({
participant: true,
userToken: self.userToken,
joinUser: channel,
channel: new_channel
});
}
function uniqueToken() {
var s4 = function() {
return Math.floor(Math.random() * 0x10000).toString(16);
};
return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
}
openDefaultSocket();
return {
createRoom: function(_config) {
_config = _config || {};
self.roomName = _config.roomName || 'Anonymous';
self.roomToken = _config.roomToken || uniqueToken();
if (_config.userToken) {
self.userToken = _config.userToken;
}
isbroadcaster = true;
isGetNewRoom = false;
startBroadcasting(_config.transmitRoomOnce);
},
joinRoom: function(_config) {
_config = _config || {};
self.roomToken = _config.roomToken;
isGetNewRoom = false;
self.joinedARoom = true;
self.broadcasterid = _config.joinUser;
openSubSocket({
channel: self.userToken
});
defaultSocket.send({
participant: true,
userToken: self.userToken,
joinUser: _config.joinUser
});
},
leaveRoom: leave
};
};