@flashphoner/websdk 2.0.260 → 2.0.262
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/docTemplate/README.md +1 -1
- package/flashphoner-no-flash.js +771 -81
- package/flashphoner-no-flash.min.js +2 -2
- package/flashphoner-no-webrtc.js +669 -9
- package/flashphoner-no-webrtc.min.js +2 -2
- package/flashphoner-no-wsplayer.js +770 -80
- package/flashphoner-no-wsplayer.min.js +2 -2
- package/flashphoner-room-api-webrtc-only.js +771 -81
- package/flashphoner-room-api-webrtc-only.min.js +1 -1
- package/flashphoner-room-api.js +519 -60
- package/flashphoner-room-api.min.js +2 -2
- package/flashphoner-temasys-flash-websocket-without-adapterjs.js +670 -10
- package/flashphoner-temasys-flash-websocket.js +670 -10
- package/flashphoner-temasys-flash-websocket.min.js +1 -1
- package/flashphoner-webrtc-only.js +770 -80
- package/flashphoner-webrtc-only.min.js +1 -1
- package/flashphoner.js +771 -81
- package/flashphoner.min.js +2 -2
- package/package.json +1 -1
- package/src/constants.d.ts +6 -0
- package/src/constants.js +6 -0
- package/src/flashphoner-core.js +60 -5
- package/src/stats-collector.js +321 -0
- package/src/util.js +61 -1
- package/src/webrtc-media-provider.js +64 -48
package/package.json
CHANGED
package/src/constants.d.ts
CHANGED
|
@@ -645,6 +645,12 @@ export const ERROR_INFO: Readonly<{
|
|
|
645
645
|
* @memberOf Flashphoner.constants.ERROR_INFO
|
|
646
646
|
*/
|
|
647
647
|
CAN_NOT_SET_RESOLUTION: string;
|
|
648
|
+
/**
|
|
649
|
+
* Error if cannot get peer connection stats
|
|
650
|
+
* @event CAN_NOT_GET_STATS
|
|
651
|
+
* @memberOf Flashphoner.constants.ERROR_INFO
|
|
652
|
+
*/
|
|
653
|
+
CAN_NOT_GET_STATS: string;
|
|
648
654
|
/**
|
|
649
655
|
* Local browser error detected
|
|
650
656
|
* @event LOCAL_ERROR
|
package/src/constants.js
CHANGED
|
@@ -654,6 +654,12 @@ const ERROR_INFO = Object.freeze({
|
|
|
654
654
|
* @memberOf Flashphoner.constants.ERROR_INFO
|
|
655
655
|
*/
|
|
656
656
|
CAN_NOT_SET_RESOLUTION: 'Cannot switch a published stream resolution',
|
|
657
|
+
/**
|
|
658
|
+
* Error if cannot get peer connection stats
|
|
659
|
+
* @event CAN_NOT_GET_STATS
|
|
660
|
+
* @memberOf Flashphoner.constants.ERROR_INFO
|
|
661
|
+
*/
|
|
662
|
+
CAN_NOT_GET_STATS: 'Cannot get PeerConnection stats',
|
|
657
663
|
/**
|
|
658
664
|
* Local browser error detected
|
|
659
665
|
* @event LOCAL_ERROR
|
package/src/flashphoner-core.js
CHANGED
|
@@ -5,6 +5,7 @@ const constants = require("./constants");
|
|
|
5
5
|
const util = require('./util');
|
|
6
6
|
const LoggerObject = require('./util').logger;
|
|
7
7
|
const clientInfo = require('./client-info');
|
|
8
|
+
const StatsCollector = require('./stats-collector');
|
|
8
9
|
const Promise = require('promise-polyfill');
|
|
9
10
|
const KalmanFilter = require('kalmanjs');
|
|
10
11
|
const browserDetails = require('webrtc-adapter').default.browserDetails;
|
|
@@ -519,6 +520,9 @@ var createSession = function (options) {
|
|
|
519
520
|
|
|
520
521
|
var wsConnection;
|
|
521
522
|
|
|
523
|
+
// WebRTC metrics sending description
|
|
524
|
+
let webRTCMetricsServerDescription;
|
|
525
|
+
|
|
522
526
|
if (lbUrl) {
|
|
523
527
|
requestURL(lbUrl);
|
|
524
528
|
} else {
|
|
@@ -621,6 +625,7 @@ var createSession = function (options) {
|
|
|
621
625
|
case 'getUserData':
|
|
622
626
|
authToken = obj.authToken;
|
|
623
627
|
cConfig = obj;
|
|
628
|
+
webRTCMetricsServerDescription = obj.webRTCMetricsServerDescription;
|
|
624
629
|
onSessionStatusChange(SESSION_STATUS.ESTABLISHED, obj);
|
|
625
630
|
break;
|
|
626
631
|
case 'setRemoteSDP':
|
|
@@ -725,6 +730,34 @@ var createSession = function (options) {
|
|
|
725
730
|
streamRefreshHandlers[obj.mediaSessionId](obj);
|
|
726
731
|
}
|
|
727
732
|
break;
|
|
733
|
+
case `webRTCMetricsDescriptionUpdate`:
|
|
734
|
+
if (obj.ids) {
|
|
735
|
+
obj.ids.forEach((id) => {
|
|
736
|
+
if (streamRefreshHandlers[id]) {
|
|
737
|
+
streamRefreshHandlers[id](obj);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
} else {
|
|
741
|
+
if (obj.compression) {
|
|
742
|
+
webRTCMetricsServerDescription.compression = obj.compression;
|
|
743
|
+
}
|
|
744
|
+
if (obj.batchSize) {
|
|
745
|
+
webRTCMetricsServerDescription.batchSize = obj.batchSize;
|
|
746
|
+
}
|
|
747
|
+
if (obj.sampling) {
|
|
748
|
+
webRTCMetricsServerDescription.sampling = obj.sampling;
|
|
749
|
+
}
|
|
750
|
+
if (obj.types) {
|
|
751
|
+
webRTCMetricsServerDescription.types = obj.types;
|
|
752
|
+
}
|
|
753
|
+
if (obj.collect) {
|
|
754
|
+
webRTCMetricsServerDescription.collect = obj.collect;
|
|
755
|
+
}
|
|
756
|
+
for (const [id, handler] of Object.entries(streamRefreshHandlers)) {
|
|
757
|
+
handler(obj);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
break;
|
|
728
761
|
default:
|
|
729
762
|
logger.info(LOG_PREFIX, "Unknown server message " + data.message);
|
|
730
763
|
}
|
|
@@ -736,10 +769,12 @@ var createSession = function (options) {
|
|
|
736
769
|
|
|
737
770
|
//WebSocket send helper
|
|
738
771
|
function send(message, data) {
|
|
739
|
-
wsConnection.
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
772
|
+
if (wsConnection.readyState === WebSocket.OPEN) {
|
|
773
|
+
wsConnection.send(JSON.stringify({
|
|
774
|
+
message: message,
|
|
775
|
+
data: [data]
|
|
776
|
+
}));
|
|
777
|
+
}
|
|
743
778
|
}
|
|
744
779
|
|
|
745
780
|
//Session status update helper
|
|
@@ -1773,6 +1808,8 @@ var createSession = function (options) {
|
|
|
1773
1808
|
|
|
1774
1809
|
var videoBytes = 0;
|
|
1775
1810
|
|
|
1811
|
+
var statsCollector = null;
|
|
1812
|
+
|
|
1776
1813
|
/**
|
|
1777
1814
|
* Represents media stream.
|
|
1778
1815
|
*
|
|
@@ -1807,7 +1844,7 @@ var createSession = function (options) {
|
|
|
1807
1844
|
return;
|
|
1808
1845
|
}
|
|
1809
1846
|
|
|
1810
|
-
if (streamInfo.available
|
|
1847
|
+
if (streamInfo.available !== undefined) {
|
|
1811
1848
|
for (var i = 0; i < availableCallbacks.length; i++) {
|
|
1812
1849
|
info_ = streamInfo.reason;
|
|
1813
1850
|
if (streamInfo.available == "true") {
|
|
@@ -1866,10 +1903,28 @@ var createSession = function (options) {
|
|
|
1866
1903
|
if (mediaConnection) {
|
|
1867
1904
|
mediaConnection.close(cacheLocalResources);
|
|
1868
1905
|
}
|
|
1906
|
+
if (statsCollector) {
|
|
1907
|
+
statsCollector.stop();
|
|
1908
|
+
statsCollector = null;
|
|
1909
|
+
}
|
|
1869
1910
|
}
|
|
1870
1911
|
if (record_ && typeof streamInfo.recordName !== 'undefined') {
|
|
1871
1912
|
recordFileName = streamInfo.recordName;
|
|
1872
1913
|
}
|
|
1914
|
+
|
|
1915
|
+
// Set up metrics collection
|
|
1916
|
+
if (event === STREAM_STATUS.PUBLISHING || event === STREAM_STATUS.PLAYING) {
|
|
1917
|
+
if (webRTCMetricsServerDescription && !statsCollector) {
|
|
1918
|
+
statsCollector = StatsCollector.StreamStatsCollector(webRTCMetricsServerDescription, id_, mediaConnection, wsConnection, logger);
|
|
1919
|
+
statsCollector.start();
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// Pause or resume metrics collection
|
|
1924
|
+
if (!streamInfo.status && streamInfo.collect !== undefined && statsCollector) {
|
|
1925
|
+
statsCollector.update(streamInfo);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1873
1928
|
//fire stream event
|
|
1874
1929
|
if (callbacks[event]) {
|
|
1875
1930
|
callbacks[event](stream);
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const util = require('./util');
|
|
4
|
+
const LOG_PREFIX = "stats-collector";
|
|
5
|
+
|
|
6
|
+
// Collect and send WebRTC statistics periodically
|
|
7
|
+
const StreamStatsCollector = function(description, id, mediaConnection, wsConnection, logger) {
|
|
8
|
+
let statCollector = {
|
|
9
|
+
description: description,
|
|
10
|
+
id: id,
|
|
11
|
+
mediaConnection: mediaConnection,
|
|
12
|
+
wsConnection: wsConnection,
|
|
13
|
+
logger: getLogger(logger),
|
|
14
|
+
headers: "",
|
|
15
|
+
compression: "none",
|
|
16
|
+
metricsBatch: null,
|
|
17
|
+
timer: null,
|
|
18
|
+
batchCount: 0,
|
|
19
|
+
start: async function() {
|
|
20
|
+
let error = "Can't collect WebRTC stats to send: ";
|
|
21
|
+
if (!statCollector.description.types) {
|
|
22
|
+
throw new Error(error + "no report types defined");
|
|
23
|
+
}
|
|
24
|
+
if (!statCollector.description.sampling) {
|
|
25
|
+
throw new Error(error + "no sampling interval defined");
|
|
26
|
+
}
|
|
27
|
+
if (!statCollector.description.batchSize) {
|
|
28
|
+
throw new Error(error + "no metrics batch size defined");
|
|
29
|
+
}
|
|
30
|
+
if (!statCollector.mediaConnection) {
|
|
31
|
+
throw new Error(error + "no media connection available");
|
|
32
|
+
}
|
|
33
|
+
if (!statCollector.wsConnection) {
|
|
34
|
+
throw new Error(error + "no websocket connection available");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await statCollector.updateHeaders();
|
|
38
|
+
await statCollector.updateCompression();
|
|
39
|
+
statCollector.sendHeaders();
|
|
40
|
+
if (statCollector.description.collect === "on") {
|
|
41
|
+
statCollector.collect(true);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
collect: function(enable) {
|
|
45
|
+
if (enable) {
|
|
46
|
+
statCollector.startTimer();
|
|
47
|
+
} else {
|
|
48
|
+
statCollector.stopTimer();
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
stop: function() {
|
|
52
|
+
statCollector.stopTimer();
|
|
53
|
+
statCollector.headers = "";
|
|
54
|
+
},
|
|
55
|
+
update: async function(description) {
|
|
56
|
+
if (!description) {
|
|
57
|
+
if (statCollector.logger) {
|
|
58
|
+
statCollector.logger.error(LOG_PREFIX, "Can't update WebRTC metrics sending: no parameters passed");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (description.types || description.compression) {
|
|
63
|
+
statCollector.stop();
|
|
64
|
+
if (description.types) {
|
|
65
|
+
statCollector.description.types = description.types;
|
|
66
|
+
await statCollector.updateHeaders();
|
|
67
|
+
}
|
|
68
|
+
if (description.compression) {
|
|
69
|
+
statCollector.description.compression = description.compression;
|
|
70
|
+
await statCollector.updateCompression();
|
|
71
|
+
}
|
|
72
|
+
statCollector.sendHeaders();
|
|
73
|
+
} else {
|
|
74
|
+
statCollector.collect(false);
|
|
75
|
+
}
|
|
76
|
+
if (description.batchSize) {
|
|
77
|
+
statCollector.description.batchSize = description.batchSize;
|
|
78
|
+
}
|
|
79
|
+
if (description.sampling) {
|
|
80
|
+
statCollector.description.sampling = description.sampling;
|
|
81
|
+
}
|
|
82
|
+
if (description.collect) {
|
|
83
|
+
statCollector.description.collect = description.collect;
|
|
84
|
+
}
|
|
85
|
+
switch(statCollector.description.collect) {
|
|
86
|
+
case "on":
|
|
87
|
+
statCollector.collect(true);
|
|
88
|
+
break;
|
|
89
|
+
case "off":
|
|
90
|
+
statCollector.collect(false);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
updateHeaders: async function() {
|
|
95
|
+
let stats = await statCollector.mediaConnection.getWebRTCStats();
|
|
96
|
+
Object.keys(statCollector.description.types).forEach((type) => {
|
|
97
|
+
let typeDescriptor = statCollector.description.types[type];
|
|
98
|
+
let metricsString = "";
|
|
99
|
+
let contentFilters = null;
|
|
100
|
+
if (typeDescriptor.metrics) {
|
|
101
|
+
metricsString = typeDescriptor.metrics;
|
|
102
|
+
}
|
|
103
|
+
if (typeDescriptor.contains) {
|
|
104
|
+
contentFilters = typeDescriptor.contains;
|
|
105
|
+
}
|
|
106
|
+
if (stats[type]) {
|
|
107
|
+
stats[type].forEach((report) => {
|
|
108
|
+
statCollector.logger.debug(LOG_PREFIX, type + " report: " + JSON.stringify(report));
|
|
109
|
+
if (contentFilters) {
|
|
110
|
+
let filtersMatched = true;
|
|
111
|
+
for (const filter in contentFilters) {
|
|
112
|
+
statCollector.logger.debug(LOG_PREFIX, type + " filter by " + filter + ": " + JSON.stringify(contentFilters[filter]));
|
|
113
|
+
let filterMatched = false;
|
|
114
|
+
if (report[filter]) {
|
|
115
|
+
for (const value of contentFilters[filter]) {
|
|
116
|
+
statCollector.logger.debug(LOG_PREFIX, filter + ": " + value + " <> " + report[filter]);
|
|
117
|
+
if (report[filter] === value) {
|
|
118
|
+
filterMatched = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
filtersMatched = filtersMatched && filterMatched;
|
|
124
|
+
if (!filterMatched) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (filtersMatched) {
|
|
129
|
+
statCollector.addHeaders(report, metricsString);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
statCollector.addHeaders(report, metricsString);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
statCollector.logger.warn(LOG_PREFIX, "No report type found in RTC stats: '" + type + "'");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
addHeaders: function(report, metricsString) {
|
|
141
|
+
if (metricsString) {
|
|
142
|
+
let metrics = metricsString.split(",");
|
|
143
|
+
metrics.forEach((metric) => {
|
|
144
|
+
let metricFound = false;
|
|
145
|
+
for (const key of Object.keys(report)) {
|
|
146
|
+
if (metric === key) {
|
|
147
|
+
statCollector.headers = util.addFieldToCsvString(statCollector.headers, report.type + "." + report.id + "." + metric, ",");
|
|
148
|
+
metricFound = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!metricFound) {
|
|
153
|
+
statCollector.logger.warn(LOG_PREFIX, "No metric found in RTC stats report '" + report.type + "': '" + metric + "'");
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
updateCompression: async function() {
|
|
159
|
+
if (statCollector.description.compression) {
|
|
160
|
+
if (statCollector.description.compression.indexOf("gzip") >= 0) {
|
|
161
|
+
await statCollector.checkForCompression("gzip");
|
|
162
|
+
} else if (statCollector.description.compression.indexOf("deflate") >= 0) {
|
|
163
|
+
await statCollector.checkForCompression("deflate");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
checkForCompression: async function(compression) {
|
|
168
|
+
try {
|
|
169
|
+
await util.compress(compression, "test", false);
|
|
170
|
+
statCollector.compression = compression;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
statCollector.logger.warn(LOG_PREFIX, "Can't compress metrics data using " + compression + ": " + e);
|
|
173
|
+
statCollector.compression = "none";
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
sendHeaders: function() {
|
|
177
|
+
let data = {
|
|
178
|
+
mediaSessionId: statCollector.id,
|
|
179
|
+
compression: statCollector.compression,
|
|
180
|
+
headers: statCollector.headers
|
|
181
|
+
};
|
|
182
|
+
statCollector.send("webRTCMetricsClientDescription", data);
|
|
183
|
+
},
|
|
184
|
+
send: function(message, data) {
|
|
185
|
+
statCollector.logger.debug(LOG_PREFIX, data);
|
|
186
|
+
if (statCollector.wsConnection.readyState === WebSocket.OPEN) {
|
|
187
|
+
statCollector.wsConnection.send(JSON.stringify({
|
|
188
|
+
message: message,
|
|
189
|
+
data: [data]
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
startTimer: function() {
|
|
194
|
+
if (!statCollector.timer && statCollector.headers) {
|
|
195
|
+
statCollector.batchCount = statCollector.description.batchSize;
|
|
196
|
+
statCollector.timer = setInterval(statCollector.collectMetrics, statCollector.description.sampling);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
stopTimer: function() {
|
|
200
|
+
if (statCollector.timer) {
|
|
201
|
+
clearInterval(statCollector.timer);
|
|
202
|
+
statCollector.timer = null;
|
|
203
|
+
statCollector.metricsBatch = null;
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
collectMetrics: async function() {
|
|
207
|
+
if (statCollector.timer) {
|
|
208
|
+
let stats = await statCollector.mediaConnection.getWebRTCStats();
|
|
209
|
+
|
|
210
|
+
if (!statCollector.metricsBatch) {
|
|
211
|
+
statCollector.metricsBatch = [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let metrics = [];
|
|
215
|
+
statCollector.headers.split(",").forEach((header) => {
|
|
216
|
+
let components = header.split(".");
|
|
217
|
+
let descriptor = {
|
|
218
|
+
type: components[0],
|
|
219
|
+
id: components[1],
|
|
220
|
+
name: components[2]
|
|
221
|
+
}
|
|
222
|
+
let value = "undefined";
|
|
223
|
+
|
|
224
|
+
if (stats[descriptor.type]) {
|
|
225
|
+
for (const report of stats[descriptor.type]) {
|
|
226
|
+
if (report.id === descriptor.id) {
|
|
227
|
+
value = report[descriptor.name];
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
metrics.push(value);
|
|
233
|
+
});
|
|
234
|
+
statCollector.metricsBatch.push(metrics);
|
|
235
|
+
statCollector.batchCount--;
|
|
236
|
+
if (statCollector.batchCount === 0) {
|
|
237
|
+
await statCollector.sendMetrics();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
sendMetrics: async function() {
|
|
242
|
+
let previous;
|
|
243
|
+
let metricsToSend = [];
|
|
244
|
+
let metricsData;
|
|
245
|
+
|
|
246
|
+
for (let i = 0; i < statCollector.metricsBatch.length; i++) {
|
|
247
|
+
let metricsString = "";
|
|
248
|
+
for (let j = 0; j < statCollector.metricsBatch[i].length; j++) {
|
|
249
|
+
let valueString = valueToString(statCollector.metricsBatch[i][j]);
|
|
250
|
+
let previousString = "";
|
|
251
|
+
let delimiter = ";";
|
|
252
|
+
if (previous) {
|
|
253
|
+
previousString = valueToString(previous[j]);
|
|
254
|
+
}
|
|
255
|
+
if (valueString === previousString) {
|
|
256
|
+
valueString = "";
|
|
257
|
+
}
|
|
258
|
+
metricsString = util.addFieldToCsvString(metricsString, valueString, delimiter);
|
|
259
|
+
if (j > 0 && metricsString === "") {
|
|
260
|
+
metricsString = delimiter;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
previous = statCollector.metricsBatch[i];
|
|
264
|
+
metricsToSend.push(metricsString);
|
|
265
|
+
}
|
|
266
|
+
if (statCollector.compression !== "none") {
|
|
267
|
+
try {
|
|
268
|
+
metricsData = await util.compress(statCollector.compression, JSON.stringify(metricsToSend), true);
|
|
269
|
+
} catch(e) {
|
|
270
|
+
statCollector.logger.warn(LOG_PREFIX, "Can't send metrics data using" + statCollector.compression + ": " + e);
|
|
271
|
+
metricsData = null;
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
metricsData = metricsToSend;
|
|
275
|
+
}
|
|
276
|
+
if (metricsData) {
|
|
277
|
+
let data = {
|
|
278
|
+
mediaSessionId: statCollector.id,
|
|
279
|
+
metrics: metricsData
|
|
280
|
+
};
|
|
281
|
+
statCollector.send("webRTCMetricsBatch", data);
|
|
282
|
+
}
|
|
283
|
+
statCollector.metricsBatch = null;
|
|
284
|
+
statCollector.batchCount = statCollector.description.batchSize;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return statCollector;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Helper function to stringify a value
|
|
291
|
+
const valueToString = function(value) {
|
|
292
|
+
let valueString = "undefined";
|
|
293
|
+
if (typeof value === "object") {
|
|
294
|
+
valueString = JSON.stringify(value);
|
|
295
|
+
} else {
|
|
296
|
+
valueString = value.toString();
|
|
297
|
+
}
|
|
298
|
+
return valueString;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Helper function to get logger object
|
|
302
|
+
const getLogger = function(logger) {
|
|
303
|
+
if (logger) {
|
|
304
|
+
if (logger.info !== undefined &&
|
|
305
|
+
logger.warn !== undefined &&
|
|
306
|
+
logger.error !== undefined &&
|
|
307
|
+
logger.debug !== undefined) {
|
|
308
|
+
return logger;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
info: function() {},
|
|
313
|
+
warn: function() {},
|
|
314
|
+
error: function() {},
|
|
315
|
+
debug: function() {}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
module.exports = {
|
|
320
|
+
StreamStatsCollector: StreamStatsCollector
|
|
321
|
+
}
|
package/src/util.js
CHANGED
|
@@ -490,6 +490,64 @@ const setPublishingBitrate = function(sdp, mediaConnection, minBitrate, maxBitra
|
|
|
490
490
|
return sdp;
|
|
491
491
|
};
|
|
492
492
|
|
|
493
|
+
const addFieldToCsvString = function(csvString, field, delimiter) {
|
|
494
|
+
if (field !== "" && field.indexOf(delimiter) >= 0 ) {
|
|
495
|
+
field = '"' + field + '"';
|
|
496
|
+
}
|
|
497
|
+
if (csvString === "") {
|
|
498
|
+
csvString = field;
|
|
499
|
+
} else {
|
|
500
|
+
csvString = csvString + delimiter + field;
|
|
501
|
+
}
|
|
502
|
+
return csvString;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const compress = async function(compression, data, base64) {
|
|
506
|
+
// Throw exception if CompessionStream is not available
|
|
507
|
+
if (typeof CompressionStream === "undefined") {
|
|
508
|
+
throw new Error("Compression is not available");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Convert incoming string to a stream
|
|
512
|
+
let stream;
|
|
513
|
+
if(typeof data == "string") {
|
|
514
|
+
stream = new Blob([data], {
|
|
515
|
+
type: 'text/plain',
|
|
516
|
+
}).stream();
|
|
517
|
+
} else {
|
|
518
|
+
// Assume blog
|
|
519
|
+
stream = data.stream();
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// gzip stream
|
|
523
|
+
const compressedReadableStream = stream.pipeThrough(
|
|
524
|
+
new CompressionStream(compression)
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
// create Response
|
|
528
|
+
const compressedResponse = await new Response(compressedReadableStream);
|
|
529
|
+
|
|
530
|
+
// Get response Blob
|
|
531
|
+
const blob = await compressedResponse.blob();
|
|
532
|
+
|
|
533
|
+
if(base64) {
|
|
534
|
+
// Get the ArrayBuffer
|
|
535
|
+
const buffer = await blob.arrayBuffer();
|
|
536
|
+
|
|
537
|
+
// convert ArrayBuffer to base64 encoded string
|
|
538
|
+
const compressedBase64 = btoa(
|
|
539
|
+
String.fromCharCode(
|
|
540
|
+
...new Uint8Array(buffer)
|
|
541
|
+
)
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
return compressedBase64;
|
|
545
|
+
|
|
546
|
+
} else {
|
|
547
|
+
return blob;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
493
551
|
module.exports = {
|
|
494
552
|
isEmptyObject,
|
|
495
553
|
copyObjectToArray,
|
|
@@ -501,5 +559,7 @@ module.exports = {
|
|
|
501
559
|
stripCodecs,
|
|
502
560
|
getCurrentCodecAndSampleRate,
|
|
503
561
|
isPromise,
|
|
504
|
-
setPublishingBitrate
|
|
562
|
+
setPublishingBitrate,
|
|
563
|
+
addFieldToCsvString,
|
|
564
|
+
compress
|
|
505
565
|
};
|