@wazo/sdk 0.37.16 → 0.37.20

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.
Files changed (114) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +9 -5
  3. package/esm/__tests__/web-rtc-client.test.js +69 -19
  4. package/esm/api/agentd.js +1 -1
  5. package/esm/api/amid.js +1 -1
  6. package/esm/api/application.js +1 -1
  7. package/esm/api/auth.js +1 -1
  8. package/esm/api/call-logd.js +3 -3
  9. package/esm/api/calld.js +1 -1
  10. package/esm/api/chatd.js +1 -1
  11. package/esm/api/confd.js +7 -3
  12. package/esm/api/ctid-ng.js +1 -1
  13. package/esm/api/dird.js +1 -1
  14. package/esm/api/webhookd.js +1 -1
  15. package/esm/api-client.js +9 -5
  16. package/esm/checker/Checker.js +69 -19
  17. package/esm/checker/checks/aor.js +9 -5
  18. package/esm/checker/checks/api.js +9 -5
  19. package/esm/checker/checks/index.js +69 -19
  20. package/esm/checker/checks/wazo-websocket.js +1 -1
  21. package/esm/checker/checks/webrtc-transport.js +69 -19
  22. package/esm/checker/checks/webrtc.js +69 -19
  23. package/esm/domain/AdHocAPIConference.js +9 -5
  24. package/esm/domain/CallLog.js +2 -2
  25. package/esm/domain/Features.js +9 -5
  26. package/esm/domain/Meeting.js +6 -2
  27. package/esm/domain/Phone/CTIPhone.js +9 -5
  28. package/esm/domain/Phone/WebRTCPhone.js +9 -2
  29. package/esm/domain/Recording.js +1 -1
  30. package/esm/domain/__tests__/CallLog.test.js +2 -2
  31. package/esm/index.js +77 -19
  32. package/esm/lib/WazoSessionDescriptionHandler.js +1 -1
  33. package/esm/service/CallApi.js +9 -5
  34. package/esm/service/IssueReporter.js +1 -1
  35. package/esm/service/__tests__/IssueReporter.test.js +1 -1
  36. package/esm/service/getApiClient.js +9 -5
  37. package/esm/simple/Auth.js +77 -19
  38. package/esm/simple/Configuration.js +77 -19
  39. package/esm/simple/Directory.js +9 -5
  40. package/esm/simple/Phone.js +77 -19
  41. package/esm/simple/Websocket.js +1 -1
  42. package/esm/simple/index.js +77 -19
  43. package/esm/simple/room/LocalParticipant.js +77 -19
  44. package/esm/simple/room/Participant.js +77 -19
  45. package/esm/simple/room/RemoteParticipant.js +77 -19
  46. package/esm/simple/room/Room.js +77 -19
  47. package/esm/simple/room/SipRoom.js +77 -19
  48. package/esm/simple/room/Stream.js +77 -19
  49. package/esm/simple/room/__tests__/RemoteParticipant.test.js +77 -19
  50. package/esm/simple/utils.js +77 -19
  51. package/esm/utils/__tests__/api-requester.test.js +1 -1
  52. package/esm/utils/api-requester.js +1 -1
  53. package/esm/web-rtc-client.js +69 -19
  54. package/esm/websocket-client.js +1 -1
  55. package/lib/__tests__/api-client.test.js +9 -5
  56. package/lib/__tests__/web-rtc-client.test.js +69 -19
  57. package/lib/api/agentd.js +1 -1
  58. package/lib/api/amid.js +1 -1
  59. package/lib/api/application.js +1 -1
  60. package/lib/api/auth.js +1 -1
  61. package/lib/api/call-logd.js +3 -3
  62. package/lib/api/calld.js +1 -1
  63. package/lib/api/chatd.js +1 -1
  64. package/lib/api/confd.js +7 -3
  65. package/lib/api/ctid-ng.js +1 -1
  66. package/lib/api/dird.js +1 -1
  67. package/lib/api/webhookd.js +1 -1
  68. package/lib/api-client.js +9 -5
  69. package/lib/checker/Checker.js +69 -19
  70. package/lib/checker/checks/aor.js +9 -5
  71. package/lib/checker/checks/api.js +9 -5
  72. package/lib/checker/checks/index.js +69 -19
  73. package/lib/checker/checks/wazo-websocket.js +1 -1
  74. package/lib/checker/checks/webrtc-transport.js +69 -19
  75. package/lib/checker/checks/webrtc.js +69 -19
  76. package/lib/domain/AdHocAPIConference.js +9 -5
  77. package/lib/domain/CallLog.js +2 -2
  78. package/lib/domain/Features.js +9 -5
  79. package/lib/domain/Meeting.js +6 -2
  80. package/lib/domain/Phone/CTIPhone.js +9 -5
  81. package/lib/domain/Phone/WebRTCPhone.js +9 -1
  82. package/lib/domain/Recording.js +1 -1
  83. package/lib/domain/__tests__/CallLog.test.js +2 -2
  84. package/lib/index.js +77 -19
  85. package/lib/lib/WazoSessionDescriptionHandler.js +1 -1
  86. package/lib/service/CallApi.js +9 -5
  87. package/lib/service/IssueReporter.js +1 -1
  88. package/lib/service/__tests__/IssueReporter.test.js +1 -1
  89. package/lib/service/getApiClient.js +9 -5
  90. package/lib/simple/Auth.js +77 -19
  91. package/lib/simple/Configuration.js +77 -19
  92. package/lib/simple/Directory.js +9 -5
  93. package/lib/simple/Phone.js +77 -19
  94. package/lib/simple/Websocket.js +1 -1
  95. package/lib/simple/index.js +77 -19
  96. package/lib/simple/room/LocalParticipant.js +77 -19
  97. package/lib/simple/room/Participant.js +77 -19
  98. package/lib/simple/room/RemoteParticipant.js +77 -19
  99. package/lib/simple/room/Room.js +77 -19
  100. package/lib/simple/room/SipRoom.js +77 -19
  101. package/lib/simple/room/Stream.js +77 -19
  102. package/lib/simple/room/__tests__/RemoteParticipant.test.js +77 -19
  103. package/lib/simple/utils.js +77 -19
  104. package/lib/utils/__tests__/api-requester.test.js +1 -1
  105. package/lib/utils/api-requester.js +1 -1
  106. package/lib/web-rtc-client.js +69 -19
  107. package/lib/websocket-client.js +1 -1
  108. package/package.json +2 -2
  109. package/src/domain/CallLog.js +1 -1
  110. package/src/domain/Meeting.js +6 -2
  111. package/src/domain/Phone/WebRTCPhone.js +7 -0
  112. package/src/domain/Recording.js +1 -1
  113. package/src/service/IssueReporter.js +1 -1
  114. package/src/web-rtc-client.js +60 -14
@@ -675,7 +675,7 @@ class IssueReporter {
675
675
  payload._retry = retry + 1;
676
676
  }
677
677
 
678
- this._sendDebugToGrafana(body, retry + 1);
678
+ this._sendDebugToGrafana(payload, retry + 1);
679
679
  }, 5000 + retry * 1000);
680
680
  });
681
681
  }
@@ -3177,7 +3177,8 @@ class ExternalApp {
3177
3177
 
3178
3178
 
3179
3179
 
3180
-
3180
+
3181
+
3181
3182
 
3182
3183
 
3183
3184
  class Meeting {
@@ -3189,6 +3190,7 @@ class Meeting {
3189
3190
 
3190
3191
 
3191
3192
 
3193
+
3192
3194
 
3193
3195
 
3194
3196
  static parse(plain ) {
@@ -3200,6 +3202,7 @@ class Meeting {
3200
3202
  port: plain.port,
3201
3203
  uuid: plain.uuid,
3202
3204
  number: plain.number,
3205
+ persistent: plain.persistent,
3203
3206
  });
3204
3207
  }
3205
3208
 
@@ -3207,7 +3210,7 @@ class Meeting {
3207
3210
  return newFrom(meeting, Meeting);
3208
3211
  }
3209
3212
 
3210
- constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, uri, number } = {}) {
3213
+ constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, uri, number, persistent } = {}) {
3211
3214
  this.guestSipAuthorization = guestSipAuthorization;
3212
3215
  this.uri = uri;
3213
3216
  this.name = name;
@@ -3215,6 +3218,7 @@ class Meeting {
3215
3218
  this.port = port;
3216
3219
  this.uuid = uuid;
3217
3220
  this.number = number;
3221
+ this.persistent = persistent;
3218
3222
 
3219
3223
  // Useful to compare instead of instanceof with minified code
3220
3224
  this.type = 'Meeting';
@@ -3778,7 +3782,7 @@ class Recording {
3778
3782
 
3779
3783
 
3780
3784
 
3781
- static parseMany(recordings ) {
3785
+ static parseMany(recordings = []) {
3782
3786
  return recordings.map(item => Recording.parse(item));
3783
3787
  }
3784
3788
 
@@ -3904,7 +3908,7 @@ class CallLog {
3904
3908
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3905
3909
  start: moment__default['default'](plain.start).toDate(),
3906
3910
  end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3907
- recordings: Recording.parseMany(plain.recordings),
3911
+ recordings: Recording.parseMany(plain.recordings || []),
3908
3912
  });
3909
3913
  }
3910
3914
 
@@ -5299,6 +5303,7 @@ const ON_REINVITE = 'reinvite';
5299
5303
  const ON_ERROR = 'onError';
5300
5304
  const ON_SCREEN_SHARING_REINVITE = 'onScreenSharingReinvite';
5301
5305
  const ON_NETWORK_STATS = 'onNetworkStats';
5306
+ const ON_DISCONNECTED = 'onDisconnected';
5302
5307
 
5303
5308
  const events = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
5304
5309
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
@@ -5375,6 +5380,7 @@ class WebRTCClient extends Emitter {
5375
5380
 
5376
5381
 
5377
5382
 
5383
+
5378
5384
 
5379
5385
  static isAPrivateIp(ip ) {
5380
5386
  const regex = /^(?:10|127|172\.(?:1[6-9]|2[0-9]|3[01])|192\.168)\..*/;
@@ -5440,6 +5446,7 @@ class WebRTCClient extends Emitter {
5440
5446
  this.ON_ERROR = ON_ERROR;
5441
5447
  this.ON_SCREEN_SHARING_REINVITE = ON_SCREEN_SHARING_REINVITE;
5442
5448
  this.ON_NETWORK_STATS = ON_NETWORK_STATS;
5449
+ this.ON_DISCONNECTED = ON_DISCONNECTED;
5443
5450
  }
5444
5451
 
5445
5452
  configureMedia(media ) {
@@ -6041,7 +6048,7 @@ class WebRTCClient extends Emitter {
6041
6048
  // We search for the last transceiver without `video-` in the mid (video- means remote transceiver)
6042
6049
  const transceivers = pc.getTransceivers();
6043
6050
  const transceiverIdx = lastIndexOf(transceivers, transceiver =>
6044
- transceiver.sender.track === null && transceiver.mid.indexOf('video') === -1);
6051
+ transceiver.sender.track === null && transceiver.mid && transceiver.mid.indexOf('video') === -1);
6045
6052
 
6046
6053
  videoSender = transceiverIdx !== -1 ? transceivers[transceiverIdx].sender : null;
6047
6054
  } else {
@@ -6287,10 +6294,13 @@ class WebRTCClient extends Emitter {
6287
6294
  return null;
6288
6295
  }
6289
6296
 
6290
- // let's update the local value
6291
- if (this.audio && this.audio.deviceId) {
6292
- // $FlowFixMe
6293
- this.audio.deviceId.exact = id;
6297
+ // let's update the local audio value
6298
+ if (this.audio) {
6299
+ this.audio = {
6300
+ deviceId: {
6301
+ exact: id,
6302
+ },
6303
+ };
6294
6304
  }
6295
6305
 
6296
6306
  if (session) {
@@ -6303,6 +6313,9 @@ class WebRTCClient extends Emitter {
6303
6313
  const sender = pc && pc.getSenders().find(s => audioTrack && s && s.track && s.track.kind === audioTrack.kind);
6304
6314
 
6305
6315
  if (sender) {
6316
+ if (sender.track) {
6317
+ audioTrack.enabled = sender.track.enabled;
6318
+ }
6306
6319
  sender.replaceTrack(audioTrack);
6307
6320
  }
6308
6321
 
@@ -6327,7 +6340,7 @@ class WebRTCClient extends Emitter {
6327
6340
  return null;
6328
6341
  }
6329
6342
 
6330
- // let's update the local value
6343
+ // let's update the local video value
6331
6344
  if (this.video) {
6332
6345
  this.video = {
6333
6346
  deviceId: {
@@ -6362,12 +6375,21 @@ class WebRTCClient extends Emitter {
6362
6375
  return navigator.mediaDevices.getUserMedia(constraints).then(async stream => {
6363
6376
  const videoTrack = stream.getVideoTracks()[0];
6364
6377
  let sender = pc && pc.getSenders().find(s => videoTrack && s && s.track && s.track.kind === videoTrack.kind);
6378
+ let wasTrackEnabled = false;
6365
6379
  if (!sender) {
6366
6380
  sender = pc && pc.getSenders().find(s => !s.track);
6367
6381
  }
6368
6382
 
6369
6383
  if (sender) {
6370
- sender.replaceTrack(videoTrack);
6384
+ // No video track means video not enabled
6385
+ wasTrackEnabled = sender.track ? sender.track.enabled : false;
6386
+ videoTrack.enabled = wasTrackEnabled;
6387
+
6388
+ if (!wasTrackEnabled) {
6389
+ videoTrack.stop();
6390
+ }
6391
+
6392
+ sender.replaceTrack(wasTrackEnabled ? videoTrack : null);
6371
6393
  }
6372
6394
 
6373
6395
  // let's update the local stream
@@ -7051,6 +7073,7 @@ class WebRTCClient extends Emitter {
7051
7073
 
7052
7074
  this.updateRemoteStream(this.getSipSessionId(session));
7053
7075
 
7076
+ const pc = session.sessionDescriptionHandler.peerConnection;
7054
7077
  const onTrack = (event ) => {
7055
7078
  const isAudioOnly = this._isAudioOnly(session);
7056
7079
  const { kind, label, readyState, id, muted } = event.track;
@@ -7070,6 +7093,12 @@ class WebRTCClient extends Emitter {
7070
7093
 
7071
7094
  session.sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack;
7072
7095
 
7096
+ pc.oniceconnectionstatechange = () => {
7097
+ if (pc.iceConnectionState === 'disconnected') {
7098
+ this.eventEmitter.emit(ON_DISCONNECTED);
7099
+ }
7100
+ };
7101
+
7073
7102
  if (withEvent) {
7074
7103
  this.eventEmitter.emit(ACCEPTED, session, sessionDialog);
7075
7104
  }
@@ -7281,25 +7310,33 @@ class WebRTCClient extends Emitter {
7281
7310
  const lastVideoSent = lastNetworkStats ? lastNetworkStats.videoBytesSent : 0;
7282
7311
  const lastAudioReceived = lastNetworkStats ? lastNetworkStats.audioBytesReceived : 0;
7283
7312
  const lastVideoReceived = lastNetworkStats ? lastNetworkStats.videoBytesReceived : 0;
7313
+ const lastTransportSent = lastNetworkStats ? lastNetworkStats.transportSent : 0;
7314
+ const lastTransportReceived = lastNetworkStats ? lastNetworkStats.transportReceived : 0;
7284
7315
  let audioBytesSent = 0;
7285
7316
  let audioBytesReceived = 0;
7286
7317
  let videoBytesSent = 0;
7287
7318
  let videoBytesReceived = 0;
7319
+ let transportSent = 0;
7320
+ let transportReceived = 0;
7321
+ let packetsLost = 0;
7288
7322
 
7289
7323
  stats.forEach(report => {
7290
7324
  if (report.type === 'outbound-rtp' && report.kind === 'audio') {
7325
+ audioBytesSent += report.bytesSent + report.headerBytesSent;
7326
+ }
7327
+ if (report.type === 'remote-outbound-rtp' && report.kind === 'audio') {
7291
7328
  audioBytesSent += report.bytesSent;
7292
7329
  }
7293
7330
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7294
- videoBytesSent += report.bytesSent;
7331
+ videoBytesSent += report.bytesSent + report.headerBytesSent;
7295
7332
  }
7296
7333
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
7297
- networkStats.packetsLost = report.packetsLost;
7334
+ packetsLost += report.packetsLost;
7298
7335
  networkStats.packetsReceived = report.packetsReceived;
7299
- audioBytesReceived += report.bytesReceived;
7336
+ audioBytesReceived += report.bytesReceived + report.headerBytesReceived;
7300
7337
  }
7301
7338
  if (report.type === 'inbound-rtp' && report.kind === 'video') {
7302
- videoBytesReceived += report.bytesReceived;
7339
+ videoBytesReceived += report.bytesReceived + report.headerBytesReceived;
7303
7340
  }
7304
7341
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7305
7342
  if ('framesPerSecond' in report) {
@@ -7311,19 +7348,32 @@ class WebRTCClient extends Emitter {
7311
7348
  }
7312
7349
  }
7313
7350
  if (report.type === 'remote-inbound-rtp' && report.kind === 'audio') {
7351
+ packetsLost += report.packetsLost;
7314
7352
  networkStats.roundTripTime = report.roundTripTime;
7315
7353
  networkStats.jitter = report.jitter;
7316
7354
  }
7355
+ if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
7356
+ packetsLost += report.packetsLost;
7357
+ }
7358
+ if (report.type === 'transport') {
7359
+ transportSent += report.bytesSent;
7360
+ transportReceived += report.bytesReceived;
7361
+ }
7317
7362
  });
7318
7363
 
7364
+ networkStats.packetsLost = packetsLost;
7319
7365
  networkStats.audioBytesSent = audioBytesSent;
7320
7366
  networkStats.audioBytesReceived = audioBytesReceived;
7321
7367
  networkStats.videoBytesSent = videoBytesSent;
7322
7368
  networkStats.videoBytesReceived = videoBytesReceived;
7369
+ networkStats.transportSent = transportSent;
7370
+ networkStats.transportReceived = transportReceived;
7371
+
7372
+ const bandwidth = (networkStats.audioBytesSent - lastAudioSent) + (networkStats.videoBytesSent - lastVideoSent)
7373
+ + (networkStats.audioBytesReceived - lastAudioReceived) + (networkStats.videoBytesReceived - lastVideoReceived)
7374
+ + (networkStats.transportReceived - lastTransportReceived) + (networkStats.transportSent - lastTransportSent);
7323
7375
 
7324
- networkStats.bandwidth = (networkStats.audioBytesSent - lastAudioSent)
7325
- + (networkStats.videoBytesSent - lastVideoSent) + (networkStats.audioBytesReceived - lastAudioReceived)
7326
- + (networkStats.videoBytesReceived - lastVideoReceived);
7376
+ networkStats.bandwidth = bandwidth;
7327
7377
 
7328
7378
  this.eventEmitter.emit(ON_NETWORK_STATS, session, networkStats, this.sessionNetworkStats[sessionId]);
7329
7379
  this.sessionNetworkStats[sessionId].push(networkStats);
@@ -383,7 +383,7 @@ class IssueReporter {
383
383
  payload._retry = retry + 1;
384
384
  }
385
385
 
386
- this._sendDebugToGrafana(body, retry + 1);
386
+ this._sendDebugToGrafana(payload, retry + 1);
387
387
  }, 5000 + retry * 1000);
388
388
  });
389
389
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.37.16",
3
+ "version": "0.37.20",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "dist/wazo-sdk.js",
6
6
  "author": "Wazo (http://wazo.io)",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "events": "^3.3.0",
49
49
  "fstream": "^1.0.12",
50
- "getstats": "manuquentin/getStats#fix_undefined_results",
50
+ "getstats": "manuquentin/getStats#fix_stats_mobile",
51
51
  "globby": "^11.0.3",
52
52
  "google-libphonenumber": "^3.2.18",
53
53
  "js-base64": "^3.6.0",
@@ -108,7 +108,7 @@ export default class CallLog {
108
108
  duration: (plain.duration || 0) * 1000, // duration is in seconds
109
109
  start: moment(plain.start).toDate(),
110
110
  end: plain.end ? moment(plain.end).toDate() : null,
111
- recordings: Recording.parseMany(plain.recordings),
111
+ recordings: Recording.parseMany(plain.recordings || []),
112
112
  });
113
113
  }
114
114
 
@@ -8,7 +8,8 @@ export type MeetingCreationResponse = {
8
8
  owner_uuids: Array<string>,
9
9
  port: string,
10
10
  uuid: string,
11
- number: string
11
+ number: string,
12
+ persistent: boolean,
12
13
  }
13
14
 
14
15
  export default class Meeting {
@@ -20,6 +21,7 @@ export default class Meeting {
20
21
  name: string;
21
22
  port: string;
22
23
  number: string;
24
+ persistent: boolean;
23
25
  ownerUuids: Array<string>;
24
26
 
25
27
  static parse(plain: MeetingCreationResponse): Meeting {
@@ -31,6 +33,7 @@ export default class Meeting {
31
33
  port: plain.port,
32
34
  uuid: plain.uuid,
33
35
  number: plain.number,
36
+ persistent: plain.persistent,
34
37
  });
35
38
  }
36
39
 
@@ -38,7 +41,7 @@ export default class Meeting {
38
41
  return newFrom(meeting, Meeting);
39
42
  }
40
43
 
41
- constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, uri, number }: Object = {}) {
44
+ constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, uri, number, persistent }: Object = {}) {
42
45
  this.guestSipAuthorization = guestSipAuthorization;
43
46
  this.uri = uri;
44
47
  this.name = name;
@@ -46,6 +49,7 @@ export default class Meeting {
46
49
  this.port = port;
47
50
  this.uuid = uuid;
48
51
  this.number = number;
52
+ this.persistent = persistent;
49
53
 
50
54
  // Useful to compare instead of instanceof with minified code
51
55
  this.type = 'Meeting';
@@ -52,6 +52,7 @@ export const ON_MESSAGE_TRACK_UPDATED = 'onTrackUpdated';
52
52
  export const ON_NETWORK_STATS = 'onNetworkStats';
53
53
  export const ON_CHAT = 'phone/ON_CHAT';
54
54
  export const ON_SIGNAL = 'phone/ON_SIGNAL';
55
+ export const ON_DISCONNECTED = 'onDisconnected';
55
56
 
56
57
  export const MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
57
58
  export const MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
@@ -91,6 +92,7 @@ export const events = [
91
92
  ON_CHAT,
92
93
  ON_SIGNAL,
93
94
  ON_NETWORK_STATS,
95
+ ON_DISCONNECTED,
94
96
  ];
95
97
 
96
98
  const logger = IssueReporter.loggerFor('webrtc-phone');
@@ -1445,6 +1447,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
1445
1447
  this.eventEmitter.emit(ON_UNREGISTERED);
1446
1448
  });
1447
1449
 
1450
+ this.client.on(this.client.ON_DISCONNECTED, () => {
1451
+ logger.info('WebRTC disconnected');
1452
+ this.eventEmitter.emit(ON_DISCONNECTED);
1453
+ });
1454
+
1448
1455
  this.client.on(this.client.REGISTERED, () => {
1449
1456
  logger.info('WebRTC registered', {
1450
1457
  shouldSendReinvite: this.shouldSendReinvite,
@@ -24,7 +24,7 @@ export default class Recording {
24
24
  start: Date;
25
25
  uuid: string;
26
26
 
27
- static parseMany(recordings: RecordingResponse[]): Recording[] {
27
+ static parseMany(recordings: RecordingResponse[] = []): Recording[] {
28
28
  return recordings.map(item => Recording.parse(item));
29
29
  }
30
30
 
@@ -316,7 +316,7 @@ class IssueReporter {
316
316
  payload._retry = retry + 1;
317
317
  }
318
318
 
319
- this._sendDebugToGrafana(body, retry + 1);
319
+ this._sendDebugToGrafana(payload, retry + 1);
320
320
  }, 5000 + retry * 1000);
321
321
  });
322
322
  }
@@ -69,6 +69,7 @@ const ON_REINVITE = 'reinvite';
69
69
  const ON_ERROR = 'onError';
70
70
  const ON_SCREEN_SHARING_REINVITE = 'onScreenSharingReinvite';
71
71
  const ON_NETWORK_STATS = 'onNetworkStats';
72
+ const ON_DISCONNECTED = 'onDisconnected';
72
73
 
73
74
  export const events = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
74
75
  export const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
@@ -145,6 +146,7 @@ export default class WebRTCClient extends Emitter {
145
146
  ON_ERROR: string;
146
147
  ON_SCREEN_SHARING_REINVITE: string;
147
148
  ON_NETWORK_STATS: string;
149
+ ON_DISCONNECTED: string;
148
150
 
149
151
  static isAPrivateIp(ip: string): boolean {
150
152
  const regex = /^(?:10|127|172\.(?:1[6-9]|2[0-9]|3[01])|192\.168)\..*/;
@@ -210,6 +212,7 @@ export default class WebRTCClient extends Emitter {
210
212
  this.ON_ERROR = ON_ERROR;
211
213
  this.ON_SCREEN_SHARING_REINVITE = ON_SCREEN_SHARING_REINVITE;
212
214
  this.ON_NETWORK_STATS = ON_NETWORK_STATS;
215
+ this.ON_DISCONNECTED = ON_DISCONNECTED;
213
216
  }
214
217
 
215
218
  configureMedia(media: MediaConfig) {
@@ -811,7 +814,7 @@ export default class WebRTCClient extends Emitter {
811
814
  // We search for the last transceiver without `video-` in the mid (video- means remote transceiver)
812
815
  const transceivers = pc.getTransceivers();
813
816
  const transceiverIdx = lastIndexOf(transceivers, transceiver =>
814
- transceiver.sender.track === null && transceiver.mid.indexOf('video') === -1);
817
+ transceiver.sender.track === null && transceiver.mid && transceiver.mid.indexOf('video') === -1);
815
818
 
816
819
  videoSender = transceiverIdx !== -1 ? transceivers[transceiverIdx].sender : null;
817
820
  } else {
@@ -1057,10 +1060,13 @@ export default class WebRTCClient extends Emitter {
1057
1060
  return null;
1058
1061
  }
1059
1062
 
1060
- // let's update the local value
1061
- if (this.audio && this.audio.deviceId) {
1062
- // $FlowFixMe
1063
- this.audio.deviceId.exact = id;
1063
+ // let's update the local audio value
1064
+ if (this.audio) {
1065
+ this.audio = {
1066
+ deviceId: {
1067
+ exact: id,
1068
+ },
1069
+ };
1064
1070
  }
1065
1071
 
1066
1072
  if (session) {
@@ -1073,6 +1079,9 @@ export default class WebRTCClient extends Emitter {
1073
1079
  const sender = pc && pc.getSenders().find(s => audioTrack && s && s.track && s.track.kind === audioTrack.kind);
1074
1080
 
1075
1081
  if (sender) {
1082
+ if (sender.track) {
1083
+ audioTrack.enabled = sender.track.enabled;
1084
+ }
1076
1085
  sender.replaceTrack(audioTrack);
1077
1086
  }
1078
1087
 
@@ -1097,7 +1106,7 @@ export default class WebRTCClient extends Emitter {
1097
1106
  return null;
1098
1107
  }
1099
1108
 
1100
- // let's update the local value
1109
+ // let's update the local video value
1101
1110
  if (this.video) {
1102
1111
  this.video = {
1103
1112
  deviceId: {
@@ -1132,12 +1141,21 @@ export default class WebRTCClient extends Emitter {
1132
1141
  return navigator.mediaDevices.getUserMedia(constraints).then(async stream => {
1133
1142
  const videoTrack = stream.getVideoTracks()[0];
1134
1143
  let sender = pc && pc.getSenders().find(s => videoTrack && s && s.track && s.track.kind === videoTrack.kind);
1144
+ let wasTrackEnabled = false;
1135
1145
  if (!sender) {
1136
1146
  sender = pc && pc.getSenders().find(s => !s.track);
1137
1147
  }
1138
1148
 
1139
1149
  if (sender) {
1140
- sender.replaceTrack(videoTrack);
1150
+ // No video track means video not enabled
1151
+ wasTrackEnabled = sender.track ? sender.track.enabled : false;
1152
+ videoTrack.enabled = wasTrackEnabled;
1153
+
1154
+ if (!wasTrackEnabled) {
1155
+ videoTrack.stop();
1156
+ }
1157
+
1158
+ sender.replaceTrack(wasTrackEnabled ? videoTrack : null);
1141
1159
  }
1142
1160
 
1143
1161
  // let's update the local stream
@@ -1821,6 +1839,7 @@ export default class WebRTCClient extends Emitter {
1821
1839
 
1822
1840
  this.updateRemoteStream(this.getSipSessionId(session));
1823
1841
 
1842
+ const pc = session.sessionDescriptionHandler.peerConnection;
1824
1843
  const onTrack = (event: any) => {
1825
1844
  const isAudioOnly = this._isAudioOnly(session);
1826
1845
  const { kind, label, readyState, id, muted } = event.track;
@@ -1840,6 +1859,12 @@ export default class WebRTCClient extends Emitter {
1840
1859
 
1841
1860
  session.sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack;
1842
1861
 
1862
+ pc.oniceconnectionstatechange = () => {
1863
+ if (pc.iceConnectionState === 'disconnected') {
1864
+ this.eventEmitter.emit(ON_DISCONNECTED);
1865
+ }
1866
+ };
1867
+
1843
1868
  if (withEvent) {
1844
1869
  this.eventEmitter.emit(ACCEPTED, session, sessionDialog);
1845
1870
  }
@@ -2051,25 +2076,33 @@ export default class WebRTCClient extends Emitter {
2051
2076
  const lastVideoSent = lastNetworkStats ? lastNetworkStats.videoBytesSent : 0;
2052
2077
  const lastAudioReceived = lastNetworkStats ? lastNetworkStats.audioBytesReceived : 0;
2053
2078
  const lastVideoReceived = lastNetworkStats ? lastNetworkStats.videoBytesReceived : 0;
2079
+ const lastTransportSent = lastNetworkStats ? lastNetworkStats.transportSent : 0;
2080
+ const lastTransportReceived = lastNetworkStats ? lastNetworkStats.transportReceived : 0;
2054
2081
  let audioBytesSent = 0;
2055
2082
  let audioBytesReceived = 0;
2056
2083
  let videoBytesSent = 0;
2057
2084
  let videoBytesReceived = 0;
2085
+ let transportSent = 0;
2086
+ let transportReceived = 0;
2087
+ let packetsLost = 0;
2058
2088
 
2059
2089
  stats.forEach(report => {
2060
2090
  if (report.type === 'outbound-rtp' && report.kind === 'audio') {
2091
+ audioBytesSent += report.bytesSent + report.headerBytesSent;
2092
+ }
2093
+ if (report.type === 'remote-outbound-rtp' && report.kind === 'audio') {
2061
2094
  audioBytesSent += report.bytesSent;
2062
2095
  }
2063
2096
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
2064
- videoBytesSent += report.bytesSent;
2097
+ videoBytesSent += report.bytesSent + report.headerBytesSent;
2065
2098
  }
2066
2099
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
2067
- networkStats.packetsLost = report.packetsLost;
2100
+ packetsLost += report.packetsLost;
2068
2101
  networkStats.packetsReceived = report.packetsReceived;
2069
- audioBytesReceived += report.bytesReceived;
2102
+ audioBytesReceived += report.bytesReceived + report.headerBytesReceived;
2070
2103
  }
2071
2104
  if (report.type === 'inbound-rtp' && report.kind === 'video') {
2072
- videoBytesReceived += report.bytesReceived;
2105
+ videoBytesReceived += report.bytesReceived + report.headerBytesReceived;
2073
2106
  }
2074
2107
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
2075
2108
  if ('framesPerSecond' in report) {
@@ -2081,19 +2114,32 @@ export default class WebRTCClient extends Emitter {
2081
2114
  }
2082
2115
  }
2083
2116
  if (report.type === 'remote-inbound-rtp' && report.kind === 'audio') {
2117
+ packetsLost += report.packetsLost;
2084
2118
  networkStats.roundTripTime = report.roundTripTime;
2085
2119
  networkStats.jitter = report.jitter;
2086
2120
  }
2121
+ if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
2122
+ packetsLost += report.packetsLost;
2123
+ }
2124
+ if (report.type === 'transport') {
2125
+ transportSent += report.bytesSent;
2126
+ transportReceived += report.bytesReceived;
2127
+ }
2087
2128
  });
2088
2129
 
2130
+ networkStats.packetsLost = packetsLost;
2089
2131
  networkStats.audioBytesSent = audioBytesSent;
2090
2132
  networkStats.audioBytesReceived = audioBytesReceived;
2091
2133
  networkStats.videoBytesSent = videoBytesSent;
2092
2134
  networkStats.videoBytesReceived = videoBytesReceived;
2135
+ networkStats.transportSent = transportSent;
2136
+ networkStats.transportReceived = transportReceived;
2137
+
2138
+ const bandwidth = (networkStats.audioBytesSent - lastAudioSent) + (networkStats.videoBytesSent - lastVideoSent)
2139
+ + (networkStats.audioBytesReceived - lastAudioReceived) + (networkStats.videoBytesReceived - lastVideoReceived)
2140
+ + (networkStats.transportReceived - lastTransportReceived) + (networkStats.transportSent - lastTransportSent);
2093
2141
 
2094
- networkStats.bandwidth = (networkStats.audioBytesSent - lastAudioSent)
2095
- + (networkStats.videoBytesSent - lastVideoSent) + (networkStats.audioBytesReceived - lastAudioReceived)
2096
- + (networkStats.videoBytesReceived - lastVideoReceived);
2142
+ networkStats.bandwidth = bandwidth;
2097
2143
 
2098
2144
  this.eventEmitter.emit(ON_NETWORK_STATS, session, networkStats, this.sessionNetworkStats[sessionId]);
2099
2145
  this.sessionNetworkStats[sessionId].push(networkStats);