symple-webrtc-video-chat-demo
Version:
The Symple video chat demo is an example of how to use Symple to build an instant messaging and WebRTC video chat application in about 100 lines of JavaScript. External projects used are AngularJS, Bootstrap, Node.js and Express.
264 lines (220 loc) • 8.15 kB
JavaScript
//
// AngilarJS controller
function SympleChat($scope) {
$scope.client;
$scope.localPlayer;
$scope.remotePlayer;
$scope.remoteVideoPeer;
$scope.handle;
$scope.directUser;
$scope.peers = [];
$scope.messages = [];
$scope.messageText = "";
$scope.errorText = "";
$scope.isLoading = false;
$scope.disableLocalAudio = false; // set true to prevent local feedback loop
$(document).ready(function() {
//
// Client
$scope.client = new Symple.Client(CLIENT_OPTIONS);
$scope.client.on('announce', function(peer) {
//console.log('announce:', peer)
$scope.client.join('public'); // join the public room
$scope.isLoading = false;
$scope.$apply();
});
$scope.client.on('presence', function(p) {
//console.log('presence:', p)
});
$scope.client.on('message', function(m) {
console.log('message:', m)
// Normal Message
if (!m.direct || m.direct == $scope.handle) {
$scope.messages.push({
user: m.from.user,
data: m.data,
to: m.to,
direct: m.direct,
time: Symple.formatTime(new Date)
});
$scope.$apply();
}
else {
console.log('dropping message:', m, m.direct)
}
});
$scope.client.on('command', function(c) {
console.log('command:', c)
if (c.node == 'call:init') {
if (!c.status) {
// Show a dialog to the user asking if they want to accept the call
var e = $('#incoming-call-modal')
e.find('.caller').text('@' + c.from.user)
e.find('.accept').unbind('click').click(function() {
c.status = 200;
$scope.remoteVideoPeer = c.from;
$scope.client.respond(c);
$scope.$apply();
e.modal('hide');
})
e.find('.reject').unbind('click').click(function() {
c.status = 500;
$scope.client.respond(c);
e.modal('hide');
})
e.modal('show')
}
else if (c.status == 200) {
// Handle call accepted
$scope.remoteVideoPeer = c.from;
$scope.startLocalVideo();
$scope.$apply();
}
else if (c.status == 500) {
// Handle call rejected
}
else {
alert('Unknown response status')
}
}
});
$scope.client.on('event', function(e) {
console.log('event:', e)
// Only handle events from the remoteVideoPeer
if (!$scope.remoteVideoPeer || $scope.remoteVideoPeer.id != e.from.id) {
console.log('mismatch event:', e.from, $scope.remoteVideoPeer)
return
}
// ICE SDP
if (e.name == 'call:ice:sdp') {
if (e.sdp.type == 'offer') {
// Create the remote player on offer
if (!$scope.remotePlayer) {
$scope.remotePlayer = createPlayer($scope, 'answerer', '#video .remote-video');
$scope.remotePlayer.play();
}
$scope.remotePlayer.engine.recvRemoteSDP(e.sdp);
}
if (e.sdp.type == 'answer') {
$scope.localPlayer.engine.recvRemoteSDP(e.sdp);
}
}
// ICE Candidate
else if (e.name == 'call:ice:candidate') {
if (e.origin == 'answerer')
$scope.localPlayer.engine.recvRemoteCandidate(e.candidate);
else if (e.origin == 'caller')
$scope.remotePlayer.engine.recvRemoteCandidate(e.candidate);
else
alert('Unknown candidate origin');
}
else {
alert('Unknown event: ' + e.name);
}
});
$scope.client.on('disconnect', function() {
console.log('disconnected')
$scope.isLoading = false;
$scope.errorText = 'Disconnected from the server';
$scope.peers = [];
$scope.$apply();
});
$scope.client.on('error', function(error, message) {
console.log('connection error:', error, message)
$scope.isLoading = false;
$scope.errorText = 'Cannot connect to the server.';
$scope.$apply();
});
$scope.client.on('addPeer', function(peer) {
console.log('add peer:', peer)
$scope.peers.push(peer);
$scope.$apply();
});
$scope.client.on('removePeer', function(peer) {
console.log('remove peer:', peer)
for (var i =0; i < $scope.peers.length; i++) {
if ($scope.peers[i].id === peer.id) {
$scope.peers.splice(i,1);
$scope.$apply();
break;
}
}
});
// Init handle from URL if available
var handle = getHandleFromURL();
if (handle && handle.length) {
$scope.handle = handle;
$scope.login();
}
});
//
// Messaging
$scope.setMessageTarget = function(user) {
console.log('setMessageTarget', user)
$scope.directUser = user ? user : ''
$('#post-message .direct-user').text('@' + $scope.directUser)
$('#post-message .message-text')[0].focus()
}
$scope.sendMessage = function() {
console.log('sendMessage', $scope.messageText);
$scope.client.sendMessage({
data: $scope.messageText,
to: $scope.directUser,
direct: $scope.directUser
});
$scope.messages.push({
to: $scope.directUser,
direct: $scope.directUser,
user: $scope.handle,
data: $scope.messageText,
time: Symple.formatTime(new Date)
});
$scope.messageText = "";
};
// Login
$scope.login = function() {
if (!$scope.handle || $scope.handle.length < 3) {
alert('Please enter 3 or more alphanumeric characters.');
return;
}
$scope.client.options.peer.user = $scope.handle;
console.log('directUser:', $scope.client.options.peer.user)
$scope.client.connect();
$scope.isLoading = true;
//$scope.$apply();
}
//
// Video
$scope.startVideoCall = function(user) {
if (assertGetUserMedia()) {
console.log('startVideoCall', user)
if (user == $scope.handle) {
alert('Cannot video chat with yourself. Please open a new browser window and login with a different handle.');
return;
}
$scope.client.sendCommand({
node: 'call:init',
to: user
})
}
}
$scope.startLocalVideo = function() {
if (assertGetUserMedia()) {
// Init local video player
$scope.localPlayer = createPlayer($scope, 'caller', '#video .local-video');
$scope.localPlayer.play({ localMedia: true, disableAudio: $scope.disableLocalAudio });
// TODO: Set false on session end or Symple error
$scope.localVideoPlaying = true;
}
}
//
// Helpers
$scope.isLoggedIn = function() {
return $scope.handle != null && $scope.client.online();
}
$scope.getMessageClass = function(m) {
if (m.direct)
return 'list-group-item-warning';
return '';
}
}