quickblox 2.24.0-beta.1 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/quickblox.js +99 -80
- package/quickblox.min.js +1 -1
- package/src/modules/chat/qbChat.js +2 -4
- package/src/modules/webrtc/qbRTCPeerConnection.js +95 -74
- package/src/plugins/streamManagement.js +235 -0
- package/src/qbConfig.js +2 -2
|
@@ -2473,10 +2473,8 @@ ChatProxy.prototype = {
|
|
|
2473
2473
|
*
|
|
2474
2474
|
* TODO(2.25.0): make auto-enable SDK-internal — fire on initial connect and
|
|
2475
2475
|
* on relogin after session-expired, skip on Stream-Management reconnect.
|
|
2476
|
-
* Spec & three-state matrix
|
|
2477
|
-
*
|
|
2478
|
-
* backlog-2.25.0-auto-enable-notices.md
|
|
2479
|
-
* jira-stories/_jira-src-06-auto-enable-notices.md
|
|
2476
|
+
* Spec & three-state matrix are tracked in the internal 2.25.0 backlog
|
|
2477
|
+
* (auto-enable notices).
|
|
2480
2478
|
*
|
|
2481
2479
|
* Do not call enableNotices again before the previous callback fires —
|
|
2482
2480
|
* concurrent enable calls are not specified.
|
|
@@ -542,92 +542,110 @@ function _getStats(peer, lastResults, successCallback, errorCallback) {
|
|
|
542
542
|
|
|
543
543
|
peer.getStats(null).then(function (results) {
|
|
544
544
|
results.forEach(function (result) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if (result.bytesReceived && result.type === 'inbound-rtp') {
|
|
548
|
-
item = statistic.remote[result.mediaType];
|
|
549
|
-
item.bitrate = _getBitratePerSecond(result, lastResults, false);
|
|
550
|
-
item.bytesReceived = result.bytesReceived;
|
|
551
|
-
item.packetsReceived = result.packetsReceived;
|
|
552
|
-
item.timestamp = result.timestamp;
|
|
553
|
-
if (result.mediaType === 'video' && result.framerateMean) {
|
|
554
|
-
item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
|
|
555
|
-
}
|
|
556
|
-
} else if (result.bytesSent && result.type === 'outbound-rtp') {
|
|
557
|
-
item = statistic.local[result.mediaType];
|
|
558
|
-
item.bitrate = _getBitratePerSecond(result, lastResults, true);
|
|
559
|
-
item.bytesSent = result.bytesSent;
|
|
560
|
-
item.packetsSent = result.packetsSent;
|
|
561
|
-
item.timestamp = result.timestamp;
|
|
562
|
-
if (result.mediaType === 'video' && result.framerateMean) {
|
|
563
|
-
item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
|
|
564
|
-
}
|
|
565
|
-
} else if (result.type === 'local-candidate') {
|
|
566
|
-
item = statistic.local.candidate;
|
|
567
|
-
if (result.candidateType === 'host' && result.mozLocalTransport === 'udp' && result.transport === 'udp') {
|
|
568
|
-
item.protocol = result.transport;
|
|
569
|
-
item.ip = result.ipAddress;
|
|
570
|
-
item.port = result.portNumber;
|
|
571
|
-
} else if (!Helpers.getVersionFirefox()) {
|
|
572
|
-
item.protocol = result.protocol;
|
|
573
|
-
item.ip = result.ip;
|
|
574
|
-
item.port = result.port;
|
|
575
|
-
}
|
|
576
|
-
} else if (result.type === 'remote-candidate') {
|
|
577
|
-
item = statistic.remote.candidate;
|
|
578
|
-
item.protocol = result.protocol || result.transport;
|
|
579
|
-
item.ip = result.ip || result.ipAddress;
|
|
580
|
-
item.port = result.port || result.portNumber;
|
|
581
|
-
} else if (result.type === 'track' && result.kind === 'video' && !Helpers.getVersionFirefox()) {
|
|
582
|
-
if (result.remoteSource) {
|
|
583
|
-
item = statistic.remote.video;
|
|
584
|
-
item.frameHeight = result.frameHeight;
|
|
585
|
-
item.frameWidth = result.frameWidth;
|
|
586
|
-
item.framesPerSecond = _getFramesPerSecond(result, lastResults, false);
|
|
587
|
-
} else {
|
|
588
|
-
item = statistic.local.video;
|
|
589
|
-
item.frameHeight = result.frameHeight;
|
|
590
|
-
item.frameWidth = result.frameWidth;
|
|
591
|
-
item.framesPerSecond = _getFramesPerSecond(result, lastResults, true);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
545
|
+
_applyStatReport(statistic, result, lastResults);
|
|
594
546
|
});
|
|
595
547
|
successCallback(statistic, results);
|
|
596
548
|
}, errorCallback);
|
|
549
|
+
}
|
|
597
550
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
551
|
+
function _applyStatReport(statistic, result, lastResults) {
|
|
552
|
+
var item;
|
|
553
|
+
// mediaType is deprecated in the W3C webrtc-stats spec and replaced by kind.
|
|
554
|
+
// Safari/WebKit omits mediaType on some reports and provides only kind, so we
|
|
555
|
+
// normalize once and use it for both the statistic lookup and the 'video' check.
|
|
556
|
+
var mediaType = result.mediaType || result.kind;
|
|
557
|
+
|
|
558
|
+
if (result.bytesReceived && result.type === 'inbound-rtp') {
|
|
559
|
+
item = statistic.remote[mediaType];
|
|
560
|
+
|
|
561
|
+
if (item) {
|
|
562
|
+
item.bitrate = _getBitratePerSecond(result, lastResults, false);
|
|
563
|
+
item.bytesReceived = result.bytesReceived;
|
|
564
|
+
item.packetsReceived = result.packetsReceived;
|
|
565
|
+
item.timestamp = result.timestamp;
|
|
566
|
+
if (mediaType === 'video' && result.framerateMean) {
|
|
567
|
+
item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
Helpers.traceWarning('_getStats: skipping inbound-rtp report with unknown mediaType/kind: ' + mediaType);
|
|
571
|
+
}
|
|
572
|
+
} else if (result.bytesSent && result.type === 'outbound-rtp') {
|
|
573
|
+
item = statistic.local[mediaType];
|
|
574
|
+
|
|
575
|
+
if (item) {
|
|
576
|
+
item.bitrate = _getBitratePerSecond(result, lastResults, true);
|
|
577
|
+
item.bytesSent = result.bytesSent;
|
|
578
|
+
item.packetsSent = result.packetsSent;
|
|
579
|
+
item.timestamp = result.timestamp;
|
|
580
|
+
if (mediaType === 'video' && result.framerateMean) {
|
|
581
|
+
item.framesPerSecond = Math.round(result.framerateMean * 10) / 10;
|
|
582
|
+
}
|
|
583
|
+
} else {
|
|
584
|
+
Helpers.traceWarning('_getStats: skipping outbound-rtp report with unknown mediaType/kind: ' + mediaType);
|
|
585
|
+
}
|
|
586
|
+
} else if (result.type === 'local-candidate') {
|
|
587
|
+
item = statistic.local.candidate;
|
|
588
|
+
if (result.candidateType === 'host' && result.mozLocalTransport === 'udp' && result.transport === 'udp') {
|
|
589
|
+
item.protocol = result.transport;
|
|
590
|
+
item.ip = result.ipAddress;
|
|
591
|
+
item.port = result.portNumber;
|
|
592
|
+
} else if (!Helpers.getVersionFirefox()) {
|
|
593
|
+
item.protocol = result.protocol;
|
|
594
|
+
item.ip = result.ip;
|
|
595
|
+
item.port = result.port;
|
|
596
|
+
}
|
|
597
|
+
} else if (result.type === 'remote-candidate') {
|
|
598
|
+
item = statistic.remote.candidate;
|
|
599
|
+
item.protocol = result.protocol || result.transport;
|
|
600
|
+
item.ip = result.ip || result.ipAddress;
|
|
601
|
+
item.port = result.port || result.portNumber;
|
|
602
|
+
} else if (result.type === 'track' && result.kind === 'video' && !Helpers.getVersionFirefox()) {
|
|
603
|
+
if (result.remoteSource) {
|
|
604
|
+
item = statistic.remote.video;
|
|
605
|
+
item.frameHeight = result.frameHeight;
|
|
606
|
+
item.frameWidth = result.frameWidth;
|
|
607
|
+
item.framesPerSecond = _getFramesPerSecond(result, lastResults, false);
|
|
609
608
|
} else {
|
|
610
|
-
|
|
609
|
+
item = statistic.local.video;
|
|
610
|
+
item.frameHeight = result.frameHeight;
|
|
611
|
+
item.frameWidth = result.frameWidth;
|
|
612
|
+
item.framesPerSecond = _getFramesPerSecond(result, lastResults, true);
|
|
611
613
|
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
612
616
|
|
|
613
|
-
|
|
617
|
+
function _getBitratePerSecond(result, lastResults, isLocal) {
|
|
618
|
+
var lastResult = lastResults && lastResults.get(result.id),
|
|
619
|
+
seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
|
|
620
|
+
kilo = 1024,
|
|
621
|
+
bit = 8,
|
|
622
|
+
bitrate;
|
|
623
|
+
|
|
624
|
+
if (!lastResult) {
|
|
625
|
+
bitrate = 0;
|
|
626
|
+
} else if (isLocal) {
|
|
627
|
+
bitrate = bit * (result.bytesSent - lastResult.bytesSent) / (kilo * seconds);
|
|
628
|
+
} else {
|
|
629
|
+
bitrate = bit * (result.bytesReceived - lastResult.bytesReceived) / (kilo * seconds);
|
|
614
630
|
}
|
|
615
631
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
|
|
619
|
-
framesPerSecond;
|
|
632
|
+
return Math.round(bitrate);
|
|
633
|
+
}
|
|
620
634
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
} else {
|
|
626
|
-
framesPerSecond = (result.framesReceived - lastResult.framesReceived) / seconds;
|
|
627
|
-
}
|
|
635
|
+
function _getFramesPerSecond(result, lastResults, isLocal) {
|
|
636
|
+
var lastResult = lastResults && lastResults.get(result.id),
|
|
637
|
+
seconds = lastResult ? ((result.timestamp - lastResult.timestamp) / 1000) : 5,
|
|
638
|
+
framesPerSecond;
|
|
628
639
|
|
|
629
|
-
|
|
640
|
+
if (!lastResult) {
|
|
641
|
+
framesPerSecond = 0;
|
|
642
|
+
} else if (isLocal) {
|
|
643
|
+
framesPerSecond = (result.framesSent - lastResult.framesSent) / seconds;
|
|
644
|
+
} else {
|
|
645
|
+
framesPerSecond = (result.framesReceived - lastResult.framesReceived) / seconds;
|
|
630
646
|
}
|
|
647
|
+
|
|
648
|
+
return Math.round(framesPerSecond * 10) / 10;
|
|
631
649
|
}
|
|
632
650
|
|
|
633
651
|
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
|
|
@@ -814,4 +832,7 @@ function setMediaBitrate(sdp, media, bitrate) {
|
|
|
814
832
|
return newLines.join('\n');
|
|
815
833
|
}
|
|
816
834
|
|
|
835
|
+
// PRIVATE - exposed for unit tests only, not part of the public SDK contract.
|
|
836
|
+
qbRTCPeerConnection._applyStatReport = _applyStatReport;
|
|
837
|
+
|
|
817
838
|
module.exports = qbRTCPeerConnection;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QuickBlox JavaScript SDK
|
|
5
|
+
* Chat Stream Management plugin
|
|
6
|
+
* doc: http://xmpp.org/extensions/xep-0198.html
|
|
7
|
+
*
|
|
8
|
+
* To enable this features add to config
|
|
9
|
+
* ```javascript
|
|
10
|
+
* streamManagement: {
|
|
11
|
+
- enable: true
|
|
12
|
+
- }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Uses listener by QB.chat.onSentMessageCallback
|
|
16
|
+
*
|
|
17
|
+
* ```javascript
|
|
18
|
+
* QB.chat.onSentMessageCallback = function (messageLost, messageSent) {
|
|
19
|
+
* if (messageLost) {
|
|
20
|
+
* console.error('sendErrorCallback', messageLost);
|
|
21
|
+
* } else {
|
|
22
|
+
* console.info('sendMessageSuccessCallback', messageSent);
|
|
23
|
+
* }
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** JSHint inline rules */
|
|
29
|
+
/* globals $build */
|
|
30
|
+
|
|
31
|
+
var Utils = require('../qbUtils'),
|
|
32
|
+
chatUtils = require('../modules/chat/qbChatHelpers');
|
|
33
|
+
|
|
34
|
+
function StreamManagement(options) {
|
|
35
|
+
|
|
36
|
+
this._NS = 'urn:xmpp:sm:3';
|
|
37
|
+
|
|
38
|
+
this._isStreamManagementEnabled = false;
|
|
39
|
+
|
|
40
|
+
// Counter of the incoming stanzas
|
|
41
|
+
this._clientProcessedStanzasCounter = null;
|
|
42
|
+
|
|
43
|
+
// The client send stanza counter.
|
|
44
|
+
this._clientSentStanzasCounter = null;
|
|
45
|
+
|
|
46
|
+
this._intervalId = null;
|
|
47
|
+
|
|
48
|
+
this._timeInterval = Utils.getTimeIntervalForCallBackMessage();
|
|
49
|
+
|
|
50
|
+
this.sentMessageCallback = null;
|
|
51
|
+
|
|
52
|
+
if(Utils.getEnv().browser){
|
|
53
|
+
this._parser = new DOMParser();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// connection
|
|
57
|
+
this._c = null;
|
|
58
|
+
|
|
59
|
+
this._nodeBuilder = null;
|
|
60
|
+
// Original connection.send method
|
|
61
|
+
this._originalSend = null;
|
|
62
|
+
|
|
63
|
+
// In progress stanzas queue
|
|
64
|
+
this._stanzasQueue = [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
StreamManagement.prototype.enable = function (connection, client) {
|
|
69
|
+
var self = this,
|
|
70
|
+
stanza,
|
|
71
|
+
enableParams = {
|
|
72
|
+
xmlns: self._NS
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if(!self._isStreamManagementEnabled){
|
|
76
|
+
self._c = connection;
|
|
77
|
+
self._originalSend = this._c.send;
|
|
78
|
+
self._c.send = this.send.bind(self);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if(Utils.getEnv().browser){
|
|
82
|
+
this._clientProcessedStanzasCounter = null;
|
|
83
|
+
this._clientSentStanzasCounter = null;
|
|
84
|
+
self._addEnableHandlers();
|
|
85
|
+
stanza = $build('enable', enableParams);
|
|
86
|
+
} else {
|
|
87
|
+
self._nodeBuilder = client.Stanza;
|
|
88
|
+
self._addEnableHandlers();
|
|
89
|
+
stanza = chatUtils.createStanza(self._nodeBuilder, enableParams, 'enable');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
self._c.send(stanza);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
StreamManagement.prototype._timeoutCallback = function () {
|
|
96
|
+
var self = this,
|
|
97
|
+
now = Date.now(),
|
|
98
|
+
updatedStanzasQueue = [];
|
|
99
|
+
|
|
100
|
+
if(self._stanzasQueue.length){
|
|
101
|
+
for(var i = 0; i < self._stanzasQueue.length; i++){
|
|
102
|
+
if(self._stanzasQueue[i] && self._stanzasQueue[i].time < now){
|
|
103
|
+
self.sentMessageCallback(self._stanzasQueue[i].message);
|
|
104
|
+
} else {
|
|
105
|
+
updatedStanzasQueue.push(self._stanzasQueue[i]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
self._stanzasQueue = updatedStanzasQueue;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
StreamManagement.prototype._addEnableHandlers = function () {
|
|
114
|
+
var self = this;
|
|
115
|
+
|
|
116
|
+
if (Utils.getEnv().browser) {
|
|
117
|
+
self._c.XAddTrackedHandler(_incomingStanzaHandler.bind(self));
|
|
118
|
+
} else {
|
|
119
|
+
self._c.on('stanza', _incomingStanzaHandler.bind(self));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _incomingStanzaHandler (stanza){
|
|
123
|
+
/*
|
|
124
|
+
* Getting incoming stanza tagName
|
|
125
|
+
* */
|
|
126
|
+
|
|
127
|
+
var tagName = stanza.name || stanza.tagName || stanza.nodeTree.tagName;
|
|
128
|
+
|
|
129
|
+
if(tagName === 'enabled'){
|
|
130
|
+
self._isStreamManagementEnabled = true;
|
|
131
|
+
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (self._isStreamManagementEnabled && tagName === 'message') {
|
|
136
|
+
clearInterval(self._intervalId);
|
|
137
|
+
self._intervalId = setInterval(self._timeoutCallback.bind(self), self._timeInterval);
|
|
138
|
+
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if(chatUtils.getAttr(stanza, 'xmlns') !== self._NS){
|
|
143
|
+
self._increaseReceivedStanzasCounter();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if(tagName === 'r'){
|
|
147
|
+
var params = {
|
|
148
|
+
xmlns: self._NS,
|
|
149
|
+
h: self._clientProcessedStanzasCounter
|
|
150
|
+
},
|
|
151
|
+
answerStanza = Utils.getEnv().browser ? $build('a', params) :
|
|
152
|
+
chatUtils.createStanza(self._nodeBuilder, params, 'a');
|
|
153
|
+
|
|
154
|
+
self._originalSend.call(self._c, answerStanza);
|
|
155
|
+
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if(tagName === 'a'){
|
|
160
|
+
var h = parseInt(chatUtils.getAttr(stanza, 'h'));
|
|
161
|
+
|
|
162
|
+
self._checkCounterOnIncomeStanza(h);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
StreamManagement.prototype.send = function (stanza, message) {
|
|
170
|
+
var self = this,
|
|
171
|
+
stanzaXML = stanza.nodeTree ? this._parser.parseFromString(stanza.nodeTree.outerHTML, "application/xml").childNodes[0] : stanza,
|
|
172
|
+
tagName = stanzaXML.name || stanzaXML.tagName || stanzaXML.nodeTree.tagName,
|
|
173
|
+
type = chatUtils.getAttr(stanzaXML, 'type'),
|
|
174
|
+
bodyContent = chatUtils.getElementText(stanzaXML, 'body') || '',
|
|
175
|
+
attachments = chatUtils.getAllElements(stanzaXML, 'attachment') || '';
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
self._originalSend.call(self._c, stanza);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
Utils.QBLog('[QBChat]', e.message);
|
|
181
|
+
} finally {
|
|
182
|
+
if (tagName === 'message' && (type === 'chat' || type === 'groupchat') && (bodyContent || attachments.length)) {
|
|
183
|
+
self._sendStanzasRequest({
|
|
184
|
+
message: message,
|
|
185
|
+
time: Date.now() + self._timeInterval,
|
|
186
|
+
expect: self._clientSentStanzasCounter
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
self._clientSentStanzasCounter++;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
StreamManagement.prototype._sendStanzasRequest = function (data) {
|
|
196
|
+
var self = this;
|
|
197
|
+
|
|
198
|
+
if(self._isStreamManagementEnabled){
|
|
199
|
+
self._stanzasQueue.push(data);
|
|
200
|
+
|
|
201
|
+
var stanza = Utils.getEnv().browser ? $build('r', { xmlns: self._NS }) :
|
|
202
|
+
chatUtils.createStanza(self._nodeBuilder, { xmlns: self._NS }, 'r');
|
|
203
|
+
|
|
204
|
+
if (self._c.connected) {
|
|
205
|
+
self._originalSend.call(self._c, stanza);
|
|
206
|
+
} else {
|
|
207
|
+
self._checkCounterOnIncomeStanza();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
StreamManagement.prototype.getClientSentStanzasCounter = function(){
|
|
213
|
+
return this._clientSentStanzasCounter;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
StreamManagement.prototype._checkCounterOnIncomeStanza = function (count){
|
|
217
|
+
var updatedStanzasQueue = [];
|
|
218
|
+
|
|
219
|
+
if(this._stanzasQueue.length){
|
|
220
|
+
for(var i = 0; i < this._stanzasQueue.length; i++){
|
|
221
|
+
if(this._stanzasQueue[i].expect == count){
|
|
222
|
+
this.sentMessageCallback(null, this._stanzasQueue[i].message);
|
|
223
|
+
} else {
|
|
224
|
+
updatedStanzasQueue.push(this._stanzasQueue[i]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
this._stanzasQueue = updatedStanzasQueue;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
StreamManagement.prototype._increaseReceivedStanzasCounter = function(){
|
|
232
|
+
this._clientProcessedStanzasCounter++;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
module.exports = StreamManagement;
|