Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 2 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
Commits on Mar 26, 2013
@jugglinmike jugglinmike Create Peers collection and encapsulate options
Re-factor Peer object to use hard-coded connection options be default,
but allow these options to be overridden at instantiation or invocation.
d126d69
@jugglinmike jugglinmike Introduce targeted calling
This is a "first pass" implementation and needs heavy refactoring.
ac9ac75
View
110 prototype/public/scripts/app.js
@@ -4,23 +4,23 @@ require([
'use strict';
var config = {
- socketServer: 'ws://' + window.location.host,
- pcConfig: {
- iceServers: [
- { url: 'stun:stun.l.google.com:19302' },
- { url: 'stun:23.21.150.121' }
- ]
- }
+ socketServer: 'ws://' + window.location.host
};
+ var user = new Peer.Model();
+ // activePeer
+ // A global reference to the current call.
+ // TODO: Re-factor in order to support multiple simultaneous connections (and
+ // remove this variable)
+ var activePeer;
// TODO: Fetch contacts from remote identitiy provider
- var contacts = [
+ var contacts = new Peer.Collection([
{ name: 'creationix' },
{ name: 'robin' },
{ name: 'erik' },
{ name: 'lawrence' },
{ name: 'cassie' },
{ name: 'jugglinmike' }
- ];
+ ]);
var mediaConstraints = {
mandatory: {
OfferToReceiveAudio: true,
@@ -36,31 +36,39 @@ require([
console.error('Create Answer failed');
}
- var user = new Peer();
- var pc = new Peer();
var layout = new Layout({
el: '#app',
user: user,
- contacts: new Backbone.Collection(contacts)
+ contacts: contacts
});
layout.render();
- layout.on('connectRequest', function(stream) {
- // TODO: Derive remote peer ID from application state
- var remotePeerID = 'creationix';
- if (!pc.isActive() && transport.state === 'OPEN') {
+ layout.on('send-connect-request', function(peer) {
+ if (transport.state === 'OPEN') {
+
+ // TODO: Remove this line and reduce dependence on global state.
+ activePeer = peer;
+
+ peer.on('ice', function(candidate) {
+ console.log('Sending ICE candidate:', candidate);
+ transport.request('update', {
+ candidate: candidate,
+ to: this.get('locationID')
+ });
+ });
+ peer.on('addstream', function(stream) {
+ console.log('Remote stream added');
+ layout.playRemoteStream(stream);
+ });
- pc.connect(config.pcConfig);
- pc.addStream(stream);
- pc.createOffer(
+ peer.createOffer(
function(sessionDescription) {
this.setLocalDescription(sessionDescription);
- transport.peerLocationFind(remotePeerID, {
+ transport.peerLocationFind(peer.get('name'), {
session: sessionDescription,
userName: user.get('name')
}).then(function(findReply) {
- console.log('Promise Resolved', findReply);
- pc.setRemoteDescription(findReply.sessionDescription);
- pc.set('locationID', findReply.from);
+ peer.setRemoteDescription(findReply.sessionDescription);
+ peer.set('locationID', findReply.from);
}, function() {
// TODO: Update the UI to reflect this failure.
console.error('Find request failed.');
@@ -72,24 +80,9 @@ require([
});
layout.on('hangup', function() {
transport.request('bye', {
- to: pc.get('locationID')
- });
- pc.destroy();
- });
- pc.on('addstream', function(stream) {
- console.log('Remote stream added');
- layout.playRemoteStream(stream);
- });
- pc.on('removestream', function() {
- console.log('Remove remote stream');
- layout.stopRemoteStream();
- });
- pc.on('ice', function(candidate) {
- console.log('Sending ICE candidate:', candidate);
- transport.request('update', {
- candidate: candidate,
- to: pc.get('locationID')
+ to: activePeer.get('locationID')
});
+ activePeer.destroy();
});
var transport = new Transport({
invite: function(request) {
@@ -110,19 +103,38 @@ require([
console.log('Receiving call from ' + blob.userName +
'. Would you like to answer?');
- if (!pc.isActive()) {
- pc.connect(config.pcConfig);
+ if (!activePeer) {
+ activePeer = new Peer.Model();
+ activePeer.on('addstream', function(stream) {
+ console.log('Remote stream added');
+ layout.playRemoteStream(stream);
+ });
+ activePeer.on('removestream', function() {
+ console.log('Remove remote stream');
+ layout.stopRemoteStream();
+ });
+ activePeer.on('ice', function(candidate) {
+ console.log('Sending ICE candidate:', candidate);
+ transport.request('update', {
+ candidate: candidate,
+ to: this.get('locationID')
+ });
+ });
+ }
+ if (!activePeer.isActive()) {
+ activePeer.connect();
// TODO: Refactor so transport is not so tightly-coupled to the layout.
// This should also allow recieving calls without sharing the local
// stream.
- pc.addStream(layout.localStreamView.getStream());
+ activePeer.addStream(layout.localStreamView.getStream());
}
- pc.set('locationID', request.username.from);
+ activePeer.set('locationID', request.username.from);
+ activePeer.set('name', blob.userName);
console.log('Creating remote session description:', remoteSession);
- pc.setRemoteDescription(remoteSession);
+ activePeer.setRemoteDescription(remoteSession);
console.log('Sending answer...');
var dfd = Q.defer();
- pc.createAnswer(function(sessionDescription) {
+ activePeer.createAnswer(function(sessionDescription) {
this.setLocalDescription(sessionDescription);
dfd.resolve({
peer: true,
@@ -133,14 +145,14 @@ require([
return dfd.promise;
},
bye: function() {
- pc.destroy();
+ activePeer.destroy();
},
update: function(msg) {
- if (!pc.isActive()) {
+ if (!activePeer.isActive()) {
return;
}
console.log('Received ICE candidate:', msg.candidate);
- pc.addIceCandidate(msg.candidate);
+ activePeer.addIceCandidate(msg.candidate);
}
});
View
18 prototype/public/scripts/modules/contacts-view.js
@@ -1,11 +1,19 @@
-define(['text!templates/contacts-list.html','backbone', '_', 'layoutmanager'],
- function(html, Backbone, _) {
+define(['text!templates/contacts-list.html',
+ 'text!templates/contact-list-item.html',
+ 'backbone', '_', 'layoutmanager'],
+ function(contactListHtml, contactListItemHtml, Backbone, _) {
'use strict';
var ContactView = Backbone.Layout.extend({
tagName: 'li',
- className: 'contact',
- template: _.template('<%= name %>'),
+ className: 'contact cf',
+ template: _.template(contactListItemHtml),
+ events: {
+ 'click .option-call': 'call'
+ },
+ call: function() {
+ this.model.trigger('send-connect-request', this.model);
+ },
serialize: function() {
return this.model.toJSON();
}
@@ -13,7 +21,7 @@ define(['text!templates/contacts-list.html','backbone', '_', 'layoutmanager'],
var ContactsView = Backbone.Layout.extend({
className: 'contacts',
- template: _.template(html),
+ template: _.template(contactListHtml),
beforeRender: function() {
this.collection.forEach(function(contact) {
this.insertView('.contacts-list', new ContactView({ model: contact }));
View
20 prototype/public/scripts/modules/layout.js
@@ -7,14 +7,15 @@ define([
var Layout = Backbone.Layout.extend({
template: _.template(html),
events: {
- 'click .btn-connect': 'connect',
'click .btn-hang-up': 'hangUp'
},
initialize: function(options) {
this.user = options.user;
+ this.contacts = options.contacts;
this.localStreamView = new StreamViews.LocalStreamView();
this.remoteStreamView = new StreamViews.StreamView();
- this.contactsView = new ContactsView({ collection: options.contacts });
+ this.contactsView = new ContactsView({ collection: this.contacts });
+ this.listenTo(this.contacts, 'send-connect-request', this.sendConnectReq);
this.loginView = new LoginView({ model: this.user });
this.setView('.source', this.localStreamView);
this.setView('.remote', this.remoteStreamView);
@@ -38,12 +39,19 @@ define([
this.remoteStreamView.stop();
this.render();
},
- connect: function() {
+ sendConnectReq: function(peer) {
+
+ if (peer.isActive()) {
+ // TODO: Update UI accordingly
+ console.error('Already connected to ' + peer.get('name') + '!');
+ return;
+ }
+ peer.connect();
if (this.localStreamView.isPlaying()) {
- this.trigger('connectRequest', this.localStreamView.getStream());
- } else {
- alert('Local stream not running yet - try again.');
+ peer.addStream(this.localStreamView.getStream());
}
+
+ this.trigger('send-connect-request', peer);
},
hangUp: function() {
console.log('Hang up.');
View
21 prototype/public/scripts/modules/peer.js
@@ -5,6 +5,17 @@ define([
var Peer = Backbone.Model.extend({
nameRegex: /^[0-9a-z\.-]+$/i,
+ connectOptions: {
+ iceServers: [
+ { url: 'stun:stun.l.google.com:19302' },
+ { url: 'stun:23.21.150.121' }
+ ]
+ },
+ initialize: function(options) {
+ if (options && options.connectOptions) {
+ this.connectOptions = options.connectOptions;
+ }
+ },
validate: function(attrs) {
if (!attrs || !attrs.name) {
return new Error('No username specified');
@@ -19,6 +30,7 @@ define([
if (this.peerConn) {
this.destroy();
}
+ options = options || this.connectOptions;
try {
peerConn = this.peerConn = new rtc.RTCPeerConnection(options);
} catch (e) {
@@ -137,5 +149,12 @@ define([
delete this.peerConn;
};
- return Peer;
+ var Peers = Backbone.Collection.extend({
+ model: Peer
+ });
+
+ return {
+ Model: Peer,
+ Collection: Peers
+ };
});
View
20 prototype/public/styles/main.css
@@ -42,10 +42,26 @@ body {
.contacts .contact {
display: block;
border-bottom: solid 1px #888;
- line-height: 1.9em;
+ padding: 0.5em 0.3em;
+}
+
+.contact .name {
+ float: left;
+} .contact .options {
+ float: right;
+ visibility: hidden;
+} .contact:hover .options {
+ visibility: visible;
+} .contact .option {
cursor: pointer;
- padding: 0 0.3em;
+ background-color: transparent;
+ border-width: 0 1px 1px 0;
+ border-color: #555;
+ border-style: solid;
+} .contact .option:active {
+ border-width: 0;
}
+
.login {
color: #eee;
position: absolute;
View
4 prototype/public/templates/contact-list-item.html
@@ -0,0 +1,4 @@
+<span class="name"><%= name %></span>
+<ul class="options">
+ <li><button class="option option-call">Call</button></li>
+</ul>
View
3 prototype/public/templates/layout.html
@@ -2,7 +2,4 @@
<div class="stream-cont">
<div class="source"></div>
<div class="remote"></div>
- <button type="button" class="btn <%= isPlaying ? 'btn-hang-up' : 'btn-connect' %>">
- <%= isPlaying ? 'Hang Up' : 'Connect' %>
- </button>
</div>
View
33 prototype/test/client/tests/peer.js
@@ -2,17 +2,30 @@ define(['modules/peer'], function(Peer) {
'use strict';
suite('Peer', function() {
- suite('#validate', function() {
- var peer;
- suiteSetup(function() {
- peer = new Peer();
- });
- test('Rejects unspecified string names', function() {
- assert.instanceOf(peer.validate(), Error);
- assert.instanceOf(peer.validate({ name: '' }), Error);
+
+ suite('Model', function() {
+ suite('#initialize', function() {
+ test('Defines default connection options', function() {
+ assert.ok((new Peer.Model()).connectOptions);
+ });
+ test('Accepts connection options as overrides', function() {
+ var newOpts = {};
+ var peer = new Peer.Model({ connectOptions: newOpts });
+ assert.equal(peer.connectOptions, newOpts);
+ });
});
- test('Rejects invalid names', function() {
- assert.instanceOf(peer.validate({ name: '!@#@#$' }), Error);
+ suite('#validate', function() {
+ var peer;
+ suiteSetup(function() {
+ peer = new Peer.Model();
+ });
+ test('Rejects unspecified string names', function() {
+ assert.instanceOf(peer.validate(), Error);
+ assert.instanceOf(peer.validate({ name: '' }), Error);
+ });
+ test('Rejects invalid names', function() {
+ assert.instanceOf(peer.validate({ name: '!@#@#$' }), Error);
+ });
});
});
});

No commit comments for this range

Something went wrong with that request. Please try again.