@flashphoner/websdk 2.0.245 → 2.0.247

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flashphoner/websdk",
3
- "version": "2.0.245",
3
+ "version": "2.0.247",
4
4
  "description": "Official Flashphoner WebCallServer WebSDK package",
5
5
  "main": "./src/flashphoner-core.js",
6
6
  "types": "./src/flashphoner-core.d.ts",
@@ -0,0 +1,237 @@
1
+ 'use strict';
2
+
3
+ function getClientHints(navigator) {
4
+ let {userAgent} = navigator;
5
+ let mobile, platform = '', platformVersion = '', architecture = '', bitness = '', model = '', uaFullVersion = '', fullVersionList = [];
6
+ let platformInfo = userAgent;
7
+ let found = false;
8
+ let versionInfo = userAgent.replace(/\(([^)]+)\)?/g, ($0, $1) => {
9
+ if (!found) {
10
+ platformInfo = $1;
11
+ found = true;
12
+ }
13
+ return '';
14
+ });
15
+ let items = versionInfo.match(/(\S+)\/(\S+)/g);
16
+ let webview = false;
17
+ // detect mobile
18
+ mobile = userAgent.indexOf('Mobile') !== -1;
19
+ let m;
20
+ let m2;
21
+ // detect platform
22
+ if ((m = /Windows NT (\d+(\.\d+)*)/.exec(platformInfo)) !== null) {
23
+ platform = 'Windows';
24
+ // see https://docs.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11
25
+ let nt2win = {
26
+ '6.1': '0.1', // win-7
27
+ '6.2': '0.2', // win-8
28
+ '6.3': '0.3', // win-8.1
29
+ '10.0': '10.0', // win-10
30
+ '11.0': '13.0', // win-11
31
+ };
32
+ let ver = nt2win[m[1]];
33
+ if (ver)
34
+ platformVersion = padVersion(ver, 3);
35
+ if ((m2 = /\b(WOW64|Win64|x64)\b/.exec(platformInfo)) !== null) {
36
+ architecture = 'x86';
37
+ bitness = '64';
38
+ }
39
+ } else if ((m = /Android (\d+(\.\d+)*)/.exec(platformInfo)) !== null) {
40
+ platform = 'Android';
41
+ platformVersion = padVersion(m[1]);
42
+ if ((m2 = /Linux (\w+)/.exec(navigator.platform)) !== null) {
43
+ if (m2[1]) {
44
+ m2 = parseArch(m2[1]);
45
+ architecture = m2[0];
46
+ bitness = m2[1];
47
+ }
48
+ }
49
+ } else if ((m = /(iPhone|iPod touch); CPU iPhone OS (\d+(_\d+)*)/.exec(platformInfo)) !== null) {
50
+ // see special notes at https://www.whatismybrowser.com/guides/the-latest-user-agent/safari
51
+ platform = 'iOS';
52
+ platformVersion = padVersion(m[2].replace(/_/g, '.'));
53
+ } else if ((m = /(iPad); CPU OS (\d+(_\d+)*)/.exec(platformInfo)) !== null) {
54
+ platform = 'iOS';
55
+ platformVersion = padVersion(m[2].replace(/_/g, '.'));
56
+ } else if ((m = /Macintosh; (Intel|\w+) Mac OS X (\d+([_.]\d+)*)/.exec(platformInfo)) !== null) {
57
+ platform = 'macOS';
58
+ platformVersion = padVersion(m[2].replace(/_/g, '.'));
59
+ } else if ((m = /Linux/.exec(platformInfo)) !== null) {
60
+ platform = 'Linux';
61
+ platformVersion = '';
62
+ // TODO
63
+ } else if ((m = /CrOS (\w+) (\d+(\.\d+)*)/.exec(platformInfo)) !== null) {
64
+ platform = 'Chrome OS';
65
+ platformVersion = padVersion(m[2]);
66
+ m2 = parseArch(m[1]);
67
+ architecture = m2[0];
68
+ bitness = m2[1];
69
+ }
70
+ if (!platform) {
71
+ platform = 'Unknown';
72
+ }
73
+ // detect fullVersionList / brands
74
+ let notABrand = {brand: ' Not;A Brand', version: '99.0.0.0'};
75
+ if ((m = /Chrome\/(\d+(\.\d+)*)/.exec(versionInfo)) !== null && navigator.vendor === 'Google Inc.') {
76
+ fullVersionList.push({brand: 'Chromium', version: padVersion(m[1], 4)});
77
+ if ((m2 = /(Edge?)\/(\d+(\.\d+)*)/.exec(versionInfo)) !== null) {
78
+ let identBrandMap = {
79
+ 'Edge': 'Microsoft Edge',
80
+ 'Edg': 'Microsoft Edge',
81
+ };
82
+ let brand = identBrandMap[m[1]];
83
+ fullVersionList.push({brand: brand, version: padVersion(m2[2], 4)});
84
+ } else {
85
+ fullVersionList.push({brand: 'Google Chrome', version: padVersion(m[1], 4)});
86
+ }
87
+ if (/\bwv\b/.exec(platformInfo)) {
88
+ webview = true;
89
+ }
90
+ } else if ((m = /AppleWebKit\/(\d+(\.\d+)*)/.exec(versionInfo)) !== null && navigator.vendor === 'Apple Computer, Inc.') {
91
+ fullVersionList.push({brand: 'WebKit', version: padVersion(m[1])});
92
+ if (platform === 'iOS' && (m2 = /(CriOS|EdgiOS|FxiOS|Version)\/(\d+(\.\d+)*)/.exec(versionInfo)) != null) {
93
+ let identBrandMap = { // no
94
+ 'CriOS': 'Google Chrome',
95
+ 'EdgiOS': 'Microsoft Edge',
96
+ 'FxiOS': 'Mozilla Firefox',
97
+ 'Version': 'Apple Safari',
98
+ };
99
+ let brand = identBrandMap[m2[1]];
100
+ fullVersionList.push({brand, version: padVersion(m2[2])});
101
+ if (items.findIndex((s) => s.startsWith('Safari/')) === -1) {
102
+ webview = true;
103
+ }
104
+ }
105
+ } else if ((m = /Firefox\/(\d+(\.\d+)*)/.exec(versionInfo)) !== null) {
106
+ fullVersionList.push({brand: 'Firefox', version: padVersion(m[1])});
107
+ } else {
108
+ fullVersionList.push(notABrand);
109
+ }
110
+ uaFullVersion = fullVersionList.length > 0 ? fullVersionList[fullVersionList.length - 1] : '';
111
+ let brands = fullVersionList.map((b) => {
112
+ let pos = b.version.indexOf('.');
113
+ let version = pos === -1 ? b.version : b.version.slice(0, pos);
114
+ return {brand: b.brand, version};
115
+ });
116
+ // TODO detect architecture, bitness and model
117
+ return {
118
+ mobile,
119
+ platform,
120
+ brands,
121
+ platformVersion,
122
+ architecture,
123
+ bitness,
124
+ model,
125
+ uaFullVersion,
126
+ fullVersionList,
127
+ webview
128
+ };
129
+ }
130
+
131
+ function parseArch(arch) {
132
+ switch (arch) {
133
+ case 'x86_64':
134
+ case 'x64':
135
+ return ['x86', '64'];
136
+ case 'x86_32':
137
+ case 'x86':
138
+ return ['x86', ''];
139
+ case 'armv6l':
140
+ case 'armv7l':
141
+ case 'armv8l':
142
+ return [arch, ''];
143
+ case 'aarch64':
144
+ return ['arm', '64'];
145
+ default:
146
+ return ['', ''];
147
+ }
148
+ }
149
+ function padVersion(ver, minSegs = 3) {
150
+ let parts = ver.split('.');
151
+ let len = parts.length;
152
+ if (len < minSegs) {
153
+ for (let i = 0, lenToPad = minSegs - len; i < lenToPad; i += 1) {
154
+ parts.push('0');
155
+ }
156
+ return parts.join('.');
157
+ }
158
+ return ver;
159
+ }
160
+
161
+ class NavigatorUAData {
162
+ constructor(navigator) {
163
+ this._ch = getClientHints(navigator);
164
+ Object.defineProperties(this, {
165
+ _ch: {enumerable: false},
166
+ });
167
+ }
168
+ get mobile() {
169
+ return this._ch.mobile;
170
+ }
171
+ get platform() {
172
+ return this._ch.platform;
173
+ }
174
+ get brands() {
175
+ return this._ch.brands;
176
+ }
177
+ getHighEntropyValues(hints) {
178
+ return new Promise((resolve, reject) => {
179
+ if (!Array.isArray(hints)) {
180
+ throw new TypeError('argument hints is not an array');
181
+ }
182
+ let hintSet = new Set(hints);
183
+ let data = this._ch;
184
+ let obj = {
185
+ mobile: data.mobile,
186
+ platform: data.platform,
187
+ brands: data.brands,
188
+ };
189
+ if (hintSet.has('architecture'))
190
+ obj.architecture = data.architecture;
191
+ if (hintSet.has('bitness'))
192
+ obj.bitness = data.bitness;
193
+ if (hintSet.has('model'))
194
+ obj.model = data.model;
195
+ if (hintSet.has('platformVersion'))
196
+ obj.platformVersion = data.platformVersion;
197
+ if (hintSet.has('uaFullVersion'))
198
+ obj.uaFullVersion = data.uaFullVersion;
199
+ if (hintSet.has('fullVersionList'))
200
+ obj.fullVersionList = data.fullVersionList;
201
+ resolve(obj);
202
+ });
203
+ }
204
+ toJSON() {
205
+ let data = this._ch;
206
+ return {
207
+ mobile: data.mobile,
208
+ brands: data.brands,
209
+ };
210
+ }
211
+ }
212
+ Object.defineProperty(NavigatorUAData.prototype, Symbol.toStringTag, {
213
+ enumerable: false,
214
+ configurable: true,
215
+ writable: false,
216
+ value: 'NavigatorUAData'
217
+ });
218
+
219
+ async function getClientInfo(navigator, keys) {
220
+ let info = {};
221
+ if (location.protocol === 'https:') {
222
+ if (!keys) {
223
+ keys = ['brands', 'mobile', 'platform', 'platformVersion', 'architecture', 'bitness', 'model', 'fullVersionList'];
224
+ }
225
+ if (!navigator.userAgentData) {
226
+ let customUAData = new NavigatorUAData(navigator);
227
+ info = await customUAData.getHighEntropyValues(keys);
228
+ } else {
229
+ info = await navigator.userAgentData.getHighEntropyValues(keys);
230
+ }
231
+ }
232
+ return info;
233
+ }
234
+
235
+ module.exports = {
236
+ getClientInfo
237
+ };
@@ -4,6 +4,7 @@ const { v1: uuid_v1 } = require('uuid');
4
4
  const constants = require("./constants");
5
5
  const util = require('./util');
6
6
  const LoggerObject = require('./util').logger;
7
+ const clientInfo = require('./client-info');
7
8
  const Promise = require('promise-polyfill');
8
9
  const KalmanFilter = require('kalmanjs');
9
10
  const browserDetails = require('webrtc-adapter').default.browserDetails;
@@ -11,6 +12,7 @@ const LOG_PREFIX = "core";
11
12
  var coreLogger;
12
13
  var loggerConf = {push: false, severity: "INFO"};
13
14
  var isUsingTemasysPlugin = false;
15
+ var clientUAData;
14
16
 
15
17
  /**
16
18
  * @namespace Flashphoner
@@ -62,7 +64,7 @@ var disableConnectionQualityCalculation;
62
64
  * @throws {Error} Error if none of MediaProviders available
63
65
  * @memberof Flashphoner
64
66
  */
65
- var init = function (options) {
67
+ var init = async function (options) {
66
68
  if (!initialized) {
67
69
  if (!options) {
68
70
  options = {};
@@ -190,8 +192,12 @@ var init = function (options) {
190
192
  if (!waitingTemasys && options.mediaProvidersReadyCallback) {
191
193
  options.mediaProvidersReadyCallback(Object.keys(MediaProvider));
192
194
  }
195
+
193
196
  coreLogger.info(LOG_PREFIX, "Initialized");
194
197
  initialized = true;
198
+
199
+ clientUAData = await clientInfo.getClientInfo(window.navigator);
200
+ coreLogger.info(LOG_PREFIX, "Client system data: " + JSON.stringify(clientUAData));
195
201
  }
196
202
  };
197
203
 
@@ -572,13 +578,16 @@ var createSession = function (options) {
572
578
  appKey: appKey,
573
579
  mediaProviders: Object.keys(MediaProvider),
574
580
  keepAlive: keepAlive,
575
- authToken:authToken,
581
+ authToken: authToken,
576
582
  clientVersion: "2.0",
577
583
  clientOSVersion: window.navigator.appVersion,
578
584
  clientBrowserVersion: window.navigator.userAgent,
579
585
  msePacketizationVersion: 2,
580
586
  custom: options.custom
581
587
  };
588
+ if (clientUAData) {
589
+ cConfig.clientInfo = clientUAData;
590
+ }
582
591
  if (sipConfig) {
583
592
  util.copyObjectPropsToAnotherObject(sipConfig, cConfig);
584
593
  }
@@ -1009,6 +1018,8 @@ var createSession = function (options) {
1009
1018
  stripCodecs: stripCodecs
1010
1019
  });
1011
1020
  }).then(function (offer) {
1021
+ // Get local media info to send in publishStream message
1022
+ let localMediaInfo = collectLocalMediaInfo(MediaProvider[mediaProvider], localDisplay);
1012
1023
  send("call", {
1013
1024
  callId: id_,
1014
1025
  incoming: false,
@@ -1021,7 +1032,8 @@ var createSession = function (options) {
1021
1032
  caller: login,
1022
1033
  callee: callee_,
1023
1034
  custom: options.custom,
1024
- visibleName: visibleName_
1035
+ visibleName: visibleName_,
1036
+ localMediaInfo: localMediaInfo
1025
1037
  });
1026
1038
  });
1027
1039
  }).catch(function (error) {
@@ -1155,6 +1167,8 @@ var createSession = function (options) {
1155
1167
  });
1156
1168
  }).then(function (sdp) {
1157
1169
  if (status_ != CALL_STATUS.FINISH && status_ != CALL_STATUS.FAILED) {
1170
+ // Get local media info to send in publishStream message
1171
+ let localMediaInfo = collectLocalMediaInfo(MediaProvider[mediaProvider], localDisplay);
1158
1172
  send("answer", {
1159
1173
  callId: id_,
1160
1174
  incoming: true,
@@ -1166,7 +1180,8 @@ var createSession = function (options) {
1166
1180
  sipSDP: sipSDP,
1167
1181
  caller: cConfig.login,
1168
1182
  callee: callee_,
1169
- custom: options.custom
1183
+ custom: options.custom,
1184
+ localMediaInfo: localMediaInfo
1170
1185
  });
1171
1186
  } else {
1172
1187
  hangup();
@@ -2006,6 +2021,7 @@ var createSession = function (options) {
2006
2021
  }
2007
2022
  return;
2008
2023
  }
2024
+
2009
2025
  //create mediaProvider connection
2010
2026
  MediaProvider[mediaProvider].createConnection({
2011
2027
  id: id_,
@@ -2028,6 +2044,8 @@ var createSession = function (options) {
2028
2044
  });
2029
2045
  }).then(function (offer) {
2030
2046
  logger.debug(LOG_PREFIX, "Offer SDP:\n" + offer.sdp);
2047
+ // Get local media info to send in publishStream message
2048
+ let localMediaInfo = collectLocalMediaInfo(MediaProvider[mediaProvider], display);
2031
2049
  //publish stream with offer sdp to server
2032
2050
  send("publishStream", {
2033
2051
  mediaSessionId: id_,
@@ -2046,7 +2064,8 @@ var createSession = function (options) {
2046
2064
  rtmpUrl: rtmpUrl,
2047
2065
  constraints: constraints,
2048
2066
  transport: transportType,
2049
- cvoExtension: cvoExtension
2067
+ cvoExtension: cvoExtension,
2068
+ localMediaInfo: localMediaInfo
2050
2069
  });
2051
2070
  });
2052
2071
  }).catch(function (error) {
@@ -2856,6 +2875,65 @@ var createSession = function (options) {
2856
2875
  return sessionLogger;
2857
2876
  };
2858
2877
 
2878
+ const collectLocalMediaInfo = function (mediaProvider, display) {
2879
+ // Get devices available
2880
+ let videoCams = mediaProvider.videoCams || [];
2881
+ let mics = mediaProvider.mics || [];
2882
+
2883
+ if (videoCams.length) {
2884
+ logger.info(LOG_PREFIX, "Video inputs available: " + JSON.stringify(videoCams));
2885
+ }
2886
+ if (mics.length) {
2887
+ logger.info(LOG_PREFIX, "Audio inputs available: " + JSON.stringify(mics));
2888
+ }
2889
+
2890
+ // Get track labels to identify publishing device
2891
+ let audioTracks = [];
2892
+ let videoTracks = [];
2893
+ let localVideo;
2894
+ if (mediaProvider.getCacheInstance) {
2895
+ localVideo = mediaProvider.getCacheInstance(display);
2896
+ }
2897
+ if (!localVideo && mediaProvider.getVideoElement) {
2898
+ localVideo = mediaProvider.getVideoElement(display);
2899
+ }
2900
+ if (localVideo) {
2901
+ localVideo.srcObject.getAudioTracks().forEach((track) => {
2902
+ let device = track.label;
2903
+ if (device === "MediaStreamAudioDestinationNode" && mediaProvider.getAudioSourceDevice) {
2904
+ device = mediaProvider.getAudioSourceDevice();
2905
+ }
2906
+ audioTracks.push({
2907
+ trackId: track.id,
2908
+ device: device
2909
+ });
2910
+ });
2911
+ localVideo.srcObject.getVideoTracks().forEach((track) => {
2912
+ videoTracks.push({
2913
+ trackId: track.id,
2914
+ device: track.label
2915
+ });
2916
+ });
2917
+ }
2918
+ if (videoTracks.length) {
2919
+ logger.info(LOG_PREFIX, "Video tracks captured: " + JSON.stringify(videoTracks));
2920
+ }
2921
+ if (audioTracks.length) {
2922
+ logger.info(LOG_PREFIX, "Audio tracks captured: " + JSON.stringify(audioTracks));
2923
+ }
2924
+
2925
+ return {
2926
+ devices: {
2927
+ video: videoCams,
2928
+ audio: mics
2929
+ },
2930
+ tracks: {
2931
+ video: videoTracks,
2932
+ audio: audioTracks
2933
+ }
2934
+ };
2935
+ }
2936
+
2859
2937
  //export Session
2860
2938
  session.id = id;
2861
2939
  session.status = status;