浏览代码

Merge pull request #40 from jitsi/strophe_cleanup

Strophe cleanup
dev1
hristoterezov 9 年前
父节点
当前提交
f3465b9d01
共有 6 个文件被更改,包括 267 次插入701 次删除
  1. 22
    3
      JitsiConference.js
  2. 0
    1
      modules/xmpp/ChatRoom.js
  3. 36
    33
      modules/xmpp/JingleSession.js
  4. 155
    585
      modules/xmpp/JingleSessionPC.js
  5. 42
    79
      modules/xmpp/strophe.jingle.js
  6. 12
    0
      service/xmpp/XMPPEvents.js

+ 22
- 3
JitsiConference.js 查看文件

@@ -850,9 +850,28 @@ JitsiConference.prototype.isCallstatsEnabled = function () {
850 850
  * @param conference the conference
851 851
  */
852 852
 function setupListeners(conference) {
853
-    conference.xmpp.addListener(XMPPEvents.CALL_INCOMING, function (event) {
854
-        conference.rtc.onIncommingCall(event);
855
-        conference.statistics.startRemoteStats(event.peerconnection);
853
+    conference.xmpp.addListener(
854
+        XMPPEvents.CALL_INCOMING, function (jingleSession, jingleOffer) {
855
+
856
+        if (conference.room.isFocus(jingleSession.peerjid)) {
857
+            // Accept incoming call
858
+            conference.room.setJingleSession(jingleSession);
859
+            jingleSession.initialize(false /* initiator */, conference.room);
860
+            conference.rtc.onIncommingCall(jingleSession);
861
+            jingleSession.acceptOffer(jingleOffer, null,
862
+                function (error) {
863
+                    console.error(
864
+                        "Failed to accept incoming Jingle session", error);
865
+                }
866
+            );
867
+            conference.statistics.startRemoteStats(
868
+                    jingleSession.peerconnection);
869
+        } else {
870
+            // Error cause this should never happen unless something is wrong !
871
+            logger.error(
872
+                "Rejecting session-initiate from non focus user: "
873
+                        + jingleSession.peerjid);
874
+        }
856 875
     });
857 876
 
858 877
     conference.room.addListener(XMPPEvents.REMOTE_STREAM_RECEIVED,

+ 0
- 1
modules/xmpp/ChatRoom.js 查看文件

@@ -578,7 +578,6 @@ ChatRoom.prototype.getMemberRole = function (peerJid) {
578 578
 
579 579
 ChatRoom.prototype.setJingleSession = function(session){
580 580
     this.session = session;
581
-    this.session.room = this;
582 581
 };
583 582
 
584 583
 

+ 36
- 33
modules/xmpp/JingleSession.js 查看文件

@@ -5,7 +5,8 @@
5 5
  */
6 6
 var logger = require("jitsi-meet-logger").getLogger(__filename);
7 7
 
8
-function JingleSession(me, sid, connection, service, eventEmitter) {
8
+function JingleSession(me, sid, peerjid, connection,
9
+                       media_constraints, ice_config, service, eventEmitter) {
9 10
     /**
10 11
      * Our JID.
11 12
      */
@@ -16,6 +17,11 @@ function JingleSession(me, sid, connection, service, eventEmitter) {
16 17
      */
17 18
     this.sid = sid;
18 19
 
20
+    /**
21
+     * the JID of the remote peer.
22
+     */
23
+    this.peerjid = peerjid;
24
+
19 25
     /**
20 26
      * The XMPP connection.
21 27
      */
@@ -44,37 +50,33 @@ function JingleSession(me, sid, connection, service, eventEmitter) {
44 50
     this.drip_container = [];
45 51
 
46 52
     // Media constraints. Is this WebRTC only?
47
-    this.media_constraints = null;
53
+    this.media_constraints = media_constraints;
48 54
 
49 55
     // ICE servers config (RTCConfiguration?).
50
-    this.ice_config = {};
56
+    this.ice_config = ice_config;
51 57
 
52 58
     // The chat room instance associated with the session.
53 59
     this.room = null;
60
+
61
+    // Jingle session state - uninitialized until 'initialize' is called
62
+    this.state = null;
54 63
 }
55 64
 
56 65
 /**
57 66
  * Prepares this object to initiate a session.
58
- * @param peerjid the JID of the remote peer.
59 67
  * @param isInitiator whether we will be the Jingle initiator.
60
- * @param media_constraints
61
- * @param ice_config
68
+ * @param room <tt>ChatRoom<tt> for the conference associated with this session
62 69
  */
63
-JingleSession.prototype.initialize = function(peerjid, isInitiator,
64
-                                              media_constraints, ice_config) {
65
-    this.media_constraints = media_constraints;
66
-    this.ice_config = ice_config;
67
-
70
+JingleSession.prototype.initialize = function(isInitiator, room) {
68 71
     if (this.state !== null) {
69 72
         logger.error('attempt to initiate on session ' + this.sid +
70 73
         'in state ' + this.state);
71 74
         return;
72 75
     }
76
+    this.room = room;
73 77
     this.state = 'pending';
74
-    this.initiator = isInitiator ? this.me : peerjid;
75
-    this.responder = !isInitiator ? this.me : peerjid;
76
-    this.peerjid = peerjid;
77
-
78
+    this.initiator = isInitiator ? this.me : this.peerjid;
79
+    this.responder = !isInitiator ? this.me : this.peerjid;
78 80
     this.doInitialize();
79 81
 };
80 82
 
@@ -89,6 +91,16 @@ JingleSession.prototype.doInitialize = function() {};
89 91
  */
90 92
 JingleSession.prototype.addIceCandidates = function(contents) {};
91 93
 
94
+/**
95
+ * Checks if this JingleSession is in 'active' state which means that the call
96
+ * is in progress.
97
+ * @returns {boolean} <tt>true</tt> if this JingleSession is in 'active' state
98
+ *          or <tt>false</tt> otherwise.
99
+ */
100
+JingleSession.prototype.active = function () {
101
+    return this.state === 'active';
102
+};
103
+
92 104
 /**
93 105
  * Handles an 'add-source' event.
94 106
  *
@@ -104,29 +116,20 @@ JingleSession.prototype.addSources = function(contents) {};
104 116
 JingleSession.prototype.removeSources = function(contents) {};
105 117
 
106 118
 /**
107
- * Terminates this Jingle session (stops sending media and closes the streams?)
108
- */
109
-JingleSession.prototype.terminate = function() {};
110
-
111
-/**
112
- * Sends a Jingle session-terminate message to the peer and terminates the
113
- * session.
114
- * @param reason
115
- * @param text
119
+ * Terminates this Jingle session by sending session-terminate
120
+ * @param reason XMPP Jingle error condition
121
+ * @param text some meaningful error message
116 122
  */
117
-JingleSession.prototype.sendTerminate = function(reason, text) {};
123
+JingleSession.prototype.terminate = function(reason, text) {};
118 124
 
119 125
 /**
120 126
  * Handles an offer from the remote peer (prepares to accept a session).
121 127
  * @param jingle the 'jingle' XML element.
128
+ * @param success callback called when we the incoming session has been accepted
129
+ * @param failure callback called when we fail for any reason, will supply error
130
+ *        object with details(which is meant more to be printed to the logger
131
+ *        than analysed in the code, as the error is unrecoverable anyway)
122 132
  */
123
-JingleSession.prototype.setOffer = function(jingle) {};
124
-
125
-/**
126
- * Handles an answer from the remote peer (prepares to accept a session).
127
- * @param jingle the 'jingle' XML element.
128
- */
129
-JingleSession.prototype.setAnswer = function(jingle) {};
130
-
133
+JingleSession.prototype.acceptOffer = function(jingle, success, failure) {};
131 134
 
132 135
 module.exports = JingleSession;

+ 155
- 585
modules/xmpp/JingleSessionPC.js 查看文件

@@ -11,27 +11,25 @@ var XMPPEvents = require("../../service/xmpp/XMPPEvents");
11 11
 var RTCBrowserType = require("../RTC/RTCBrowserType");
12 12
 var RTC = require("../RTC/RTC");
13 13
 
14
+/**
15
+ * Constant tells how long we're going to wait for IQ response, before timeout
16
+ * error is  triggered.
17
+ * @type {number}
18
+ */
19
+var IQ_TIMEOUT = 10000;
20
+
14 21
 // Jingle stuff
15
-function JingleSessionPC(me, sid, connection, service) {
16
-    JingleSession.call(this, me, sid, connection, service);
17
-    this.initiator = null;
18
-    this.responder = null;
19
-    this.peerjid = null;
20
-    this.state = null;
22
+function JingleSessionPC(me, sid, peerjid, connection,
23
+                         media_constraints, ice_config, service, eventEmitter) {
24
+    JingleSession.call(this, me, sid, peerjid, connection,
25
+                       media_constraints, ice_config, service, eventEmitter);
21 26
     this.localSDP = null;
22 27
     this.remoteSDP = null;
23 28
 
24
-    this.usetrickle = true;
25
-    this.usepranswer = false; // early transport warmup -- mind you, this might fail. depends on webrtc issue 1718
26
-
27 29
     this.hadstuncandidate = false;
28 30
     this.hadturncandidate = false;
29 31
     this.lasticecandidate = false;
30 32
 
31
-    this.statsinterval = null;
32
-
33
-    this.reason = null;
34
-
35 33
     this.addssrc = [];
36 34
     this.removessrc = [];
37 35
     this.pendingop = null;
@@ -51,13 +49,6 @@ function JingleSessionPC(me, sid, connection, service) {
51 49
     this.webrtcIceUdpDisable = !!this.service.options.webrtcIceUdpDisable;
52 50
     this.webrtcIceTcpDisable = !!this.service.options.webrtcIceTcpDisable;
53 51
 
54
-    /**
55
-     * The indicator which determines whether the (local) video has been muted
56
-     * in response to a user command in contrast to an automatic decision made
57
-     * by the application logic.
58
-     */
59
-    this.videoMuteByUser = false;
60
-
61 52
     this.modifySourcesQueue = async.queue(this._modifySources.bind(this), 1);
62 53
     // We start with the queue paused. We resume it when the signaling state is
63 54
     // stable and the ice connection state is connected.
@@ -68,14 +59,6 @@ JingleSessionPC.prototype = JingleSession.prototype;
68 59
 JingleSessionPC.prototype.constructor = JingleSessionPC;
69 60
 
70 61
 
71
-JingleSessionPC.prototype.setOffer = function(offer) {
72
-    this.setRemoteDescription(offer, 'offer');
73
-};
74
-
75
-JingleSessionPC.prototype.setAnswer = function(answer) {
76
-    this.setRemoteDescription(answer, 'answer');
77
-};
78
-
79 62
 JingleSessionPC.prototype.updateModifySourcesQueue = function() {
80 63
     var signalingState = this.peerconnection.signalingState;
81 64
     var iceConnectionState = this.peerconnection.iceConnectionState;
@@ -191,138 +174,12 @@ JingleSessionPC.prototype.doInitialize = function () {
191 174
                 self.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
192 175
                 break;
193 176
         }
194
-        onIceConnectionStateChange(self.sid, self);
195 177
     };
196 178
     this.peerconnection.onnegotiationneeded = function (event) {
197 179
         self.room.eventEmitter.emit(XMPPEvents.PEERCONNECTION_READY, self);
198 180
     };
199 181
 };
200 182
 
201
-function onIceConnectionStateChange(sid, session) {
202
-    switch (session.peerconnection.iceConnectionState) {
203
-        case 'checking':
204
-            session.timeChecking = (new Date()).getTime();
205
-            session.firstconnect = true;
206
-            break;
207
-        case 'completed': // on caller side
208
-        case 'connected':
209
-            if (session.firstconnect) {
210
-                session.firstconnect = false;
211
-                var metadata = {};
212
-                metadata.setupTime
213
-                    = (new Date()).getTime() - session.timeChecking;
214
-                session.peerconnection.getStats(function (res) {
215
-                    if(res && res.result) {
216
-                        res.result().forEach(function (report) {
217
-                            if (report.type == 'googCandidatePair' &&
218
-                                report.stat('googActiveConnection') == 'true') {
219
-                                metadata.localCandidateType
220
-                                    = report.stat('googLocalCandidateType');
221
-                                metadata.remoteCandidateType
222
-                                    = report.stat('googRemoteCandidateType');
223
-
224
-                                // log pair as well so we can get nice pie
225
-                                // charts
226
-                                metadata.candidatePair
227
-                                    = report.stat('googLocalCandidateType') +
228
-                                        ';' +
229
-                                        report.stat('googRemoteCandidateType');
230
-
231
-                                if (report.stat('googRemoteAddress').indexOf('[') === 0)
232
-                                {
233
-                                    metadata.ipv6 = true;
234
-                                }
235
-                            }
236
-                        });
237
-                    }
238
-                });
239
-            }
240
-            break;
241
-    }
242
-}
243
-
244
-JingleSessionPC.prototype.accept = function () {
245
-    this.state = 'active';
246
-
247
-    var pranswer = this.peerconnection.localDescription;
248
-    if (!pranswer || pranswer.type != 'pranswer') {
249
-        return;
250
-    }
251
-    logger.log('going from pranswer to answer');
252
-    if (this.usetrickle) {
253
-        // remove candidates already sent from session-accept
254
-        var lines = SDPUtil.find_lines(pranswer.sdp, 'a=candidate:');
255
-        for (var i = 0; i < lines.length; i++) {
256
-            pranswer.sdp = pranswer.sdp.replace(lines[i] + '\r\n', '');
257
-        }
258
-    }
259
-    while (SDPUtil.find_line(pranswer.sdp, 'a=inactive')) {
260
-        // FIXME: change any inactive to sendrecv or whatever they were originally
261
-        pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
262
-    }
263
-    var prsdp = new SDP(pranswer.sdp);
264
-    if (this.webrtcIceTcpDisable) {
265
-        prsdp.removeTcpCandidates = true;
266
-    }
267
-    if (this.webrtcIceUdpDisable) {
268
-        prsdp.removeUdpCandidates = true;
269
-    }
270
-    var accept = $iq({to: this.peerjid,
271
-        type: 'set'})
272
-        .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
273
-            action: 'session-accept',
274
-            initiator: this.initiator,
275
-            responder: this.responder,
276
-            sid: this.sid });
277
-    // FIXME why do we generate session-accept in 3 different places ?
278
-    prsdp.toJingle(
279
-        accept,
280
-        this.initiator == this.me ? 'initiator' : 'responder');
281
-    var sdp = this.peerconnection.localDescription.sdp;
282
-    while (SDPUtil.find_line(sdp, 'a=inactive')) {
283
-        // FIXME: change any inactive to sendrecv or whatever they were originally
284
-        sdp = sdp.replace('a=inactive', 'a=sendrecv');
285
-    }
286
-    var self = this;
287
-    this.peerconnection.setLocalDescription(new RTCSessionDescription({type: 'answer', sdp: sdp}),
288
-        function () {
289
-            self.connection.sendIQ(accept,
290
-                function () {
291
-                    var ack = {};
292
-                    ack.source = 'answer';
293
-                    $(document).trigger('ack.jingle', [self.sid, ack]);
294
-                },
295
-                function (stanza) {
296
-                    var error = ($(stanza).find('error').length) ? {
297
-                        code: $(stanza).find('error').attr('code'),
298
-                        reason: $(stanza).find('error :first')[0].tagName
299
-                    }:{};
300
-                    error.source = 'answer';
301
-                    JingleSessionPC.onJingleError(self.sid, error);
302
-                },
303
-                10000);
304
-        },
305
-        function (e) {
306
-            logger.error('setLocalDescription failed', e);
307
-            self.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
308
-        }
309
-    );
310
-};
311
-
312
-JingleSessionPC.prototype.terminate = function (reason) {
313
-    this.state = 'ended';
314
-    this.reason = reason;
315
-    this.peerconnection.close();
316
-    if (this.statsinterval !== null) {
317
-        window.clearInterval(this.statsinterval);
318
-        this.statsinterval = null;
319
-    }
320
-};
321
-
322
-JingleSessionPC.prototype.active = function () {
323
-    return this.state == 'active';
324
-};
325
-
326 183
 JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
327 184
     var self = this;
328 185
     if (candidate && !this.lasticecandidate) {
@@ -340,79 +197,25 @@ JingleSessionPC.prototype.sendIceCandidate = function (candidate) {
340 197
             this.hadturncandidate = true;
341 198
         }
342 199
 
343
-        if (this.usetrickle) {
344
-            if (this.usedrip) {
345
-                if (this.drip_container.length === 0) {
346
-                    // start 20ms callout
347
-                    window.setTimeout(function () {
348
-                        if (self.drip_container.length === 0) return;
349
-                        self.sendIceCandidates(self.drip_container);
350
-                        self.drip_container = [];
351
-                    }, 20);
352
-
353
-                }
354
-                this.drip_container.push(candidate);
355
-                return;
356
-            } else {
357
-                self.sendIceCandidates([candidate]);
358
-                // FIXME this.lasticecandidate is going to be set to true
359
-                // bellow and that seems wrong. The execution doesn't come here
360
-                // with the default values at the time of this writing.
200
+        if (this.usedrip) {
201
+            if (this.drip_container.length === 0) {
202
+                // start 20ms callout
203
+                window.setTimeout(function () {
204
+                    if (self.drip_container.length === 0) return;
205
+                    self.sendIceCandidates(self.drip_container);
206
+                    self.drip_container = [];
207
+                }, 20);
361 208
             }
209
+            this.drip_container.push(candidate);
210
+        } else {
211
+            self.sendIceCandidates([candidate]);
362 212
         }
363 213
     } else {
364
-        //logger.log('sendIceCandidate: last candidate.');
365
-        if (!this.usetrickle) {
366
-            //logger.log('should send full offer now...');
367
-            //FIXME why do we generate session-accept in 3 different places ?
368
-            var init = $iq({to: this.peerjid,
369
-                type: 'set'})
370
-                .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
371
-                    action: this.peerconnection.localDescription.type == 'offer' ? 'session-initiate' : 'session-accept',
372
-                    initiator: this.initiator,
373
-                    sid: this.sid});
374
-            this.localSDP = new SDP(this.peerconnection.localDescription.sdp);
375
-            if (self.webrtcIceTcpDisable) {
376
-                this.localSDP.removeTcpCandidates = true;
377
-            }
378
-            if (self.webrtcIceUdpDisable) {
379
-                this.localSDP.removeUdpCandidates = true;
380
-            }
381
-            var sendJingle = function (ssrc) {
382
-                if(!ssrc)
383
-                    ssrc = {};
384
-                self.localSDP.toJingle(
385
-                    init,
386
-                    self.initiator == self.me ? 'initiator' : 'responder',
387
-                    ssrc);
388
-                self.connection.sendIQ(init,
389
-                    function () {
390
-                        //logger.log('session initiate ack');
391
-                        var ack = {};
392
-                        ack.source = 'offer';
393
-                        $(document).trigger('ack.jingle', [self.sid, ack]);
394
-                    },
395
-                    function (stanza) {
396
-                        self.state = 'error';
397
-                        self.peerconnection.close();
398
-                        var error = ($(stanza).find('error').length) ? {
399
-                            code: $(stanza).find('error').attr('code'),
400
-                            reason: $(stanza).find('error :first')[0].tagName,
401
-                        }:{};
402
-                        error.source = 'offer';
403
-                        JingleSessionPC.onJingleError(self.sid, error);
404
-                    },
405
-                    10000);
406
-            };
407
-            sendJingle();
408
-        }
214
+        logger.log('sendIceCandidate: last candidate.');
215
+        // FIXME: remember to re-think in ICE-restart
409 216
         this.lasticecandidate = true;
410 217
         logger.log('Have we encountered any srflx candidates? ' + this.hadstuncandidate);
411 218
         logger.log('Have we encountered any relay candidates? ' + this.hadturncandidate);
412
-
413
-        if (!(this.hadstuncandidate || this.hadturncandidate) && this.peerconnection.signalingState != 'closed') {
414
-            $(document).trigger('nostuncandidates.jingle', [this.sid]);
415
-        }
416 219
     }
417 220
 };
418 221
 
@@ -454,21 +257,8 @@ JingleSessionPC.prototype.sendIceCandidates = function (candidates) {
454 257
     }
455 258
     // might merge last-candidate notification into this, but it is called alot later. See webrtc issue #2340
456 259
     //logger.log('was this the last candidate', this.lasticecandidate);
457
-    this.connection.sendIQ(cand,
458
-        function () {
459
-            var ack = {};
460
-            ack.source = 'transportinfo';
461
-            $(document).trigger('ack.jingle', [this.sid, ack]);
462
-        },
463
-        function (stanza) {
464
-            var error = ($(stanza).find('error').length) ? {
465
-                code: $(stanza).find('error').attr('code'),
466
-                reason: $(stanza).find('error :first')[0].tagName,
467
-            }:{};
468
-            error.source = 'transportinfo';
469
-            JingleSessionPC.onJingleError(this.sid, error);
470
-        },
471
-        10000);
260
+    this.connection.sendIQ(
261
+        cand, null, this.newJingleErrorHandler(cand), IQ_TIMEOUT);
472 262
 };
473 263
 
474 264
 JingleSessionPC.prototype.readSsrcInfo = function (contents) {
@@ -489,7 +279,18 @@ JingleSessionPC.prototype.readSsrcInfo = function (contents) {
489 279
     });
490 280
 };
491 281
 
492
-JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype) {
282
+JingleSessionPC.prototype.acceptOffer = function(jingleOffer,
283
+                                                 success, failure) {
284
+    this.state = 'active';
285
+    this.setRemoteDescription(jingleOffer, 'offer',
286
+        function() {
287
+            this.sendAnswer(success, failure);
288
+        }.bind(this),
289
+        failure);
290
+};
291
+
292
+JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype,
293
+                                                           success, failure) {
493 294
     //logger.log('setting remote description... ', desctype);
494 295
     this.remoteSDP = new SDP('');
495 296
     if (this.webrtcIceTcpDisable) {
@@ -501,205 +302,46 @@ JingleSessionPC.prototype.setRemoteDescription = function (elem, desctype) {
501 302
 
502 303
     this.remoteSDP.fromJingle(elem);
503 304
     this.readSsrcInfo($(elem).find(">content"));
504
-    var pcremotedesc = this.peerconnection.remoteDescription;
505
-    if (pcremotedesc) {
506
-        logger.log('setRemoteDescription when remote description is not null, should be pranswer', pcremotedesc);
507
-        if (pcremotedesc.type == 'pranswer') {
508
-            var pranswer = new SDP(pcremotedesc.sdp);
509
-            for (var i = 0; i < pranswer.media.length; i++) {
510
-                // make sure we have ice ufrag and pwd
511
-                if (!SDPUtil.find_line(this.remoteSDP.media[i], 'a=ice-ufrag:', this.remoteSDP.session)) {
512
-                    var ice_ufrag_line = SDPUtil.find_line(pranswer.media[i], 'a=ice-ufrag:', pranswer.session);
513
-                    if (ice_ufrag_line) {
514
-                        this.remoteSDP.media[i] += ice_ufrag_line + '\r\n';
515
-                    } else {
516
-                        logger.warn('no ice ufrag?');
517
-                    }
518
-                    var ice_pwd_line = SDPUtil.find_line(pranswer.media[i], 'a=ice-pwd:', pranswer.session);
519
-                    if (ice_pwd_line) {
520
-                        this.remoteSDP.media[i] += ice_pwd_line + '\r\n';
521
-                    } else {
522
-                        logger.warn('no ice pwd?');
523
-                    }
524
-                }
525
-                // copy over candidates
526
-                var lines = SDPUtil.find_lines(pranswer.media[i], 'a=candidate:');
527
-                for (var j = 0; j < lines.length; j++) {
528
-                    this.remoteSDP.media[i] += lines[j] + '\r\n';
529
-                }
530
-            }
531
-            this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
532
-        }
533
-    }
534 305
     var remotedesc = new RTCSessionDescription({type: desctype, sdp: this.remoteSDP.raw});
535 306
 
536 307
     this.peerconnection.setRemoteDescription(remotedesc,
537 308
         function () {
538 309
             //logger.log('setRemoteDescription success');
310
+            if (success) {
311
+                success();
312
+            }
539 313
         },
540 314
         function (e) {
541 315
             logger.error('setRemoteDescription error', e);
542
-            JingleSessionPC.onJingleFatalError(self, e);
543
-        }
316
+            if (failure)
317
+                failure(e);
318
+            JingleSessionPC.onJingleFatalError(this, e);
319
+        }.bind(this)
544 320
     );
545 321
 };
546 322
 
547
-/**
548
- * Adds remote ICE candidates to this Jingle session.
549
- * @param elem An array of Jingle "content" elements?
550
- */
551
-JingleSessionPC.prototype.addIceCandidate = function (elem) {
552
-    var self = this;
553
-    if (this.peerconnection.signalingState == 'closed') {
554
-        return;
555
-    }
556
-    if (!this.peerconnection.remoteDescription && this.peerconnection.signalingState == 'have-local-offer') {
557
-        logger.log('trickle ice candidate arriving before session accept...');
558
-        // create a PRANSWER for setRemoteDescription
559
-        if (!this.remoteSDP) {
560
-            var cobbled = 'v=0\r\n' +
561
-                'o=- 1923518516 2 IN IP4 0.0.0.0\r\n' +// FIXME
562
-                's=-\r\n' +
563
-                't=0 0\r\n';
564
-            // first, take some things from the local description
565
-            for (var i = 0; i < this.localSDP.media.length; i++) {
566
-                cobbled += SDPUtil.find_line(this.localSDP.media[i], 'm=') + '\r\n';
567
-                cobbled += SDPUtil.find_lines(this.localSDP.media[i], 'a=rtpmap:').join('\r\n') + '\r\n';
568
-                var mid_line = SDPUtil.find_line(this.localSDP.media[i], 'a=mid:');
569
-                if (mid_line) {
570
-                    cobbled += mid_line + '\r\n';
571
-                }
572
-                cobbled += 'a=inactive\r\n';
573
-            }
574
-            this.remoteSDP = new SDP(cobbled);
575
-        }
576
-        // then add things like ice and dtls from remote candidate
577
-        elem.each(function () {
578
-            for (var i = 0; i < self.remoteSDP.media.length; i++) {
579
-                if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
580
-                    self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
581
-                    if (!SDPUtil.find_line(self.remoteSDP.media[i], 'a=ice-ufrag:')) {
582
-                        var tmp = $(this).find('transport');
583
-                        self.remoteSDP.media[i] += 'a=ice-ufrag:' + tmp.attr('ufrag') + '\r\n';
584
-                        self.remoteSDP.media[i] += 'a=ice-pwd:' + tmp.attr('pwd') + '\r\n';
585
-                        tmp = $(this).find('transport>fingerprint');
586
-                        if (tmp.length) {
587
-                            self.remoteSDP.media[i] += 'a=fingerprint:' + tmp.attr('hash') + ' ' + tmp.text() + '\r\n';
588
-                        } else {
589
-                            logger.log('no dtls fingerprint (webrtc issue #1718?)');
590
-                            self.remoteSDP.media[i] += 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:BAADBAADBAADBAADBAADBAADBAADBAADBAADBAAD\r\n';
591
-                        }
592
-                        break;
593
-                    }
594
-                }
595
-            }
596
-        });
597
-        this.remoteSDP.raw = this.remoteSDP.session + this.remoteSDP.media.join('');
598
-
599
-        // we need a complete SDP with ice-ufrag/ice-pwd in all parts
600
-        // this makes the assumption that the PRANSWER is constructed such that the ice-ufrag is in all mediaparts
601
-        // but it could be in the session part as well. since the code above constructs this sdp this can't happen however
602
-        var iscomplete = this.remoteSDP.media.filter(function (mediapart) {
603
-            return SDPUtil.find_line(mediapart, 'a=ice-ufrag:');
604
-        }).length == this.remoteSDP.media.length;
605
-
606
-        if (iscomplete) {
607
-            logger.log('setting pranswer');
608
-            try {
609
-                this.peerconnection.setRemoteDescription(new RTCSessionDescription({type: 'pranswer', sdp: this.remoteSDP.raw }),
610
-                    function() {
611
-                    },
612
-                    function(e) {
613
-                        logger.log('setRemoteDescription pranswer failed', e.toString());
614
-                    });
615
-            } catch (e) {
616
-                logger.error('setting pranswer failed', e);
617
-            }
618
-        } else {
619
-            //logger.log('not yet setting pranswer');
620
-        }
621
-    }
622
-    // operate on each content element
623
-    elem.each(function () {
624
-        // would love to deactivate this, but firefox still requires it
625
-        var idx = -1;
626
-        var i;
627
-        for (i = 0; i < self.remoteSDP.media.length; i++) {
628
-            if (SDPUtil.find_line(self.remoteSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
629
-                self.remoteSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
630
-                idx = i;
631
-                break;
632
-            }
633
-        }
634
-        if (idx == -1) { // fall back to localdescription
635
-            for (i = 0; i < self.localSDP.media.length; i++) {
636
-                if (SDPUtil.find_line(self.localSDP.media[i], 'a=mid:' + $(this).attr('name')) ||
637
-                    self.localSDP.media[i].indexOf('m=' + $(this).attr('name')) === 0) {
638
-                    idx = i;
639
-                    break;
640
-                }
641
-            }
642
-        }
643
-        var name = $(this).attr('name');
644
-        // TODO: check ice-pwd and ice-ufrag?
645
-        $(this).find('transport>candidate').each(function () {
646
-            var line, candidate;
647
-            var protocol = this.getAttribute('protocol');
648
-            protocol =
649
-                (typeof protocol === 'string') ? protocol.toLowerCase() : '';
650
-            if ((self.webrtcIceTcpDisable && protocol == 'tcp') ||
651
-                (self.webrtcIceUdpDisable && protocol == 'udp')) {
652
-                return;
653
-            }
654
-
655
-            line = SDPUtil.candidateFromJingle(this);
656
-            candidate = new RTCIceCandidate({sdpMLineIndex: idx,
657
-                sdpMid: name,
658
-                candidate: line});
659
-            try {
660
-                self.peerconnection.addIceCandidate(candidate);
661
-            } catch (e) {
662
-                logger.error('addIceCandidate failed', e.toString(), line);
663
-                self.room.eventEmitter.emit(XMPPEvents.ADD_ICE_CANDIDATE_FAILED,
664
-                    err, self.peerconnection);
665
-            }
666
-        });
667
-    });
668
-};
669
-
670
-JingleSessionPC.prototype.sendAnswer = function (provisional) {
671
-    //logger.log('createAnswer', provisional);
672
-    var self = this;
323
+JingleSessionPC.prototype.sendAnswer = function (success, failure) {
324
+    //logger.log('createAnswer');
673 325
     this.peerconnection.createAnswer(
674 326
         function (sdp) {
675
-            self.createdAnswer(sdp, provisional);
676
-        },
677
-        function (e) {
678
-            logger.error('createAnswer failed', e);
679
-            self.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
680
-        },
327
+            this.createdAnswer(sdp, success, failure);
328
+        }.bind(this),
329
+        function (error) {
330
+            logger.error("createAnswer failed", error);
331
+            if (failure)
332
+                failure(error);
333
+            this.room.eventEmitter.emit(
334
+                    XMPPEvents.CONFERENCE_SETUP_FAILED, error);
335
+        }.bind(this),
681 336
         this.media_constraints
682 337
     );
683 338
 };
684 339
 
685
-JingleSessionPC.prototype.createdAnswer = function (sdp, provisional) {
340
+JingleSessionPC.prototype.createdAnswer = function (sdp, success, failure) {
686 341
     //logger.log('createAnswer callback');
687 342
     var self = this;
688 343
     this.localSDP = new SDP(sdp.sdp);
689
-    //this.localSDP.mangle();
690
-    this.usepranswer = provisional === true;
691
-    if (this.usetrickle) {
692
-        if (this.usepranswer) {
693
-            sdp.type = 'pranswer';
694
-            for (var i = 0; i < this.localSDP.media.length; i++) {
695
-                this.localSDP.media[i] = this.localSDP.media[i].replace('a=sendrecv\r\n', 'a=inactive\r\n');
696
-            }
697
-            this.localSDP.raw = this.localSDP.session + '\r\n' + this.localSDP.media.join('');
698
-        }
699
-    }
700
-    var self = this;
701 344
     var sendJingle = function (ssrcs) {
702
-                // FIXME why do we generate session-accept in 3 different places ?
703 345
                 var accept = $iq({to: self.peerjid,
704 346
                     type: 'set'})
705 347
                     .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
@@ -719,32 +361,20 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, provisional) {
719 361
                     ssrcs);
720 362
                 self.fixJingle(accept);
721 363
                 self.connection.sendIQ(accept,
722
-                    function () {
723
-                        var ack = {};
724
-                        ack.source = 'answer';
725
-                        $(document).trigger('ack.jingle', [self.sid, ack]);
726
-                    },
727
-                    function (stanza) {
728
-                        var error = ($(stanza).find('error').length) ? {
729
-                            code: $(stanza).find('error').attr('code'),
730
-                            reason: $(stanza).find('error :first')[0].tagName,
731
-                        }:{};
732
-                        error.source = 'answer';
733
-                        JingleSessionPC.onJingleError(self.sid, error);
734
-                    },
735
-                    10000);
736
-    }
364
+                    success,
365
+                    self.newJingleErrorHandler(accept, failure),
366
+                    IQ_TIMEOUT);
367
+    };
737 368
     sdp.sdp = this.localSDP.raw;
738 369
     this.peerconnection.setLocalDescription(sdp,
739 370
         function () {
740
-
741 371
             //logger.log('setLocalDescription success');
742
-            if (self.usetrickle && !self.usepranswer) {
743
-                sendJingle();
744
-            }
372
+            sendJingle(success, failure);
745 373
         },
746
-        function (e) {
747
-            logger.error('setLocalDescription failed', e);
374
+        function (error) {
375
+            logger.error('setLocalDescription failed', error);
376
+            if (failure)
377
+                failure(error);
748 378
             self.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
749 379
         }
750 380
     );
@@ -759,42 +389,39 @@ JingleSessionPC.prototype.createdAnswer = function (sdp, provisional) {
759 389
     }
760 390
 };
761 391
 
762
-JingleSessionPC.prototype.sendTerminate = function (reason, text) {
763
-    var self = this,
764
-        term = $iq({to: this.peerjid,
765
-            type: 'set'})
766
-            .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
767
-                action: 'session-terminate',
768
-                initiator: this.initiator,
769
-                sid: this.sid})
770
-            .c('reason')
771
-            .c(reason || 'success');
392
+JingleSessionPC.prototype.terminate = function (reason,  text,
393
+                                                success, failure) {
394
+    var term = $iq({to: this.peerjid,
395
+        type: 'set'})
396
+        .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
397
+            action: 'session-terminate',
398
+            initiator: this.initiator,
399
+            sid: this.sid})
400
+        .c('reason')
401
+        .c(reason || 'success');
772 402
 
773 403
     if (text) {
774 404
         term.up().c('text').t(text);
775 405
     }
776 406
 
777
-    this.connection.sendIQ(term,
778
-        function () {
779
-            self.peerconnection.close();
780
-            self.peerconnection = null;
781
-            self.terminate();
782
-            var ack = {};
783
-            ack.source = 'terminate';
784
-            $(document).trigger('ack.jingle', [self.sid, ack]);
785
-        },
786
-        function (stanza) {
787
-            var error = ($(stanza).find('error').length) ? {
788
-                code: $(stanza).find('error').attr('code'),
789
-                reason: $(stanza).find('error :first')[0].tagName,
790
-            }:{};
791
-            $(document).trigger('ack.jingle', [self.sid, error]);
792
-        },
793
-        10000);
794
-    if (this.statsinterval !== null) {
795
-        window.clearInterval(this.statsinterval);
796
-        this.statsinterval = null;
797
-    }
407
+    this.connection.sendIQ(
408
+        term, success, this.newJingleErrorHandler(term, failure), IQ_TIMEOUT);
409
+
410
+    // this should result in 'onTerminated' being called by strope.jingle.js
411
+    this.connection.jingle.terminate(this.sid);
412
+};
413
+
414
+JingleSessionPC.prototype.onTerminated = function (reasonCondition,
415
+                                                   reasonText) {
416
+    this.state = 'ended';
417
+
418
+    // Do something with reason and reasonCondition when we start to care
419
+    //this.reasonCondition = reasonCondition;
420
+    //this.reasonText = reasonText;
421
+    logger.info("Session terminated", this, reasonCondition, reasonText);
422
+
423
+    if (this.peerconnection)
424
+        this.peerconnection.close();
798 425
 };
799 426
 
800 427
 /**
@@ -1224,14 +851,8 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1224 851
 
1225 852
     if (removed && remove) {
1226 853
         logger.info("Sending source-remove", remove.tree());
1227
-        this.connection.sendIQ(remove,
1228
-            function (res) {
1229
-                logger.info('got remove result', res);
1230
-            },
1231
-            function (err) {
1232
-                logger.error('got remove error', err);
1233
-            }
1234
-        );
854
+        this.connection.sendIQ(
855
+            remove, null, this.newJingleErrorHandler(remove), IQ_TIMEOUT);
1235 856
     } else {
1236 857
         logger.log('removal not necessary');
1237 858
     }
@@ -1252,111 +873,71 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
1252 873
 
1253 874
     if (added && add) {
1254 875
         logger.info("Sending source-add", add.tree());
1255
-        this.connection.sendIQ(add,
1256
-            function (res) {
1257
-                logger.info('got add result', res);
1258
-            },
1259
-            function (err) {
1260
-                logger.error('got add error', err);
1261
-            }
1262
-        );
876
+        this.connection.sendIQ(
877
+            add, null, this.newJingleErrorHandler(add), IQ_TIMEOUT);
1263 878
     } else {
1264 879
         logger.log('addition not necessary');
1265 880
     }
1266 881
 };
1267 882
 
1268
-JingleSessionPC.prototype.getStats = function (interval) {
1269
-    var self = this;
1270
-    var recv = {audio: 0, video: 0};
1271
-    var lost = {audio: 0, video: 0};
1272
-    var lastrecv = {audio: 0, video: 0};
1273
-    var lastlost = {audio: 0, video: 0};
1274
-    var loss = {audio: 0, video: 0};
1275
-    var delta = {audio: 0, video: 0};
1276
-    this.statsinterval = window.setInterval(function () {
1277
-        if (self && self.peerconnection && self.peerconnection.getStats) {
1278
-            self.peerconnection.getStats(function (stats) {
1279
-                var results = stats.result();
1280
-                // TODO: there are so much statistics you can get from this..
1281
-                for (var i = 0; i < results.length; ++i) {
1282
-                    if (results[i].type == 'ssrc') {
1283
-                        var packetsrecv = results[i].stat('packetsReceived');
1284
-                        var packetslost = results[i].stat('packetsLost');
1285
-                        if (packetsrecv && packetslost) {
1286
-                            packetsrecv = parseInt(packetsrecv, 10);
1287
-                            packetslost = parseInt(packetslost, 10);
1288
-
1289
-                            if (results[i].stat('googFrameRateReceived')) {
1290
-                                lastlost.video = lost.video;
1291
-                                lastrecv.video = recv.video;
1292
-                                recv.video = packetsrecv;
1293
-                                lost.video = packetslost;
1294
-                            } else {
1295
-                                lastlost.audio = lost.audio;
1296
-                                lastrecv.audio = recv.audio;
1297
-                                recv.audio = packetsrecv;
1298
-                                lost.audio = packetslost;
1299
-                            }
1300
-                        }
1301
-                    }
1302
-                }
1303
-                delta.audio = recv.audio - lastrecv.audio;
1304
-                delta.video = recv.video - lastrecv.video;
1305
-                loss.audio = (delta.audio > 0) ? Math.ceil(100 * (lost.audio - lastlost.audio) / delta.audio) : 0;
1306
-                loss.video = (delta.video > 0) ? Math.ceil(100 * (lost.video - lastlost.video) / delta.video) : 0;
1307
-                $(document).trigger('packetloss.jingle', [self.sid, loss]);
1308
-            });
883
+/**
884
+ * Method returns function(errorResponse) which is a callback to be passed to
885
+ * Strophe connection.sendIQ method. An 'error' structure is created that is
886
+ * passed as 1st argument to given <tt>failureCb</tt>. The format of this
887
+ * structure is as follows:
888
+ * {
889
+ *  code: {XMPP error response code}
890
+ *  reason: {the name of XMPP error reason element or 'timeout' if the request
891
+ *           has timed out within <tt>IQ_TIMEOUT</tt> milliseconds}
892
+ *  source: {request.tree() that provides original request}
893
+ *  session: {JingleSessionPC instance on which the error occurred}
894
+ * }
895
+ * @param request Strophe IQ instance which is the request to be dumped into
896
+ *        the error structure
897
+ * @param failureCb function(error) called when error response was returned or
898
+ *        when a timeout has occurred.
899
+ * @returns {function(this:JingleSessionPC)}
900
+ */
901
+JingleSessionPC.prototype.newJingleErrorHandler = function(request, failureCb) {
902
+    return function (errResponse) {
903
+
904
+        var error = { };
905
+
906
+        // Get XMPP error code and condition(reason)
907
+        var errorElSel = $(errResponse).find('error');
908
+        if (errorElSel.length) {
909
+            error.code = errorElSel.attr('code');
910
+            var errorReasonSel = $(errResponse).find('error :first');
911
+            if (errorReasonSel.length)
912
+                error.reason = errorReasonSel[0].tagName;
1309 913
         }
1310
-    }, interval || 3000);
1311
-    return this.statsinterval;
1312
-};
1313 914
 
1314
-JingleSessionPC.onJingleError = function (session, error)
1315
-{
1316
-    logger.error("Jingle error", error);
1317
-}
915
+        if (!errResponse) {
916
+            error.reason = 'timeout';
917
+        }
918
+
919
+        error.source = null;
920
+        if (request && "function" == typeof request.tree) {
921
+            error.source = request.tree();
922
+        }
923
+
924
+        error.session = this;
925
+
926
+        logger.error("Jingle error", error);
927
+        if (failureCb) {
928
+            failureCb(error);
929
+        }
930
+
931
+        this.room.eventEmitter.emit(XMPPEvents.JINGLE_ERROR, error);
932
+
933
+    }.bind(this);
934
+};
1318 935
 
1319 936
 JingleSessionPC.onJingleFatalError = function (session, error)
1320 937
 {
1321 938
     this.room.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
1322 939
     this.room.eventEmitter.emit(XMPPEvents.JINGLE_FATAL_ERROR, session, error);
1323
-}
1324
-
1325
-// an attempt to work around https://github.com/jitsi/jitmeet/issues/32
1326
-JingleSessionPC.prototype.sendKeyframe = function () {
1327
-    var pc = this.peerconnection;
1328
-    logger.log('sendkeyframe', pc.iceConnectionState);
1329
-    if (pc.iceConnectionState !== 'connected') return; // safe...
1330
-    var self = this;
1331
-    pc.setRemoteDescription(
1332
-        pc.remoteDescription,
1333
-        function () {
1334
-            pc.createAnswer(
1335
-                function (modifiedAnswer) {
1336
-                    pc.setLocalDescription(
1337
-                        modifiedAnswer,
1338
-                        function () {
1339
-                            // noop
1340
-                        },
1341
-                        function (error) {
1342
-                            logger.log('triggerKeyframe setLocalDescription failed', error);
1343
-                            self.room.eventEmitter.emit(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR);
1344
-                        }
1345
-                    );
1346
-                },
1347
-                function (error) {
1348
-                    logger.log('triggerKeyframe createAnswer failed', error);
1349
-                    self.room.eventEmitter.emit(XMPPEvents.CREATE_ANSWER_ERROR);
1350
-                }
1351
-            );
1352
-        },
1353
-        function (error) {
1354
-            logger.log('triggerKeyframe setRemoteDescription failed', error);
1355
-            eventEmitter.emit(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR);
1356
-        }
1357
-    );
1358
-}
1359
-
940
+};
1360 941
 
1361 942
 JingleSessionPC.prototype.remoteStreamAdded = function (data, times) {
1362 943
     var self = this;
@@ -1397,18 +978,7 @@ JingleSessionPC.prototype.remoteStreamAdded = function (data, times) {
1397 978
     }
1398 979
 
1399 980
     this.room.remoteStreamAdded(data, this.sid, thessrc);
1400
-
1401
-    var isVideo = data.stream.getVideoTracks().length > 0;
1402
-    // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
1403
-    if (isVideo &&
1404
-        data.peerjid && this.peerjid === data.peerjid &&
1405
-        data.stream.getVideoTracks().length === 0 &&
1406
-        RTC.localVideo.getTracks().length > 0) {
1407
-        window.setTimeout(function () {
1408
-            self.sendKeyframe();
1409
-        }, 3000);
1410
-    }
1411
-}
981
+};
1412 982
 
1413 983
 /**
1414 984
  * Handles remote stream removal.
@@ -1422,7 +992,7 @@ JingleSessionPC.prototype.remoteStreamRemoved = function (event) {
1422 992
     } else if (streamId && streamId.indexOf('mixedmslabel') === -1) {
1423 993
         this.room.eventEmitter.emit(XMPPEvents.REMOTE_STREAM_REMOVED, streamId);
1424 994
     }
1425
-}
995
+};
1426 996
 
1427 997
 /**
1428 998
  * Returns the ice connection state for the peer connection.
@@ -1430,7 +1000,7 @@ JingleSessionPC.prototype.remoteStreamRemoved = function (event) {
1430 1000
  */
1431 1001
 JingleSessionPC.prototype.getIceConnectionState = function () {
1432 1002
     return this.peerconnection.iceConnectionState;
1433
-}
1003
+};
1434 1004
 
1435 1005
 
1436 1006
 /**
@@ -1456,7 +1026,7 @@ JingleSessionPC.prototype.fixJingle = function(jingle) {
1456 1026
 
1457 1027
     var sources = $(jingle.tree()).find(">jingle>content>description>source");
1458 1028
     return sources && sources.length > 0;
1459
-}
1029
+};
1460 1030
 
1461 1031
 /**
1462 1032
  * Fixes the outgoing jingle packets with action source-add by removing the
@@ -1519,7 +1089,7 @@ JingleSessionPC.prototype.fixSourceAddJingle = function (jingle) {
1519 1089
             });
1520 1090
         });
1521 1091
     }
1522
-}
1092
+};
1523 1093
 
1524 1094
 /**
1525 1095
  * Fixes the outgoing jingle packets with action source-remove by removing the
@@ -1574,7 +1144,7 @@ JingleSessionPC.prototype.fixSourceRemoveJingle = function(jingle) {
1574 1144
                 }
1575 1145
             });
1576 1146
         });
1577
-}
1147
+};
1578 1148
 
1579 1149
 /**
1580 1150
  * Returns the description node related to the passed content type. If the node

+ 42
- 79
modules/xmpp/strophe.jingle.js 查看文件

@@ -61,18 +61,20 @@ module.exports = function(XMPP, eventEmitter) {
61 61
             logger.log('on jingle ' + action + ' from ' + fromJid, iq);
62 62
             var sess = this.sessions[sid];
63 63
             if ('session-initiate' != action) {
64
-                if (sess === null) {
65
-                    ack.type = 'error';
64
+                if (!sess) {
65
+                    ack.attrs({ type: 'error' });
66 66
                     ack.c('error', {type: 'cancel'})
67 67
                         .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
68 68
                         .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
69
+                    logger.warn('invalid session id', iq);
69 70
                     this.connection.send(ack);
70 71
                     return true;
71 72
                 }
72 73
                 // local jid is not checked
73 74
                 if (fromJid != sess.peerjid) {
74
-                    logger.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
75
-                    ack.type = 'error';
75
+                    logger.warn(
76
+                        'jid mismatch for session id', sid, sess.peerjid, iq);
77
+                    ack.attrs({ type: 'error' });
76 78
                     ack.c('error', {type: 'cancel'})
77 79
                         .c('item-not-found', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up()
78 80
                         .c('unknown-session', {xmlns: 'urn:xmpp:jingle:errors:1'});
@@ -82,15 +84,13 @@ module.exports = function(XMPP, eventEmitter) {
82 84
             } else if (sess !== undefined) {
83 85
                 // existing session with same session id
84 86
                 // this might be out-of-order if the sess.peerjid is the same as from
85
-                ack.type = 'error';
87
+                ack.attrs({ type: 'error' });
86 88
                 ack.c('error', {type: 'cancel'})
87 89
                     .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'}).up();
88
-                logger.warn('duplicate session id', sid);
90
+                logger.warn('duplicate session id', sid, iq);
89 91
                 this.connection.send(ack);
90 92
                 return true;
91 93
             }
92
-            // FIXME: check for a defined action
93
-            this.connection.send(ack);
94 94
             // see http://xmpp.org/extensions/xep-0166.html#concepts-session
95 95
             switch (action) {
96 96
                 case 'session-initiate':
@@ -104,73 +104,40 @@ module.exports = function(XMPP, eventEmitter) {
104 104
                                 audioMuted === "true", videoMuted === "true");
105 105
                     }
106 106
                     sess = new JingleSession(
107
-                        $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
108
-                        this.connection, XMPP);
109
-                    // configure session
110
-
111
-                    var fromBareJid = Strophe.getBareJidFromJid(fromJid);
112
-                    this.connection.emuc.setJingleSession(fromBareJid, sess);
113
-
114
-                    sess.media_constraints = this.media_constraints;
115
-                    sess.ice_config = this.ice_config;
116
-
117
-                    sess.initialize(fromJid, false);
118
-                    eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
119
-                    // FIXME: setRemoteDescription should only be done when this call is to be accepted
120
-                    sess.setOffer($(iq).find('>jingle'));
107
+                            $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
108
+                            fromJid,
109
+                            this.connection,
110
+                            this.media_constraints,
111
+                            this.ice_config, XMPP);
121 112
 
122 113
                     this.sessions[sess.sid] = sess;
123 114
                     this.jid2session[sess.peerjid] = sess;
124 115
 
125
-                    // the callback should either
126
-                    // .sendAnswer and .accept
127
-                    // or .sendTerminate -- not necessarily synchronous
128
-
129
-                    sess.sendAnswer();
130
-                    sess.accept();
131
-                    break;
132
-                case 'session-accept':
133
-                    sess.setAnswer($(iq).find('>jingle'));
134
-                    sess.accept();
135
-                    break;
136
-                case 'session-terminate':
137
-                    // If this is not the focus sending the terminate, we have
138
-                    // nothing more to do here.
139
-                    if (Object.keys(this.sessions).length < 1
140
-                        || !(this.sessions[Object.keys(this.sessions)[0]]
141
-                            instanceof JingleSession))
116
+                    var jingleOffer = $(iq).find('>jingle');
117
+                    // FIXME there's no nice way with event to get the reason
118
+                    // why the call was rejected
119
+                    eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess, jingleOffer);
120
+                    if (!sess.active())
142 121
                     {
143
-                        break;
122
+                        // Call not accepted
123
+                        ack.attrs({ type: 'error' });
124
+                        ack.c('error', {type: 'cancel'})
125
+                           .c('bad-request',
126
+                            { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })
127
+                            .up();
128
+                        this.terminate(sess.sid);
144 129
                     }
130
+                    break;
131
+                case 'session-terminate':
145 132
                     logger.log('terminating...', sess.sid);
146
-                    sess.terminate();
147
-                    this.terminate(sess.sid);
133
+                    var reasonCondition = null;
134
+                    var reasonText = null;
148 135
                     if ($(iq).find('>jingle>reason').length) {
149
-                        $(document).trigger('callterminated.jingle', [
150
-                            sess.sid,
151
-                            sess.peerjid,
152
-                            $(iq).find('>jingle>reason>:first')[0].tagName,
153
-                            $(iq).find('>jingle>reason>text').text()
154
-                        ]);
155
-                    } else {
156
-                        $(document).trigger('callterminated.jingle',
157
-                            [sess.sid, sess.peerjid]);
158
-                    }
159
-                    break;
160
-                case 'transport-info':
161
-                    sess.addIceCandidate($(iq).find('>jingle>content'));
162
-                    break;
163
-                case 'session-info':
164
-                    var affected;
165
-                    if ($(iq).find('>jingle>ringing[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
166
-                        $(document).trigger('ringing.jingle', [sess.sid]);
167
-                    } else if ($(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
168
-                        affected = $(iq).find('>jingle>mute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
169
-                        $(document).trigger('mute.jingle', [sess.sid, affected]);
170
-                    } else if ($(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').length) {
171
-                        affected = $(iq).find('>jingle>unmute[xmlns="urn:xmpp:jingle:apps:rtp:info:1"]').attr('name');
172
-                        $(document).trigger('unmute.jingle', [sess.sid, affected]);
136
+                        reasonCondition
137
+                            = $(iq).find('>jingle>reason>:first')[0].tagName;
138
+                        reasonText = $(iq).find('>jingle>reason>text').text();
173 139
                     }
140
+                    this.terminate(sess.sid, reasonCondition, reasonText);
174 141
                     break;
175 142
                 case 'addsource': // FIXME: proprietary, un-jingleish
176 143
                 case 'source-add': // FIXME: proprietary
@@ -182,24 +149,20 @@ module.exports = function(XMPP, eventEmitter) {
182 149
                     break;
183 150
                 default:
184 151
                     logger.warn('jingle action not implemented', action);
152
+                    ack.attrs({ type: 'error' });
153
+                    ack.c('error', {type: 'cancel'})
154
+                        .c('bad-request',
155
+                            { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })
156
+                        .up();
185 157
                     break;
186 158
             }
159
+            this.connection.send(ack);
187 160
             return true;
188 161
         },
189
-        terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
190
-            if (sid === null || sid === undefined) {
191
-                for (sid in this.sessions) {
192
-                    if (this.sessions[sid].state != 'ended') {
193
-                        this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
194
-                        this.sessions[sid].terminate();
195
-                    }
196
-                    delete this.jid2session[this.sessions[sid].peerjid];
197
-                    delete this.sessions[sid];
198
-                }
199
-            } else if (this.sessions.hasOwnProperty(sid)) {
162
+        terminate: function (sid, reasonCondition, reasonText) {
163
+            if (this.sessions.hasOwnProperty(sid)) {
200 164
                 if (this.sessions[sid].state != 'ended') {
201
-                    this.sessions[sid].sendTerminate(reason || (!this.sessions[sid].active()) ? 'cancel' : null, text);
202
-                    this.sessions[sid].terminate();
165
+                    this.sessions[sid].onTerminated(reasonCondition, reasonText);
203 166
                 }
204 167
                 delete this.jid2session[this.sessions[sid].peerjid];
205 168
                 delete this.sessions[sid];

+ 12
- 0
service/xmpp/XMPPEvents.js 查看文件

@@ -40,6 +40,18 @@ var XMPPEvents = {
40 40
     FOCUS_DISCONNECTED: 'xmpp.focus_disconnected',
41 41
     FOCUS_LEFT: "xmpp.focus_left",
42 42
     GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
43
+    /* Event fired when XMPP error is returned to any request, it is meant to be
44
+     * used to report 'signaling' errors to CallStats
45
+     *
46
+     * {
47
+     *   code: {XMPP error code}
48
+     *   reason: {XMPP error condition}
49
+     *   source = request.tree()
50
+     *   session = {JingleSession instance}
51
+     * }
52
+     */
53
+    JINGLE_ERROR: 'xmpp.jingle_error',
54
+    // Event fired when we have failed to set initial offer
43 55
     JINGLE_FATAL_ERROR: 'xmpp.jingle_fatal_error',
44 56
     // Designates an event indicating that we were kicked from the XMPP MUC.
45 57
     KICKED: "xmpp.kicked",

正在加载...
取消
保存