Download:
child 61:fbdf2a0985d1
parent 59:f3c2d85d0a35
60:bbab4c40a2ba
Anton Shestakov <av6@dwimlabs.net>, Mon, 28 Mar 2016 13:56:42 +0800
webrtc: add Tram.WebRTCInterface, it will be glued to the rest of code later

7 файлов изменено, 322 вставок(+), 2 удалений(-) [+]
coffee/tram.coffee file | annotate | diff | comparison | revisions
coffee/webrtc.coffee file | annotate | diff | comparison | revisions
index.html file | annotate | diff | comparison | revisions
js/tram.js file | annotate | diff | comparison | revisions
js/tram.js.map file | annotate | diff | comparison | revisions
js/webrtc.js file | annotate | diff | comparison | revisions
js/webrtc.js.map file | annotate | diff | comparison | revisions
--- a/coffee/tram.coffee Mon Mar 28 13:54:49 2016 +0800
+++ b/coffee/tram.coffee Mon Mar 28 13:56:42 2016 +0800
@@ -3,9 +3,17 @@
client: 'Tram IM'
version: '0.1'
+ NS:
+ WEBRTC: 'tram-im:webrtc'
+
config:
host: location.host
domain: location.hostname
+ iceServers: [{
+ urls: 'stun:stun.services.mozilla.com'
+ }, {
+ urls: 'stun:stun.l.google.com:19302'
+ }]
colors:
show:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/coffee/webrtc.coffee Mon Mar 28 13:56:42 2016 +0800
@@ -0,0 +1,112 @@
+class Tram.WebRTCInterface
+ lstream: null
+ rstream: null
+
+ constructor: (@contact) ->
+ @peerJid = @contact.get('jid')
+
+ init: (@initiator, constraints) ->
+ if constraints.audio || constraints.video
+ navigator.getUserMedia(constraints, @connect, @fail)
+ else
+ @connect()
+
+ connect: (stream) =>
+ @call = calls.add(jid: @peerJid)
+ @pc = new RTCPeerConnection(iceServers: Tram.config.iceServers)
+
+ if stream?
+ @lstream = stream
+ @pc.addStream(stream)
+ @call.set('local/stream', stream)
+
+ @pc.onicecandidate = (event) =>
+ if event.candidate
+ @sendPayload(event.candidate, 'ice')
+
+ @pc.onaddstream = (event) =>
+ @rstream = event.stream
+ @call.set('remote/stream', event.stream)
+ @contact.set('callstate', 'established')
+
+ if @initiator
+ @sendIntent('initiate')
+ else
+ @createOffer()
+
+ onPayload: (stanza) ->
+ if not @call?
+ return
+
+ el = stanza.getElementsByTagName('payload')[0]
+ signal = JSON.parse(el.firstChild.nodeValue)
+ if signal.sdp
+ if signal.type is 'offer'
+ @receiveOffer(signal)
+ else if signal.type is 'answer'
+ @receiveAnswer(signal)
+ 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())
+
+ createOffer: ->
+ @pc.createOffer((offer) =>
+ @pc.setLocalDescription(offer, =>
+ @sendPayload(@pc.localDescription, 'offer')
+ , @fail)
+ , @fail)
+
+ receiveOffer: (offer) ->
+ @pc.setRemoteDescription(new RTCSessionDescription(offer), =>
+ @pc.createAnswer((answer) =>
+ @pc.setLocalDescription(answer, =>
+ @sendPayload(@pc.localDescription, 'answer')
+ , @fail)
+ , @fail)
+ , @fail)
+
+ receiveAnswer: (answer) ->
+ @pc.setRemoteDescription(new RTCSessionDescription(answer))
+
+ fail: =>
+ @disconnect()
+ @contact.unset('callstate')
+
+ disconnect: ->
+ if @call?
+ @call.unset('remote/stream')
+ @call.unset('local/stream')
+ calls.remove(@call)
+
+ if @rstream?
+ @stopStream(@rstream)
+ #@pc?.removeStream?(@rstream)
+
+ if @lstream?
+ @stopStream(@lstream)
+ #@pc?.removeStream?(@lstream)
+
+ if @pc?
+ @pc.close()
+ @pc = null
+
+ stopStream: (stream) ->
+ if stream.getAudioTracks?
+ for track in stream.getAudioTracks()
+ track.stop?()
+
+ if stream.getVideoTracks?
+ for track in stream.getVideoTracks()
+ track.stop?()
+
+ stream.stop?()
--- a/index.html Mon Mar 28 13:54:49 2016 +0800
+++ b/index.html Mon Mar 28 13:56:42 2016 +0800
@@ -163,6 +163,7 @@
<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
@@ -5,9 +5,19 @@
client: 'Tram IM',
version: '0.1'
},
+ NS: {
+ WEBRTC: 'tram-im:webrtc'
+ },
config: {
host: location.host,
- domain: location.hostname
+ domain: location.hostname,
+ iceServers: [
+ {
+ urls: 'stun:stun.services.mozilla.com'
+ }, {
+ urls: 'stun:stun.l.google.com:19302'
+ }
+ ]
},
colors: {
show: {
--- 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
@@ -6,5 +6,5 @@
"coffee/tram.coffee"
],
"names": [],
- "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
@@ -0,0 +1,179 @@
+// Generated by CoffeeScript 1.10.0
+(function() {
+ 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);
+ } else {
+ return this.connect();
+ }
+ };
+
+ WebRTCInterface.prototype.connect = function(stream) {
+ this.call = calls.add({
+ jid: this.peerJid
+ });
+ this.pc = new RTCPeerConnection({
+ iceServers: Tram.config.iceServers
+ });
+ if (stream != null) {
+ this.lstream = stream;
+ this.pc.addStream(stream);
+ this.call.set('local/stream', stream);
+ }
+ this.pc.onicecandidate = (function(_this) {
+ return function(event) {
+ if (event.candidate) {
+ return _this.sendPayload(event.candidate, 'ice');
+ }
+ };
+ })(this);
+ 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');
+ };
+ })(this);
+ if (this.initiator) {
+ return this.sendIntent('initiate');
+ } else {
+ return this.createOffer();
+ }
+ };
+
+ WebRTCInterface.prototype.onPayload = function(stanza) {
+ var el, signal;
+ if (this.call == null) {
+ return;
+ }
+ el = stanza.getElementsByTagName('payload')[0];
+ signal = JSON.parse(el.firstChild.nodeValue);
+ if (signal.sdp) {
+ 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) {
+ var msg, payload;
+ payload = JSON.stringify(data);
+ msg = $msg({
+ to: this.peerJid,
+ type: 'chat'
+ }).c('payload', {
+ xmlns: Tram.NS.WEBRTC,
+ type: type
+ }).t(payload);
+ return X.conn.send(msg.tree());
+ };
+
+ WebRTCInterface.prototype.sendIntent = function(intent) {
+ var msg;
+ msg = $msg({
+ to: this.peerJid,
+ type: 'chat'
+ }).c('intent', {
+ xmlns: Tram.NS.WEBRTC
+ }).t(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');
+ }, _this.fail);
+ };
+ })(this), this.fail);
+ };
+
+ WebRTCInterface.prototype.receiveOffer = function(offer) {
+ return this.pc.setRemoteDescription(new RTCSessionDescription(offer), (function(_this) {
+ return function() {
+ return _this.pc.createAnswer(function(answer) {
+ return _this.pc.setLocalDescription(answer, function() {
+ return _this.sendPayload(_this.pc.localDescription, 'answer');
+ }, _this.fail);
+ }, _this.fail);
+ };
+ })(this), this.fail);
+ };
+
+ WebRTCInterface.prototype.receiveAnswer = function(answer) {
+ return this.pc.setRemoteDescription(new RTCSessionDescription(answer));
+ };
+
+ WebRTCInterface.prototype.fail = function() {
+ this.disconnect();
+ 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);
+ }
+ if (this.pc != null) {
+ this.pc.close();
+ return this.pc = null;
+ }
+ };
+
+ 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++) {
+ track = ref[i];
+ if (typeof track.stop === "function") {
+ track.stop();
+ }
+ }
+ }
+ if (stream.getVideoTracks != null) {
+ ref1 = stream.getVideoTracks();
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ track = ref1[j];
+ if (typeof track.stop === "function") {
+ track.stop();
+ }
+ }
+ }
+ return typeof stream.stop === "function" ? stream.stop() : void 0;
+ };
+
+ return WebRTCInterface;
+
+ })();
+
+}).call(this);
+
+//# 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
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "webrtc.js",
+ "sourceRoot": "..",
+ "sources": [
+ "coffee/webrtc.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file