--- a/coffee/tram.coffee Mon Mar 28 13:54:49 2016 +0800
+++ b/coffee/tram.coffee Mon Mar 28 13:56:42 2016 +0800
+ WEBRTC: 'tram-im:webrtc'
domain: location.hostname
+ urls: 'stun:stun.services.mozilla.com'
+ urls: 'stun:stun.l.google.com:19302'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/coffee/webrtc.coffee Mon Mar 28 13:56:42 2016 +0800
+class Tram.WebRTCInterface
+ constructor: (@contact) ->
+ @peerJid = @contact.get('jid')
+ init: (@initiator, constraints) ->
+ if constraints.audio || constraints.video
+ navigator.getUserMedia(constraints, @connect, @fail)
+ @call = calls.add(jid: @peerJid)
+ @pc = new RTCPeerConnection(iceServers: Tram.config.iceServers)
+ @call.set('local/stream', stream)
+ @pc.onicecandidate = (event) =>
+ @sendPayload(event.candidate, 'ice')
+ @pc.onaddstream = (event) =>
+ @rstream = event.stream
+ @call.set('remote/stream', event.stream)
+ @contact.set('callstate', 'established')
+ @sendIntent('initiate')
+ el = stanza.getElementsByTagName('payload')[0]
+ signal = JSON.parse(el.firstChild.nodeValue)
+ if signal.type is 'offer'
+ else if signal.type is 'answer'
+ else if signal.candidate
+ @pc.addIceCandidate(new RTCIceCandidate(signal))
+ sendPayload: (data, type) ->
+ payload = JSON.stringify(data)
+ msg = $msg(to: @peerJid, type: 'chat')
+ .c('payload', xmlns: Tram.NS.WEBRTC, type: type).t(payload)
+ X.conn.send(msg.tree())
+ sendIntent: (intent) ->
+ msg = $msg(to: @peerJid, type: 'chat')
+ .c('intent', xmlns: Tram.NS.WEBRTC).t(intent)
+ X.conn.send(msg.tree())
+ @pc.createOffer((offer) =>
+ @pc.setLocalDescription(offer, =>
+ @sendPayload(@pc.localDescription, 'offer')
+ receiveOffer: (offer) ->
+ @pc.setRemoteDescription(new RTCSessionDescription(offer), =>
+ @pc.createAnswer((answer) =>
+ @pc.setLocalDescription(answer, =>
+ @sendPayload(@pc.localDescription, 'answer')
+ receiveAnswer: (answer) ->
+ @pc.setRemoteDescription(new RTCSessionDescription(answer))
+ @contact.unset('callstate')
+ @call.unset('remote/stream')
+ @call.unset('local/stream')
+ #@pc?.removeStream?(@rstream)
+ #@pc?.removeStream?(@lstream)
+ stopStream: (stream) ->
+ if stream.getAudioTracks?
+ for track in stream.getAudioTracks()
+ if stream.getVideoTracks?
+ for track in stream.getVideoTracks()
--- a/index.html Mon Mar 28 13:54:49 2016 +0800
+++ b/index.html Mon Mar 28 13:56:42 2016 +0800
<script src='/js/tram.js'></script>
<script src='/js/rivets.js'></script>
<script src='/js/xmpp.js'></script>
+ <script src='/js/webrtc.js'></script>
<script src='/js/contacts.js'></script>
<script src='/js/messages.js'></script>
<script src='/js/calls.js'></script>
--- a/js/tram.js Mon Mar 28 13:54:49 2016 +0800
+++ b/js/tram.js Mon Mar 28 13:56:42 2016 +0800
+ WEBRTC: 'tram-im:webrtc'
- domain: location.hostname
+ domain: location.hostname,
+ urls: 'stun:stun.services.mozilla.com'
+ urls: 'stun:stun.l.google.com:19302'
--- a/js/tram.js.map Mon Mar 28 13:54:49 2016 +0800
+++ b/js/tram.js.map Mon Mar 28 13:56:42 2016 +0800
- "mappings": ";AAAA;EAAA,MAAM,CAAC,IAAP,GACE;IAAA,IAAA,EACE;MAAA,MAAA,EAAQ,SAAR;MACA,OAAA,EAAS,KADT;KADF;IAIA,MAAA,EACE;MAAA,IAAA,EAAM,QAAQ,CAAC,IAAf;MACA,MAAA,EAAQ,QAAQ,CAAC,QADjB;KALF;IAQA,MAAA,EACE;MAAA,IAAA,EACE;QAAA,MAAA,EAAQ,SAAR;QACA,IAAA,EAAM,SADN;QAEA,EAAA,EAAI,SAFJ;QAGA,OAAA,EAAS,MAHT;QAIA,SAAA,EAAS,SAJT;OADF;MAMA,MAAA,EAAQ,CACN,SADM,EAEN,SAFM,EAGN,SAHM,EAIN,SAJM,EAKN,SALM,EAMN,SANM,EAON,SAPM,EAQN,SARM,EASN,SATM,EAUN,SAVM,CANR;KATF;;AADF"
+ "mappings": ";AAAA;EAAA,MAAM,CAAC,IAAP,GACE;IAAA,IAAA,EACE;MAAA,MAAA,EAAQ,SAAR;MACA,OAAA,EAAS,KADT;KADF;IAIA,EAAA,EACE;MAAA,MAAA,EAAQ,gBAAR;KALF;IAOA,MAAA,EACE;MAAA,IAAA,EAAM,QAAQ,CAAC,IAAf;MACA,MAAA,EAAQ,QAAQ,CAAC,QADjB;MAEA,UAAA,EAAY;QAAC;UACX,IAAA,EAAM,gCADK;SAAD,EAET;UACD,IAAA,EAAM,8BADL;SAFS;OAFZ;KARF;IAgBA,MAAA,EACE;MAAA,IAAA,EACE;QAAA,MAAA,EAAQ,SAAR;QACA,IAAA,EAAM,SADN;QAEA,EAAA,EAAI,SAFJ;QAGA,OAAA,EAAS,MAHT;QAIA,SAAA,EAAS,SAJT;OADF;MAMA,MAAA,EAAQ,CACN,SADM,EAEN,SAFM,EAGN,SAHM,EAIN,SAJM,EAKN,SALM,EAMN,SANM,EAON,SAPM,EAQN,SARM,EASN,SATM,EAUN,SAVM,CANR;KAjBF;;AADF"
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/js/webrtc.js Mon Mar 28 13:56:42 2016 +0800
+// Generated by CoffeeScript 1.10.0
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+ Tram.WebRTCInterface = (function() {
+ WebRTCInterface.prototype.lstream = null;
+ WebRTCInterface.prototype.rstream = null;
+ function WebRTCInterface(contact) {
+ this.contact = contact;
+ this.fail = bind(this.fail, this);
+ this.connect = bind(this.connect, this);
+ this.peerJid = this.contact.get('jid');
+ WebRTCInterface.prototype.init = function(initiator, constraints) {
+ this.initiator = initiator;
+ if (constraints.audio || constraints.video) {
+ return navigator.getUserMedia(constraints, this.connect, this.fail);
+ WebRTCInterface.prototype.connect = function(stream) {
+ this.call = calls.add({
+ this.pc = new RTCPeerConnection({
+ iceServers: Tram.config.iceServers
+ this.pc.addStream(stream);
+ this.call.set('local/stream', stream);
+ this.pc.onicecandidate = (function(_this) {
+ return function(event) {
+ return _this.sendPayload(event.candidate, 'ice');
+ this.pc.onaddstream = (function(_this) {
+ return function(event) {
+ _this.rstream = event.stream;
+ _this.call.set('remote/stream', event.stream);
+ return _this.contact.set('callstate', 'established');
+ return this.sendIntent('initiate');
+ return this.createOffer();
+ WebRTCInterface.prototype.onPayload = function(stanza) {
+ if (this.call == null) {
+ el = stanza.getElementsByTagName('payload')[0];
+ signal = JSON.parse(el.firstChild.nodeValue);
+ if (signal.type === 'offer') {
+ return this.receiveOffer(signal);
+ } else if (signal.type === 'answer') {
+ return this.receiveAnswer(signal);
+ } else if (signal.candidate) {
+ return this.pc.addIceCandidate(new RTCIceCandidate(signal));
+ WebRTCInterface.prototype.sendPayload = function(data, type) {
+ payload = JSON.stringify(data);
+ return X.conn.send(msg.tree());
+ WebRTCInterface.prototype.sendIntent = function(intent) {
+ return X.conn.send(msg.tree());
+ WebRTCInterface.prototype.createOffer = function() {
+ return this.pc.createOffer((function(_this) {
+ return function(offer) {
+ return _this.pc.setLocalDescription(offer, function() {
+ return _this.sendPayload(_this.pc.localDescription, 'offer');
+ WebRTCInterface.prototype.receiveOffer = function(offer) {
+ return this.pc.setRemoteDescription(new RTCSessionDescription(offer), (function(_this) {
+ return _this.pc.createAnswer(function(answer) {
+ return _this.pc.setLocalDescription(answer, function() {
+ return _this.sendPayload(_this.pc.localDescription, 'answer');
+ WebRTCInterface.prototype.receiveAnswer = function(answer) {
+ return this.pc.setRemoteDescription(new RTCSessionDescription(answer));
+ WebRTCInterface.prototype.fail = function() {
+ return this.contact.unset('callstate');
+ WebRTCInterface.prototype.disconnect = function() {
+ if (this.call != null) {
+ this.call.unset('remote/stream');
+ this.call.unset('local/stream');
+ calls.remove(this.call);
+ if (this.rstream != null) {
+ this.stopStream(this.rstream);
+ if (this.lstream != null) {
+ this.stopStream(this.lstream);
+ WebRTCInterface.prototype.stopStream = function(stream) {
+ var i, j, len, len1, ref, ref1, track;
+ if (stream.getAudioTracks != null) {
+ ref = stream.getAudioTracks();
+ for (i = 0, len = ref.length; i < len; i++) {
+ if (typeof track.stop === "function") {
+ if (stream.getVideoTracks != null) {
+ ref1 = stream.getVideoTracks();
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ if (typeof track.stop === "function") {
+ return typeof stream.stop === "function" ? stream.stop() : void 0;
+ return WebRTCInterface;
+//# sourceMappingURL=webrtc.js.map
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/js/webrtc.js.map Mon Mar 28 13:56:42 2016 +0800
+ "mappings": ";AAAA;AAAA,MAAA;;EAAM,IAAI,CAAC;8BACT,OAAA,GAAS;;8BACT,OAAA,GAAS;;IAEI,yBAAC,OAAD;MAAC,IAAC,CAAA,UAAD;;;MACZ,IAAC,CAAA,OAAD,GAAW,IAAC,CAAA,OAAO,CAAC,GAAT,CAAa,KAAb;IADA;;8BAGb,IAAA,GAAM,SAAC,SAAD,EAAa,WAAb;MAAC,IAAC,CAAA,YAAD;MACL,IAAG,WAAW,CAAC,KAAZ,IAAqB,WAAW,CAAC,KAApC;eACE,SAAS,CAAC,YAAV,CAAuB,WAAvB,EAAoC,IAAC,CAAA,OAArC,EAA8C,IAAC,CAAA,IAA/C,EADF;OAAA,MAAA;eAGE,IAAC,CAAA,OAAD,CAAA,EAHF;;IADI;;8BAMN,OAAA,GAAS,SAAC,MAAD;MACP,IAAC,CAAA,IAAD,GAAQ,KAAK,CAAC,GAAN,CAAU;QAAA,GAAA,EAAK,IAAC,CAAA,OAAN;OAAV;MACR,IAAC,CAAA,EAAD,GAAU,IAAA,iBAAA,CAAkB;QAAA,UAAA,EAAY,IAAI,CAAC,MAAM,CAAC,UAAxB;OAAlB;MAEV,IAAG,cAAH;QACE,IAAC,CAAA,OAAD,GAAW;QACX,IAAC,CAAA,EAAE,CAAC,SAAJ,CAAc,MAAd;QACA,IAAC,CAAA,IAAI,CAAC,GAAN,CAAU,cAAV,EAA0B,MAA1B,EAHF;;MAKA,IAAC,CAAA,EAAE,CAAC,cAAJ,GAAqB,CAAA,SAAA,KAAA;eAAA,SAAC,KAAD;UACnB,IAAG,KAAK,CAAC,SAAT;mBACE,KAAC,CAAA,WAAD,CAAa,KAAK,CAAC,SAAnB,EAA8B,KAA9B,EADF;;QADmB;MAAA,CAAA,CAAA,CAAA,IAAA;MAIrB,IAAC,CAAA,EAAE,CAAC,WAAJ,GAAkB,CAAA,SAAA,KAAA;eAAA,SAAC,KAAD;UAChB,KAAC,CAAA,OAAD,GAAW,KAAK,CAAC;UACjB,KAAC,CAAA,IAAI,CAAC,GAAN,CAAU,eAAV,EAA2B,KAAK,CAAC,MAAjC;iBACA,KAAC,CAAA,OAAO,CAAC,GAAT,CAAa,WAAb,EAA0B,aAA1B;QAHgB;MAAA,CAAA,CAAA,CAAA,IAAA;MAKlB,IAAG,IAAC,CAAA,SAAJ;eACE,IAAC,CAAA,UAAD,CAAY,UAAZ,EADF;OAAA,MAAA;eAGE,IAAC,CAAA,WAAD,CAAA,EAHF;;IAlBO;;8BAuBT,SAAA,GAAW,SAAC,MAAD;AACT,UAAA;MAAA,IAAO,iBAAP;AACE,eADF;;MAGA,EAAA,GAAK,MAAM,CAAC,oBAAP,CAA4B,SAA5B,CAAuC,CAAA,CAAA;MAC5C,MAAA,GAAS,IAAI,CAAC,KAAL,CAAW,EAAE,CAAC,UAAU,CAAC,SAAzB;MACT,IAAG,MAAM,CAAC,GAAV;QACE,IAAG,MAAM,CAAC,IAAP,KAAe,OAAlB;iBACE,IAAC,CAAA,YAAD,CAAc,MAAd,EADF;SAAA,MAEK,IAAG,MAAM,CAAC,IAAP,KAAe,QAAlB;iBACH,IAAC,CAAA,aAAD,CAAe,MAAf,EADG;SAHP;OAAA,MAKK,IAAG,MAAM,CAAC,SAAV;eACH,IAAC,CAAA,EAAE,CAAC,eAAJ,CAAwB,IAAA,eAAA,CAAgB,MAAhB,CAAxB,EADG;;IAXI;;8BAcX,WAAA,GAAa,SAAC,IAAD,EAAO,IAAP;AACX,UAAA;MAAA,OAAA,GAAU,IAAI,CAAC,SAAL,CAAe,IAAf;MACV,GAAA,GAAM,IAAA,CAAK;QAAA,EAAA,EAAI,IAAC,CAAA,OAAL;QAAc,IAAA,EAAM,MAApB;OAAL,CACJ,CAAC,CADG,CACD,SADC,EACU;QAAA,KAAA,EAAO,IAAI,CAAC,EAAE,CAAC,MAAf;QAAuB,IAAA,EAAM,IAA7B;OADV,CAC4C,CAAC,CAD7C,CAC+C,OAD/C;aAEN,CAAC,CAAC,IAAI,CAAC,IAAP,CAAY,GAAG,CAAC,IAAJ,CAAA,CAAZ;IAJW;;8BAMb,UAAA,GAAY,SAAC,MAAD;AACV,UAAA;MAAA,GAAA,GAAM,IAAA,CAAK;QAAA,EAAA,EAAI,IAAC,CAAA,OAAL;QAAc,IAAA,EAAM,MAApB;OAAL,CACJ,CAAC,CADG,CACD,QADC,EACS;QAAA,KAAA,EAAO,IAAI,CAAC,EAAE,CAAC,MAAf;OADT,CAC+B,CAAC,CADhC,CACkC,MADlC;aAEN,CAAC,CAAC,IAAI,CAAC,IAAP,CAAY,GAAG,CAAC,IAAJ,CAAA,CAAZ;IAHU;;8BAKZ,WAAA,GAAa,SAAA;aACX,IAAC,CAAA,EAAE,CAAC,WAAJ,CAAgB,CAAA,SAAA,KAAA;eAAA,SAAC,KAAD;iBACd,KAAC,CAAA,EAAE,CAAC,mBAAJ,CAAwB,KAAxB,EAA+B,SAAA;mBAC7B,KAAC,CAAA,WAAD,CAAa,KAAC,CAAA,EAAE,CAAC,gBAAjB,EAAmC,OAAnC;UAD6B,CAA/B,EAEE,KAAC,CAAA,IAFH;QADc;MAAA,CAAA,CAAA,CAAA,IAAA,CAAhB,EAIE,IAAC,CAAA,IAJH;IADW;;8BAOb,YAAA,GAAc,SAAC,KAAD;aACZ,IAAC,CAAA,EAAE,CAAC,oBAAJ,CAA6B,IAAA,qBAAA,CAAsB,KAAtB,CAA7B,EAA2D,CAAA,SAAA,KAAA;eAAA,SAAA;iBACzD,KAAC,CAAA,EAAE,CAAC,YAAJ,CAAiB,SAAC,MAAD;mBACf,KAAC,CAAA,EAAE,CAAC,mBAAJ,CAAwB,MAAxB,EAAgC,SAAA;qBAC9B,KAAC,CAAA,WAAD,CAAa,KAAC,CAAA,EAAE,CAAC,gBAAjB,EAAmC,QAAnC;YAD8B,CAAhC,EAEE,KAAC,CAAA,IAFH;UADe,CAAjB,EAIE,KAAC,CAAA,IAJH;QADyD;MAAA,CAAA,CAAA,CAAA,IAAA,CAA3D,EAME,IAAC,CAAA,IANH;IADY;;8BASd,aAAA,GAAe,SAAC,MAAD;aACb,IAAC,CAAA,EAAE,CAAC,oBAAJ,CAA6B,IAAA,qBAAA,CAAsB,MAAtB,CAA7B;IADa;;8BAGf,IAAA,GAAM,SAAA;MACJ,IAAC,CAAA,UAAD,CAAA;aACA,IAAC,CAAA,OAAO,CAAC,KAAT,CAAe,WAAf;IAFI;;8BAIN,UAAA,GAAY,SAAA;MACV,IAAG,iBAAH;QACE,IAAC,CAAA,IAAI,CAAC,KAAN,CAAY,eAAZ;QACA,IAAC,CAAA,IAAI,CAAC,KAAN,CAAY,cAAZ;QACA,KAAK,CAAC,MAAN,CAAa,IAAC,CAAA,IAAd,EAHF;;MAKA,IAAG,oBAAH;QACE,IAAC,CAAA,UAAD,CAAY,IAAC,CAAA,OAAb,EADF;;MAIA,IAAG,oBAAH;QACE,IAAC,CAAA,UAAD,CAAY,IAAC,CAAA,OAAb,EADF;;MAIA,IAAG,eAAH;QACE,IAAC,CAAA,EAAE,CAAC,KAAJ,CAAA;eACA,IAAC,CAAA,EAAD,GAAM,KAFR;;IAdU;;8BAkBZ,UAAA,GAAY,SAAC,MAAD;AACV,UAAA;MAAA,IAAG,6BAAH;AACE;AAAA,aAAA,qCAAA;;;YACE,KAAK,CAAC;;AADR,SADF;;MAIA,IAAG,6BAAH;AACE;AAAA,aAAA,wCAAA;;;YACE,KAAK,CAAC;;AADR,SADF;;iDAIA,MAAM,CAAC;IATG;;;;;AAtGd"
\ No newline at end of file