@observertc/observer-js 1.0.1-c3edea3.0 → 1.0.1-c9e7581.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.
Files changed (140) hide show
  1. package/lib/ObservedCall.d.ts +43 -7
  2. package/lib/ObservedCall.d.ts.map +1 -1
  3. package/lib/ObservedCall.js +142 -14
  4. package/lib/ObservedCallEventMonitor.d.ts +104 -0
  5. package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
  6. package/lib/ObservedCallEventMonitor.js +314 -0
  7. package/lib/ObservedCallSummary.d.ts +18 -0
  8. package/lib/ObservedCallSummary.d.ts.map +1 -0
  9. package/lib/ObservedClient.d.ts +71 -11
  10. package/lib/ObservedClient.d.ts.map +1 -1
  11. package/lib/ObservedClient.js +509 -72
  12. package/lib/ObservedClientEventMonitor.d.ts +89 -0
  13. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  14. package/lib/ObservedClientEventMonitor.js +247 -0
  15. package/lib/ObservedClientSummary.d.ts +23 -0
  16. package/lib/ObservedClientSummary.d.ts.map +1 -0
  17. package/lib/ObservedClientSummary.js +2 -0
  18. package/lib/ObservedDataChannel.d.ts +6 -0
  19. package/lib/ObservedDataChannel.d.ts.map +1 -1
  20. package/lib/ObservedDataChannel.js +30 -0
  21. package/lib/ObservedIceCandidatePair.d.ts +6 -1
  22. package/lib/ObservedIceCandidatePair.d.ts.map +1 -1
  23. package/lib/ObservedIceCandidatePair.js +45 -1
  24. package/lib/ObservedIceTransport.d.ts +4 -0
  25. package/lib/ObservedIceTransport.d.ts.map +1 -1
  26. package/lib/ObservedIceTransport.js +28 -0
  27. package/lib/ObservedInboundTrack.d.ts +3 -0
  28. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  29. package/lib/ObservedInboundTrack.js +3 -0
  30. package/lib/ObservedOutboundTrack.d.ts +3 -0
  31. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  32. package/lib/ObservedOutboundTrack.js +3 -0
  33. package/lib/ObservedPeerConnection.d.ts +36 -2
  34. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  35. package/lib/ObservedPeerConnection.js +184 -29
  36. package/lib/ObservedTURN.d.ts +31 -0
  37. package/lib/ObservedTURN.d.ts.map +1 -0
  38. package/lib/ObservedTURN.js +58 -0
  39. package/lib/ObservedTurnServer.d.ts +25 -0
  40. package/lib/ObservedTurnServer.d.ts.map +1 -0
  41. package/lib/ObservedTurnServer.js +59 -0
  42. package/lib/Observer.d.ts +43 -22
  43. package/lib/Observer.d.ts.map +1 -1
  44. package/lib/Observer.js +126 -40
  45. package/lib/ObserverEventMonitor.d.ts +138 -0
  46. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  47. package/lib/ObserverEventMonitor.js +433 -0
  48. package/lib/ObserverSummary.d.ts +13 -0
  49. package/lib/ObserverSummary.d.ts.map +1 -0
  50. package/lib/ObserverSummary.js +2 -0
  51. package/lib/common/utils.d.ts +2 -1
  52. package/lib/common/utils.d.ts.map +1 -1
  53. package/lib/common/utils.js +11 -3
  54. package/lib/index.d.ts +6 -0
  55. package/lib/index.d.ts.map +1 -1
  56. package/lib/index.js +9 -1
  57. package/lib/schema/ClientEventTypes.d.ts +228 -0
  58. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  59. package/lib/schema/ClientEventTypes.js +41 -0
  60. package/lib/schema/{metaTypes.d.ts → ClientMetaTypes.d.ts} +2 -2
  61. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  62. package/lib/schema/ClientMetaTypes.js +16 -0
  63. package/lib/schema/ClientSample.d.ts +1 -1
  64. package/lib/schema/ClientSample.d.ts.map +1 -1
  65. package/lib/updaters/CallUpdater.d.ts.map +1 -0
  66. package/lib/updaters/CallUpdater.js +2 -0
  67. package/lib/updaters/ObserverUpdater.d.ts +5 -0
  68. package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
  69. package/lib/updaters/ObserverUpdater.js +2 -0
  70. package/lib/updaters/OnAllCallObserverUpdater.d.ts +15 -0
  71. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  72. package/lib/updaters/OnAllCallObserverUpdater.js +50 -0
  73. package/lib/updaters/OnAllClientCallUpdater.d.ts +15 -0
  74. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
  75. package/lib/updaters/OnAllClientCallUpdater.js +53 -0
  76. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +12 -0
  77. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  78. package/lib/updaters/OnAnyCallObserverUpdater.js +37 -0
  79. package/lib/updaters/OnAnyClientCallUpdater.d.ts +12 -0
  80. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
  81. package/lib/updaters/OnAnyClientCallUpdater.js +36 -0
  82. package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
  83. package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
  84. package/lib/updaters/OnIntervalUpdater.js +17 -0
  85. package/lib/updaters/Updater.d.ts +6 -0
  86. package/lib/updaters/Updater.d.ts.map +1 -0
  87. package/lib/updaters/Updater.js +2 -0
  88. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  89. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  90. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  91. package/package.json +1 -1
  92. package/src/ObservedCall.ts +222 -17
  93. package/src/ObservedCallEventMonitor.ts +392 -0
  94. package/src/ObservedCallSummary.ts +22 -0
  95. package/src/ObservedClient.ts +608 -79
  96. package/src/ObservedClientEventMonitor.ts +301 -0
  97. package/src/ObservedClientSummary.ts +24 -0
  98. package/src/ObservedDataChannel.ts +32 -0
  99. package/src/ObservedIceCandidatePair.ts +44 -2
  100. package/src/ObservedIceTransport.ts +29 -0
  101. package/src/ObservedInboundTrack.ts +5 -0
  102. package/src/ObservedOutboundTrack.ts +5 -0
  103. package/src/ObservedPeerConnection.ts +219 -46
  104. package/src/ObservedTURN.ts +86 -0
  105. package/src/ObservedTurnServer.ts +72 -0
  106. package/src/Observer.ts +179 -64
  107. package/src/ObserverEventMonitor.ts +551 -0
  108. package/src/ObserverSummary.ts +15 -0
  109. package/src/common/utils.ts +12 -2
  110. package/src/index.ts +6 -1
  111. package/src/schema/ClientEventTypes.ts +280 -0
  112. package/src/schema/{metaTypes.ts → ClientMetaTypes.ts} +1 -1
  113. package/src/schema/ClientSample.ts +1 -1
  114. package/src/updaters/ObserverUpdater.ts +5 -0
  115. package/src/updaters/OnAllCallObserverUpdater.ts +59 -0
  116. package/src/updaters/OnAllClientCallUpdater.ts +61 -0
  117. package/src/updaters/OnAnyCallObserverUpdater.ts +41 -0
  118. package/src/updaters/OnAnyClientCallUpdater.ts +39 -0
  119. package/src/updaters/OnIntervalUpdater.ts +19 -0
  120. package/src/updaters/Updater.ts +5 -0
  121. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  122. package/tsconfig.json +1 -1
  123. package/lib/schema/eventTypes.d.ts +0 -33
  124. package/lib/schema/eventTypes.d.ts.map +0 -1
  125. package/lib/schema/eventTypes.js +0 -38
  126. package/lib/schema/metaTypes.d.ts.map +0 -1
  127. package/lib/schema/metaTypes.js +0 -16
  128. package/lib/utils/CallUpdater.d.ts.map +0 -1
  129. package/lib/utils/OnAllClientCallUpdater.d.ts +0 -16
  130. package/lib/utils/OnAllClientCallUpdater.d.ts.map +0 -1
  131. package/lib/utils/OnAllClientCallUpdater.js +0 -58
  132. package/lib/utils/onAnyClientCallUpdater.d.ts +0 -14
  133. package/lib/utils/onAnyClientCallUpdater.d.ts.map +0 -1
  134. package/lib/utils/onAnyClientCallUpdater.js +0 -51
  135. package/src/schema/eventTypes.ts +0 -36
  136. package/src/utils/OnAllClientCallUpdater.ts +0 -68
  137. package/src/utils/onAnyClientCallUpdater.ts +0 -59
  138. /package/lib/{utils/CallUpdater.js → ObservedCallSummary.js} +0 -0
  139. /package/lib/{utils → updaters}/CallUpdater.d.ts +0 -0
  140. /package/src/{utils → updaters}/CallUpdater.ts +0 -0
@@ -19,6 +19,8 @@ import { ObservedRemoteOutboundRtp } from './ObservedRemoteOutboundRtp';
19
19
  import { ObservedInboundTrack } from './ObservedInboundTrack';
20
20
  import { ObservedOutboundTrack } from './ObservedOutboundTrack';
21
21
  import { CalculatedScore } from './scores/CalculatedScore';
22
+ import { ObservedTurnServer } from './ObservedTurnServer';
23
+ import { getMedian } from './common/utils';
22
24
 
23
25
  const logger = createLogger('ObservedPeerConnection');
24
26
 
@@ -70,7 +72,27 @@ export type ObservedPeerConnectionEvents = {
70
72
  'removed-peer-connection-transport': [ObservedPeerConnectionTransport];
71
73
  'removed-remote-inbound-rtp': [ObservedRemoteInboundRtp];
72
74
  'removed-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
73
-
75
+ 'updated-inbound-rtp': [ObservedInboundRtp];
76
+ 'updated-outbound-rtp': [ObservedOutboundRtp];
77
+ 'updated-inbound-track': [ObservedInboundTrack];
78
+ 'updated-outbound-track': [ObservedOutboundTrack];
79
+ 'updated-ice-candidate-pair': [ObservedIceCandidatePair];
80
+ 'updated-ice-transport': [ObservedIceTransport];
81
+ 'updated-peer-connection-transport': [ObservedPeerConnectionTransport];
82
+ 'updated-media-source': [ObservedMediaSource];
83
+ 'updated-media-playout': [ObservedMediaPlayout];
84
+ 'updated-data-channel': [ObservedDataChannel];
85
+ 'updated-ice-candidate': [ObservedIceCandidate];
86
+ 'updated-certificate': [ObservedCertificate];
87
+ 'updated-codec': [ObservedCodec];
88
+ 'updated-remote-inbound-rtp': [ObservedRemoteInboundRtp];
89
+ 'updated-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
90
+ 'muted-inbound-track': [ObservedInboundTrack];
91
+ 'muted-outbound-track': [ObservedOutboundTrack];
92
+ 'unmuted-inbound-track': [ObservedInboundTrack];
93
+ 'unmuted-outbound-track': [ObservedOutboundTrack];
94
+
95
+ 'update': [],
74
96
  close: [];
75
97
  };
76
98
 
@@ -106,11 +128,19 @@ export class ObservedPeerConnection extends EventEmitter {
106
128
  public closedAt?: number;
107
129
  public updated = Date.now();
108
130
 
131
+ public connectionState?: string;
132
+ public iceConnectionState?: string;
133
+ public iceGatheringState?: string;
134
+
135
+ public availableIncomingBitrate = 0;
136
+ public availableOutgoingBitrate = 0;
109
137
  public totalInboundPacketsLost = 0;
110
138
  public totalInboundPacketsReceived = 0;
111
139
  public totalOutboundPacketsSent = 0;
112
140
  public totalDataChannelBytesSent = 0;
113
141
  public totalDataChannelBytesReceived = 0;
142
+ public totalDataChannelMessagesSent = 0;
143
+ public totalDataChannelMessagesReceived = 0;
114
144
 
115
145
  public totalSentAudioBytes = 0;
116
146
  public totalSentVideoBytes = 0;
@@ -126,6 +156,8 @@ export class ObservedPeerConnection extends EventEmitter {
126
156
  public deltaOutboundPacketsSent = 0;
127
157
  public deltaDataChannelBytesSent = 0;
128
158
  public deltaDataChannelBytesReceived = 0;
159
+ public deltaDataChannelMessagesSent = 0;
160
+ public deltaDataChannelMessagesReceived = 0;
129
161
  public deltaInboundReceivedBytes = 0;
130
162
  public deltaOutboundSentBytes = 0;
131
163
 
@@ -135,6 +167,8 @@ export class ObservedPeerConnection extends EventEmitter {
135
167
  public deltaReceivedVideoPackets = 0;
136
168
  public deltaSentAudioBytes = 0;
137
169
  public deltaSentVideoBytes = 0;
170
+ public deltaTransportSentBytes = 0;
171
+ public deltaTransportReceivedBytes = 0;
138
172
 
139
173
  public receivingPacketsPerSecond = 0;
140
174
  public sendingPacketsPerSecond = 0;
@@ -143,12 +177,13 @@ export class ObservedPeerConnection extends EventEmitter {
143
177
  public receivingAudioBitrate = 0;
144
178
  public receivingVideoBitrate = 0;
145
179
 
146
- public avgRttInMs?: number;
147
- public avgJitter?: number;
180
+ public currentRttInMs?: number;
181
+ public currentJitter?: number;
148
182
 
149
183
  public usingTCP = false;
150
184
  public usingTURN = false;
151
185
 
186
+ public observedTurnServer?: ObservedTurnServer;
152
187
  public readonly observedCertificates = new Map<string, ObservedCertificate>();
153
188
  public readonly observedCodecs = new Map<string, ObservedCodec>();
154
189
  public readonly observedDataChannels = new Map<string, ObservedDataChannel>();
@@ -239,6 +274,14 @@ export class ObservedPeerConnection extends EventEmitter {
239
274
  .filter((pair) => pair !== undefined) as ObservedIceCandidatePair[];
240
275
  }
241
276
 
277
+ public get selectedIceCandiadtePairForTurn() {
278
+ return this.selectedIceCandidatePairs
279
+ .filter((pair) =>
280
+ pair.getLocalCandidate()?.candidateType === 'relay' &&
281
+ pair.getRemoteCandidate()?.url?.startsWith('turn:')
282
+ );
283
+ }
284
+
242
285
  public close() {
243
286
  if (this.closed) return;
244
287
  this.closed = true;
@@ -259,6 +302,10 @@ export class ObservedPeerConnection extends EventEmitter {
259
302
  this.observedRemoteInboundRtps.clear();
260
303
  this.observedRemoteOutboundRtps.clear();
261
304
 
305
+ this.client.call.observer.observedTURN.removePeerConnection(this);
306
+
307
+ if (!this.closedAt) this.closedAt = Date.now();
308
+
262
309
  this.emit('close');
263
310
  }
264
311
 
@@ -266,6 +313,8 @@ export class ObservedPeerConnection extends EventEmitter {
266
313
  if (this.closed) return;
267
314
  this._visited = true;
268
315
 
316
+ this.availableIncomingBitrate = 0;
317
+ this.availableOutgoingBitrate = 0;
269
318
  this.deltaInboundPacketsLost = 0;
270
319
  this.deltaInboundPacketsReceived = 0;
271
320
  this.deltaOutboundPacketsSent = 0;
@@ -279,6 +328,9 @@ export class ObservedPeerConnection extends EventEmitter {
279
328
  this.deltaReceivedVideoPackets = 0;
280
329
  this.deltaSentAudioBytes = 0;
281
330
  this.deltaSentVideoBytes = 0;
331
+ this.deltaTransportReceivedBytes = 0;
332
+ this.deltaTransportSentBytes = 0;
333
+
282
334
  this.sendingAudioBitrate = 0;
283
335
  this.sendingVideoBitrate = 0;
284
336
  this.receivingAudioBitrate = 0;
@@ -287,6 +339,8 @@ export class ObservedPeerConnection extends EventEmitter {
287
339
  const now = Date.now();
288
340
  const elapsedTimeInMs = now - this.updated;
289
341
  const elapsedTimeInSec = elapsedTimeInMs / 1000;
342
+ const rttMeasurementsInSec: number[] = [];
343
+ const jitterMeasurements: number[] = [];
290
344
 
291
345
  if (sample.certificates) {
292
346
  for (const certificate of sample.certificates) {
@@ -300,7 +354,14 @@ export class ObservedPeerConnection extends EventEmitter {
300
354
  }
301
355
  if (sample.dataChannels) {
302
356
  for (const dataChannel of sample.dataChannels) {
303
- this._updateDataChannelStats(dataChannel);
357
+ const observedDataChannel = this._updateDataChannelStats(dataChannel);
358
+
359
+ if (!observedDataChannel) continue;
360
+
361
+ this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent;
362
+ this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived;
363
+ this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent;
364
+ this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived;
304
365
  }
305
366
  }
306
367
  if (sample.iceCandidates) {
@@ -310,12 +371,29 @@ export class ObservedPeerConnection extends EventEmitter {
310
371
  }
311
372
  if (sample.iceCandidatePairs) {
312
373
  for (const iceCandidatePair of sample.iceCandidatePairs) {
313
- this._updateIceCandidatePairStats(iceCandidatePair);
374
+ const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair);
375
+
376
+ if (!observedCandidatePair) continue;
377
+
378
+ if (observedCandidatePair.currentRoundTripTime) {
379
+ rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime);
380
+ }
381
+ if (observedCandidatePair.availableIncomingBitrate) {
382
+ this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate;
383
+ }
384
+ if (observedCandidatePair.availableOutgoingBitrate) {
385
+ this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate;
386
+ }
314
387
  }
315
388
  }
316
389
  if (sample.iceTransports) {
317
390
  for (const iceTransport of sample.iceTransports) {
318
- this._updateIceTransportStats(iceTransport);
391
+ const observedIceTransport = this._updateIceTransportStats(iceTransport);
392
+
393
+ if (!observedIceTransport) return;
394
+
395
+ observedIceTransport.bytesReceived;
396
+
319
397
  }
320
398
  }
321
399
  if (sample.inboundRtps) {
@@ -338,6 +416,10 @@ export class ObservedPeerConnection extends EventEmitter {
338
416
  this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
339
417
  break;
340
418
  }
419
+
420
+ if (observedInboundRtp.jitter) {
421
+ jitterMeasurements.push(observedInboundRtp.jitter);
422
+ }
341
423
  }
342
424
  }
343
425
  if (sample.mediaPlayouts) {
@@ -374,12 +456,21 @@ export class ObservedPeerConnection extends EventEmitter {
374
456
  }
375
457
  if (sample.peerConnectionTransports) {
376
458
  for (const peerConnectionTransport of sample.peerConnectionTransports) {
377
- this._updatePeerConnectionTransportStats(peerConnectionTransport);
459
+ const observedTransport = this._updatePeerConnectionTransportStats(peerConnectionTransport);
460
+
461
+ if (!observedTransport) continue;
462
+
378
463
  }
379
464
  }
380
465
  if (sample.remoteInboundRtps) {
381
466
  for (const remoteInboundRtp of sample.remoteInboundRtps) {
382
- this._updateRemoteInboundRtpStats(remoteInboundRtp);
467
+ const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp);
468
+
469
+ if (!observedRemoteInboundRtp) continue;
470
+
471
+ if (observedRemoteInboundRtp.roundTripTime) {
472
+ rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime);
473
+ }
383
474
  }
384
475
  }
385
476
  if (sample.remoteOutboundRtps) {
@@ -408,6 +499,8 @@ export class ObservedPeerConnection extends EventEmitter {
408
499
  this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
409
500
  this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
410
501
  this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
502
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
503
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
411
504
  this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
412
505
  this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
413
506
  this.totalSentAudioBytes += this.deltaSentAudioBytes;
@@ -419,20 +512,55 @@ export class ObservedPeerConnection extends EventEmitter {
419
512
 
420
513
  this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
421
514
  this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec;
422
- this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedTimeInSec;
423
- this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedTimeInSec;
424
- this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedTimeInSec;
425
- this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedTimeInSec;
426
-
515
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / elapsedTimeInSec;
516
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / elapsedTimeInSec;
517
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / elapsedTimeInSec;
518
+ this.receivingVideoBitrate = (this.deltaReceivedVideoBytes * 8) / elapsedTimeInSec;
519
+
520
+ if (rttMeasurementsInSec.length > 0) {
521
+ this.currentRttInMs = getMedian(rttMeasurementsInSec, false) * 1000;
522
+ } else {
523
+ this.currentRttInMs = undefined;
524
+ }
525
+ if (jitterMeasurements.length > 0) {
526
+ this.currentJitter = getMedian(jitterMeasurements, false);
527
+ } else {
528
+ this.currentJitter = undefined;
529
+ }
530
+ const wasUsingTURN = this.usingTURN;
427
531
  const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
532
+ const selectedCandidatePairForTurn: ObservedIceCandidatePair[] = [];
533
+
534
+ this.usingTCP = false;
535
+ this.usingTURN = false;
428
536
 
429
- this.usingTCP = selectedIceCandidatePairs.some((pair) => pair.getLocalCandidate()?.protocol === 'tcp');
430
- this.usingTURN = selectedIceCandidatePairs.some((pair) => pair.getLocalCandidate()?.candidateType === 'relay') &&
431
- selectedIceCandidatePairs.some((pair) => pair.getRemoteCandidate()?.url?.startsWith('turn:'));
537
+ for (const selectedCandidatePair of selectedIceCandidatePairs) {
538
+ if (selectedCandidatePair.getLocalCandidate()?.protocol === 'tcp') {
539
+ this.usingTCP = true;
540
+ }
541
+ if (selectedCandidatePair.getLocalCandidate()?.candidateType === 'relay' && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith('turn:')) {
542
+ selectedCandidatePairForTurn.push(selectedCandidatePair);
543
+ this.usingTURN = true;
544
+ }
545
+ this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived;
546
+ this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent;
547
+ }
432
548
 
549
+ if (this.usingTURN) {
550
+ if (!this.observedTurnServer) {
551
+ this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this);
552
+ }
553
+ this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn);
554
+ } else if (wasUsingTURN) {
555
+ if (!this.usingTURN) {
556
+ this.client.call.observer.observedTURN.removePeerConnection(this);
557
+ }
558
+ }
433
559
  this.calculatedScore.value = sample.score;
434
560
  this.updated = now;
435
561
  this._checkVisited();
562
+
563
+ this.emit('update');
436
564
  }
437
565
 
438
566
  private _checkVisited() {
@@ -540,12 +668,15 @@ export class ObservedPeerConnection extends EventEmitter {
540
668
 
541
669
  observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
542
670
 
671
+ observedCertificate.update(stats);
672
+
543
673
  this.observedCertificates.set(stats.id, observedCertificate);
544
674
  this.emit('added-certificate', observedCertificate);
675
+ } else {
676
+ observedCertificate.update(stats);
677
+ this.emit('updated-certificate', observedCertificate);
545
678
  }
546
679
 
547
- observedCertificate.update(stats);
548
-
549
680
  return observedCertificate;
550
681
  }
551
682
 
@@ -562,11 +693,14 @@ export class ObservedPeerConnection extends EventEmitter {
562
693
 
563
694
  observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
564
695
 
696
+ observedCodec.update(stats);
697
+
565
698
  this.observedCodecs.set(stats.id, observedCodec);
566
699
  this.emit('added-codec', observedCodec);
700
+ } else {
701
+ observedCodec.update(stats);
567
702
  }
568
-
569
- observedCodec.update(stats);
703
+ this.emit('updated-codec', observedCodec);
570
704
 
571
705
  return observedCodec;
572
706
  }
@@ -584,11 +718,14 @@ export class ObservedPeerConnection extends EventEmitter {
584
718
 
585
719
  observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
586
720
 
721
+ observedDataChannel.update(stats);
722
+
587
723
  this.observedDataChannels.set(stats.id, observedDataChannel);
588
724
  this.emit('added-data-channel', observedDataChannel);
725
+ } else {
726
+ observedDataChannel.update(stats);
589
727
  }
590
-
591
- observedDataChannel.update(stats);
728
+ this.emit('updated-data-channel', observedDataChannel);
592
729
 
593
730
  return observedDataChannel;
594
731
  }
@@ -606,11 +743,14 @@ export class ObservedPeerConnection extends EventEmitter {
606
743
 
607
744
  observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
608
745
 
746
+ observedIceCandidate.update(stats);
747
+
609
748
  this.observedIceCandidates.set(stats.id, observedIceCandidate);
610
749
  this.emit('added-ice-candidate', observedIceCandidate);
750
+ } else {
751
+ observedIceCandidate.update(stats);
611
752
  }
612
-
613
- observedIceCandidate.update(stats);
753
+ this.emit('updated-ice-candidate', observedIceCandidate);
614
754
 
615
755
  return observedIceCandidate;
616
756
  }
@@ -628,11 +768,14 @@ export class ObservedPeerConnection extends EventEmitter {
628
768
 
629
769
  observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
630
770
 
771
+ observedIceCandidatePair.update(stats);
772
+
631
773
  this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
632
774
  this.emit('added-ice-candidate-pair', observedIceCandidatePair);
775
+ } else {
776
+ observedIceCandidatePair.update(stats);
633
777
  }
634
-
635
- observedIceCandidatePair.update(stats);
778
+ this.emit('updated-ice-candidate-pair', observedIceCandidatePair);
636
779
 
637
780
  return observedIceCandidatePair;
638
781
  }
@@ -650,11 +793,14 @@ export class ObservedPeerConnection extends EventEmitter {
650
793
 
651
794
  observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
652
795
 
796
+ observedIceTransport.update(stats);
797
+
653
798
  this.observedIceTransports.set(stats.id, observedIceTransport);
654
799
  this.emit('added-ice-transport', observedIceTransport);
800
+ } else {
801
+ observedIceTransport.update(stats);
655
802
  }
656
-
657
- observedIceTransport.update(stats);
803
+ this.emit('updated-ice-transport', observedIceTransport);
658
804
 
659
805
  return observedIceTransport;
660
806
  }
@@ -678,12 +824,15 @@ export class ObservedPeerConnection extends EventEmitter {
678
824
  stats.trackIdentifier,
679
825
  this
680
826
  );
827
+
828
+ observedInboundRtp.update(stats);
681
829
 
682
830
  this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
683
831
  this.emit('added-inbound-rtp', observedInboundRtp);
832
+ } else {
833
+ observedInboundRtp.update(stats);
684
834
  }
685
-
686
- observedInboundRtp.update(stats);
835
+ this.emit('updated-inbound-rtp', observedInboundRtp);
687
836
 
688
837
  return observedInboundRtp;
689
838
  }
@@ -712,11 +861,14 @@ export class ObservedPeerConnection extends EventEmitter {
712
861
  mediaPlayout,
713
862
  );
714
863
 
864
+ observedInboundTrack.update(stats);
865
+
715
866
  this.observedInboundTracks.set(stats.id, observedInboundTrack);
716
867
  this.emit('added-inbound-track', observedInboundTrack);
868
+ } else {
869
+ observedInboundTrack.update(stats);
717
870
  }
718
-
719
- observedInboundTrack.update(stats);
871
+ this.emit('updated-inbound-track', observedInboundTrack);
720
872
 
721
873
  return observedInboundTrack;
722
874
  }
@@ -739,11 +891,14 @@ export class ObservedPeerConnection extends EventEmitter {
739
891
  this
740
892
  );
741
893
 
894
+ observedMediaPlayout.update(stats);
895
+
742
896
  this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
743
897
  this.emit('added-media-playout', observedMediaPlayout);
898
+ } else {
899
+ observedMediaPlayout.update(stats);
744
900
  }
745
-
746
- observedMediaPlayout.update(stats);
901
+ this.emit('updated-media-playout', observedMediaPlayout);
747
902
 
748
903
  return observedMediaPlayout;
749
904
  }
@@ -766,11 +921,14 @@ export class ObservedPeerConnection extends EventEmitter {
766
921
  this
767
922
  );
768
923
 
924
+ observedMediaSource.update(stats);
925
+
769
926
  this.observedMediaSources.set(stats.id, observedMediaSource);
770
927
  this.emit('added-media-source', observedMediaSource);
928
+ } else {
929
+ observedMediaSource.update(stats);
771
930
  }
772
-
773
- observedMediaSource.update(stats);
931
+ this.emit('updated-media-source', observedMediaSource);
774
932
 
775
933
  return observedMediaSource;
776
934
  }
@@ -794,11 +952,14 @@ export class ObservedPeerConnection extends EventEmitter {
794
952
  this
795
953
  );
796
954
 
955
+ observedOutboundRtp.update(stats);
956
+
797
957
  this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
798
958
  this.emit('added-outbound-rtp', observedOutboundRtp);
959
+ } else {
960
+ observedOutboundRtp.update(stats);
799
961
  }
800
-
801
- observedOutboundRtp.update(stats);
962
+ this.emit('updated-outbound-rtp', observedOutboundRtp);
802
963
 
803
964
  return observedOutboundRtp;
804
965
  }
@@ -826,11 +987,14 @@ export class ObservedPeerConnection extends EventEmitter {
826
987
  observedMediaSource,
827
988
  );
828
989
 
990
+ observedOutboundTrack.update(stats);
991
+
829
992
  this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
830
993
  this.emit('added-outbound-track', observedOutboundTrack);
994
+ } else {
995
+ observedOutboundTrack.update(stats);
831
996
  }
832
-
833
- observedOutboundTrack.update(stats);
997
+ this.emit('updated-outbound-track', observedOutboundTrack);
834
998
 
835
999
  return observedOutboundTrack;
836
1000
  }
@@ -848,11 +1012,14 @@ export class ObservedPeerConnection extends EventEmitter {
848
1012
 
849
1013
  observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
850
1014
 
1015
+ observedPeerConnectionTransport.update(stats);
1016
+
851
1017
  this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
852
1018
  this.emit('added-peer-connection-transport', observedPeerConnectionTransport);
1019
+ } else {
1020
+ observedPeerConnectionTransport.update(stats);
853
1021
  }
854
-
855
- observedPeerConnectionTransport.update(stats);
1022
+ this.emit('updated-peer-connection-transport', observedPeerConnectionTransport);
856
1023
 
857
1024
  return observedPeerConnectionTransport;
858
1025
  }
@@ -876,11 +1043,14 @@ export class ObservedPeerConnection extends EventEmitter {
876
1043
  this
877
1044
  );
878
1045
 
1046
+ observedRemoteInboundRtp.update(stats);
1047
+
879
1048
  this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
880
1049
  this.emit('added-remote-inbound-rtp', observedRemoteInboundRtp);
1050
+ } else {
1051
+ observedRemoteInboundRtp.update(stats);
881
1052
  }
882
-
883
- observedRemoteInboundRtp.update(stats);
1053
+ this.emit('updated-remote-inbound-rtp', observedRemoteInboundRtp);
884
1054
 
885
1055
  return observedRemoteInboundRtp;
886
1056
  }
@@ -904,11 +1074,14 @@ export class ObservedPeerConnection extends EventEmitter {
904
1074
  this
905
1075
  );
906
1076
 
1077
+ observedRemoteOutboundRtp.update(stats);
1078
+
907
1079
  this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
908
1080
  this.emit('added-remote-outbound-rtp', observedRemoteOutboundRtp);
1081
+ } else {
1082
+ observedRemoteOutboundRtp.update(stats);
909
1083
  }
910
-
911
- observedRemoteOutboundRtp.update(stats);
1084
+ this.emit('updated-remote-outbound-rtp', observedRemoteOutboundRtp);
912
1085
 
913
1086
  return observedRemoteOutboundRtp;
914
1087
  }
@@ -0,0 +1,86 @@
1
+ import { EventEmitter } from 'stream';
2
+ import { ObservedPeerConnection } from './ObservedPeerConnection';
3
+ import { ObservedTurnServer } from './ObservedTurnServer';
4
+ import { createLogger } from './common/logger';
5
+
6
+ const logger = createLogger('ObservedTURN');
7
+
8
+ export type ObservedTURNEventMap = {
9
+ 'update': [ObservedTURN];
10
+ 'close': [];
11
+ }
12
+
13
+ export declare interface ObservedTURN {
14
+ on<U extends keyof ObservedTURNEventMap>(event: U, listener: (...args: ObservedTURNEventMap[U]) => void): this;
15
+ off<U extends keyof ObservedTURNEventMap>(event: U, listener: (...args: ObservedTURNEventMap[U]) => void): this;
16
+ once<U extends keyof ObservedTURNEventMap>(event: U, listener: (...args: ObservedTURNEventMap[U]) => void): this;
17
+ emit<U extends keyof ObservedTURNEventMap>(event: U, ...args: ObservedTURNEventMap[U]): boolean;
18
+ }
19
+
20
+ export class ObservedTURN extends EventEmitter {
21
+ public totalBytesSent = 0;
22
+ public totalBytesReceived = 0;
23
+ public totalPacketsSent = 0;
24
+ public totalPacketsReceived = 0;
25
+
26
+ public packetsSentPerSecond = 0;
27
+ public packetsReceivedPerSecond = 0;
28
+ public outboundBitrate = 0;
29
+ public inboundBitrate = 0;
30
+ public numberOfClients = 0;
31
+
32
+ public readonly servers = new Map<string, ObservedTurnServer>();
33
+
34
+ public constructor(
35
+ ) {
36
+ super();
37
+ }
38
+
39
+ public update() {
40
+ const clientIds = new Set<string>();
41
+
42
+ this.packetsReceivedPerSecond = 0;
43
+ this.packetsSentPerSecond = 0;
44
+ this.outboundBitrate = 0;
45
+ this.inboundBitrate = 0;
46
+
47
+ for (const server of this.servers.values()) {
48
+ server.update();
49
+ server.observedPeerConnections.forEach((pc) => clientIds.add(pc.client.clientId));
50
+ }
51
+
52
+ this.numberOfClients = clientIds.size;
53
+ }
54
+
55
+ public addPeerConnection(peerConnection: ObservedPeerConnection) {
56
+ const turnPairs = peerConnection.selectedIceCandidatePairs.filter((pair) => pair.getLocalCandidate()?.candidateType === 'relay' && pair.getRemoteCandidate()?.url?.startsWith('turn:'));
57
+
58
+ if (turnPairs.length !== 1) {
59
+ return (logger.warn(`Expected exactly one TURN pair, but found for peerconnection ${peerConnection.peerConnectionId}`, turnPairs.length), undefined);
60
+ }
61
+
62
+ const candidatePair = turnPairs[0];
63
+ const rawUrl = candidatePair.getRemoteCandidate()?.url;
64
+
65
+ if (!rawUrl) {
66
+ return (logger.warn(`No remote candidate URL found for peerconnection ${peerConnection.peerConnectionId}`), undefined);
67
+ }
68
+
69
+ const turnUrl = new URL(rawUrl);
70
+ const turnServerUrl = `${turnUrl.protocol}//${turnUrl.hostname}:${turnUrl.port}`;
71
+ let turnServer = this.servers.get(turnServerUrl);
72
+
73
+ if (!turnServer) {
74
+ turnServer = new ObservedTurnServer(turnServerUrl, this);
75
+ }
76
+
77
+ return turnServer;
78
+ }
79
+
80
+ public removePeerConnection(peerConnection: ObservedPeerConnection) {
81
+ for (const turnServer of this.servers.values()) {
82
+ turnServer.observedPeerConnections.delete(peerConnection.peerConnectionId);
83
+ }
84
+ }
85
+
86
+ }