@observertc/observer-js 1.0.1-c3edea3.0 → 1.0.1-cda37f7.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 +129 -13
  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 +70 -11
  10. package/lib/ObservedClient.d.ts.map +1 -1
  11. package/lib/ObservedClient.js +392 -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 +2 -0
  28. package/lib/ObservedInboundTrack.d.ts.map +1 -1
  29. package/lib/ObservedInboundTrack.js +2 -0
  30. package/lib/ObservedOutboundTrack.d.ts +2 -0
  31. package/lib/ObservedOutboundTrack.d.ts.map +1 -1
  32. package/lib/ObservedOutboundTrack.js +2 -0
  33. package/lib/ObservedPeerConnection.d.ts +29 -2
  34. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  35. package/lib/ObservedPeerConnection.js +181 -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 +130 -0
  46. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  47. package/lib/ObserverEventMonitor.js +404 -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 +160 -0
  58. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  59. package/lib/schema/ClientEventTypes.js +38 -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 +210 -16
  93. package/src/ObservedCallEventMonitor.ts +392 -0
  94. package/src/ObservedCallSummary.ts +22 -0
  95. package/src/ObservedClient.ts +490 -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 +3 -0
  102. package/src/ObservedOutboundTrack.ts +3 -0
  103. package/src/ObservedPeerConnection.ts +210 -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 +518 -0
  108. package/src/ObserverSummary.ts +15 -0
  109. package/src/common/utils.ts +12 -2
  110. package/src/index.ts +6 -0
  111. package/src/schema/ClientEventTypes.ts +176 -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,22 @@ 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
+ 'update': [],
74
91
  close: [];
75
92
  };
76
93
 
@@ -106,11 +123,15 @@ export class ObservedPeerConnection extends EventEmitter {
106
123
  public closedAt?: number;
107
124
  public updated = Date.now();
108
125
 
126
+ public availableIncomingBitrate = 0;
127
+ public availableOutgoingBitrate = 0;
109
128
  public totalInboundPacketsLost = 0;
110
129
  public totalInboundPacketsReceived = 0;
111
130
  public totalOutboundPacketsSent = 0;
112
131
  public totalDataChannelBytesSent = 0;
113
132
  public totalDataChannelBytesReceived = 0;
133
+ public totalDataChannelMessagesSent = 0;
134
+ public totalDataChannelMessagesReceived = 0;
114
135
 
115
136
  public totalSentAudioBytes = 0;
116
137
  public totalSentVideoBytes = 0;
@@ -126,6 +147,8 @@ export class ObservedPeerConnection extends EventEmitter {
126
147
  public deltaOutboundPacketsSent = 0;
127
148
  public deltaDataChannelBytesSent = 0;
128
149
  public deltaDataChannelBytesReceived = 0;
150
+ public deltaDataChannelMessagesSent = 0;
151
+ public deltaDataChannelMessagesReceived = 0;
129
152
  public deltaInboundReceivedBytes = 0;
130
153
  public deltaOutboundSentBytes = 0;
131
154
 
@@ -135,6 +158,8 @@ export class ObservedPeerConnection extends EventEmitter {
135
158
  public deltaReceivedVideoPackets = 0;
136
159
  public deltaSentAudioBytes = 0;
137
160
  public deltaSentVideoBytes = 0;
161
+ public deltaTransportSentBytes = 0;
162
+ public deltaTransportReceivedBytes = 0;
138
163
 
139
164
  public receivingPacketsPerSecond = 0;
140
165
  public sendingPacketsPerSecond = 0;
@@ -143,12 +168,13 @@ export class ObservedPeerConnection extends EventEmitter {
143
168
  public receivingAudioBitrate = 0;
144
169
  public receivingVideoBitrate = 0;
145
170
 
146
- public avgRttInMs?: number;
147
- public avgJitter?: number;
171
+ public currentRttInMs?: number;
172
+ public currentJitter?: number;
148
173
 
149
174
  public usingTCP = false;
150
175
  public usingTURN = false;
151
176
 
177
+ public observedTurnServer?: ObservedTurnServer;
152
178
  public readonly observedCertificates = new Map<string, ObservedCertificate>();
153
179
  public readonly observedCodecs = new Map<string, ObservedCodec>();
154
180
  public readonly observedDataChannels = new Map<string, ObservedDataChannel>();
@@ -239,6 +265,14 @@ export class ObservedPeerConnection extends EventEmitter {
239
265
  .filter((pair) => pair !== undefined) as ObservedIceCandidatePair[];
240
266
  }
241
267
 
268
+ public get selectedIceCandiadtePairForTurn() {
269
+ return this.selectedIceCandidatePairs
270
+ .filter((pair) =>
271
+ pair.getLocalCandidate()?.candidateType === 'relay' &&
272
+ pair.getRemoteCandidate()?.url?.startsWith('turn:')
273
+ );
274
+ }
275
+
242
276
  public close() {
243
277
  if (this.closed) return;
244
278
  this.closed = true;
@@ -259,6 +293,10 @@ export class ObservedPeerConnection extends EventEmitter {
259
293
  this.observedRemoteInboundRtps.clear();
260
294
  this.observedRemoteOutboundRtps.clear();
261
295
 
296
+ this.client.call.observer.observedTURN.removePeerConnection(this);
297
+
298
+ if (!this.closedAt) this.closedAt = Date.now();
299
+
262
300
  this.emit('close');
263
301
  }
264
302
 
@@ -266,6 +304,8 @@ export class ObservedPeerConnection extends EventEmitter {
266
304
  if (this.closed) return;
267
305
  this._visited = true;
268
306
 
307
+ this.availableIncomingBitrate = 0;
308
+ this.availableOutgoingBitrate = 0;
269
309
  this.deltaInboundPacketsLost = 0;
270
310
  this.deltaInboundPacketsReceived = 0;
271
311
  this.deltaOutboundPacketsSent = 0;
@@ -279,6 +319,9 @@ export class ObservedPeerConnection extends EventEmitter {
279
319
  this.deltaReceivedVideoPackets = 0;
280
320
  this.deltaSentAudioBytes = 0;
281
321
  this.deltaSentVideoBytes = 0;
322
+ this.deltaTransportReceivedBytes = 0;
323
+ this.deltaTransportSentBytes = 0;
324
+
282
325
  this.sendingAudioBitrate = 0;
283
326
  this.sendingVideoBitrate = 0;
284
327
  this.receivingAudioBitrate = 0;
@@ -287,6 +330,8 @@ export class ObservedPeerConnection extends EventEmitter {
287
330
  const now = Date.now();
288
331
  const elapsedTimeInMs = now - this.updated;
289
332
  const elapsedTimeInSec = elapsedTimeInMs / 1000;
333
+ const rttMeasurementsInSec: number[] = [];
334
+ const jitterMeasurements: number[] = [];
290
335
 
291
336
  if (sample.certificates) {
292
337
  for (const certificate of sample.certificates) {
@@ -300,7 +345,14 @@ export class ObservedPeerConnection extends EventEmitter {
300
345
  }
301
346
  if (sample.dataChannels) {
302
347
  for (const dataChannel of sample.dataChannels) {
303
- this._updateDataChannelStats(dataChannel);
348
+ const observedDataChannel = this._updateDataChannelStats(dataChannel);
349
+
350
+ if (!observedDataChannel) continue;
351
+
352
+ this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent;
353
+ this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived;
354
+ this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent;
355
+ this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived;
304
356
  }
305
357
  }
306
358
  if (sample.iceCandidates) {
@@ -310,12 +362,29 @@ export class ObservedPeerConnection extends EventEmitter {
310
362
  }
311
363
  if (sample.iceCandidatePairs) {
312
364
  for (const iceCandidatePair of sample.iceCandidatePairs) {
313
- this._updateIceCandidatePairStats(iceCandidatePair);
365
+ const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair);
366
+
367
+ if (!observedCandidatePair) continue;
368
+
369
+ if (observedCandidatePair.currentRoundTripTime) {
370
+ rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime);
371
+ }
372
+ if (observedCandidatePair.availableIncomingBitrate) {
373
+ this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate;
374
+ }
375
+ if (observedCandidatePair.availableOutgoingBitrate) {
376
+ this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate;
377
+ }
314
378
  }
315
379
  }
316
380
  if (sample.iceTransports) {
317
381
  for (const iceTransport of sample.iceTransports) {
318
- this._updateIceTransportStats(iceTransport);
382
+ const observedIceTransport = this._updateIceTransportStats(iceTransport);
383
+
384
+ if (!observedIceTransport) return;
385
+
386
+ observedIceTransport.bytesReceived;
387
+
319
388
  }
320
389
  }
321
390
  if (sample.inboundRtps) {
@@ -338,6 +407,10 @@ export class ObservedPeerConnection extends EventEmitter {
338
407
  this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
339
408
  break;
340
409
  }
410
+
411
+ if (observedInboundRtp.jitter) {
412
+ jitterMeasurements.push(observedInboundRtp.jitter);
413
+ }
341
414
  }
342
415
  }
343
416
  if (sample.mediaPlayouts) {
@@ -374,12 +447,21 @@ export class ObservedPeerConnection extends EventEmitter {
374
447
  }
375
448
  if (sample.peerConnectionTransports) {
376
449
  for (const peerConnectionTransport of sample.peerConnectionTransports) {
377
- this._updatePeerConnectionTransportStats(peerConnectionTransport);
450
+ const observedTransport = this._updatePeerConnectionTransportStats(peerConnectionTransport);
451
+
452
+ if (!observedTransport) continue;
453
+
378
454
  }
379
455
  }
380
456
  if (sample.remoteInboundRtps) {
381
457
  for (const remoteInboundRtp of sample.remoteInboundRtps) {
382
- this._updateRemoteInboundRtpStats(remoteInboundRtp);
458
+ const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp);
459
+
460
+ if (!observedRemoteInboundRtp) continue;
461
+
462
+ if (observedRemoteInboundRtp.roundTripTime) {
463
+ rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime);
464
+ }
383
465
  }
384
466
  }
385
467
  if (sample.remoteOutboundRtps) {
@@ -408,6 +490,8 @@ export class ObservedPeerConnection extends EventEmitter {
408
490
  this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
409
491
  this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
410
492
  this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
493
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
494
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
411
495
  this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
412
496
  this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
413
497
  this.totalSentAudioBytes += this.deltaSentAudioBytes;
@@ -419,20 +503,55 @@ export class ObservedPeerConnection extends EventEmitter {
419
503
 
420
504
  this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
421
505
  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
-
506
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / elapsedTimeInSec;
507
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / elapsedTimeInSec;
508
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / elapsedTimeInSec;
509
+ this.receivingVideoBitrate = (this.deltaReceivedVideoBytes * 8) / elapsedTimeInSec;
510
+
511
+ if (rttMeasurementsInSec.length > 0) {
512
+ this.currentRttInMs = getMedian(rttMeasurementsInSec, false) * 1000;
513
+ } else {
514
+ this.currentRttInMs = undefined;
515
+ }
516
+ if (jitterMeasurements.length > 0) {
517
+ this.currentJitter = getMedian(jitterMeasurements, false);
518
+ } else {
519
+ this.currentJitter = undefined;
520
+ }
521
+ const wasUsingTURN = this.usingTURN;
427
522
  const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
523
+ const selectedCandidatePairForTurn: ObservedIceCandidatePair[] = [];
428
524
 
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:'));
525
+ this.usingTCP = false;
526
+ this.usingTURN = false;
527
+
528
+ for (const selectedCandidatePair of selectedIceCandidatePairs) {
529
+ if (selectedCandidatePair.getLocalCandidate()?.protocol === 'tcp') {
530
+ this.usingTCP = true;
531
+ }
532
+ if (selectedCandidatePair.getLocalCandidate()?.candidateType === 'relay' && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith('turn:')) {
533
+ selectedCandidatePairForTurn.push(selectedCandidatePair);
534
+ this.usingTURN = true;
535
+ }
536
+ this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived;
537
+ this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent;
538
+ }
432
539
 
540
+ if (this.usingTURN) {
541
+ if (!this.observedTurnServer) {
542
+ this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this);
543
+ }
544
+ this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn);
545
+ } else if (wasUsingTURN) {
546
+ if (!this.usingTURN) {
547
+ this.client.call.observer.observedTURN.removePeerConnection(this);
548
+ }
549
+ }
433
550
  this.calculatedScore.value = sample.score;
434
551
  this.updated = now;
435
552
  this._checkVisited();
553
+
554
+ this.emit('update');
436
555
  }
437
556
 
438
557
  private _checkVisited() {
@@ -540,12 +659,15 @@ export class ObservedPeerConnection extends EventEmitter {
540
659
 
541
660
  observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
542
661
 
662
+ observedCertificate.update(stats);
663
+
543
664
  this.observedCertificates.set(stats.id, observedCertificate);
544
665
  this.emit('added-certificate', observedCertificate);
666
+ } else {
667
+ observedCertificate.update(stats);
668
+ this.emit('updated-certificate', observedCertificate);
545
669
  }
546
670
 
547
- observedCertificate.update(stats);
548
-
549
671
  return observedCertificate;
550
672
  }
551
673
 
@@ -562,12 +684,15 @@ export class ObservedPeerConnection extends EventEmitter {
562
684
 
563
685
  observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
564
686
 
687
+ observedCodec.update(stats);
688
+
565
689
  this.observedCodecs.set(stats.id, observedCodec);
566
690
  this.emit('added-codec', observedCodec);
691
+ } else {
692
+ observedCodec.update(stats);
693
+ this.emit('updated-codec', observedCodec);
567
694
  }
568
695
 
569
- observedCodec.update(stats);
570
-
571
696
  return observedCodec;
572
697
  }
573
698
 
@@ -584,12 +709,15 @@ export class ObservedPeerConnection extends EventEmitter {
584
709
 
585
710
  observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
586
711
 
712
+ observedDataChannel.update(stats);
713
+
587
714
  this.observedDataChannels.set(stats.id, observedDataChannel);
588
715
  this.emit('added-data-channel', observedDataChannel);
716
+ } else {
717
+ observedDataChannel.update(stats);
718
+ this.emit('updated-data-channel', observedDataChannel);
589
719
  }
590
720
 
591
- observedDataChannel.update(stats);
592
-
593
721
  return observedDataChannel;
594
722
  }
595
723
 
@@ -606,12 +734,15 @@ export class ObservedPeerConnection extends EventEmitter {
606
734
 
607
735
  observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
608
736
 
737
+ observedIceCandidate.update(stats);
738
+
609
739
  this.observedIceCandidates.set(stats.id, observedIceCandidate);
610
740
  this.emit('added-ice-candidate', observedIceCandidate);
741
+ } else {
742
+ observedIceCandidate.update(stats);
743
+ this.emit('updated-ice-candidate', observedIceCandidate);
611
744
  }
612
745
 
613
- observedIceCandidate.update(stats);
614
-
615
746
  return observedIceCandidate;
616
747
  }
617
748
 
@@ -628,12 +759,15 @@ export class ObservedPeerConnection extends EventEmitter {
628
759
 
629
760
  observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
630
761
 
762
+ observedIceCandidatePair.update(stats);
763
+
631
764
  this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
632
765
  this.emit('added-ice-candidate-pair', observedIceCandidatePair);
766
+ } else {
767
+ observedIceCandidatePair.update(stats);
768
+ this.emit('updated-ice-candidate-pair', observedIceCandidatePair);
633
769
  }
634
770
 
635
- observedIceCandidatePair.update(stats);
636
-
637
771
  return observedIceCandidatePair;
638
772
  }
639
773
 
@@ -650,12 +784,15 @@ export class ObservedPeerConnection extends EventEmitter {
650
784
 
651
785
  observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
652
786
 
787
+ observedIceTransport.update(stats);
788
+
653
789
  this.observedIceTransports.set(stats.id, observedIceTransport);
654
790
  this.emit('added-ice-transport', observedIceTransport);
791
+ } else {
792
+ observedIceTransport.update(stats);
793
+ this.emit('updated-ice-transport', observedIceTransport);
655
794
  }
656
795
 
657
- observedIceTransport.update(stats);
658
-
659
796
  return observedIceTransport;
660
797
  }
661
798
 
@@ -678,13 +815,16 @@ export class ObservedPeerConnection extends EventEmitter {
678
815
  stats.trackIdentifier,
679
816
  this
680
817
  );
818
+
819
+ observedInboundRtp.update(stats);
681
820
 
682
821
  this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
683
822
  this.emit('added-inbound-rtp', observedInboundRtp);
823
+ } else {
824
+ observedInboundRtp.update(stats);
825
+ this.emit('updated-inbound-rtp', observedInboundRtp);
684
826
  }
685
827
 
686
- observedInboundRtp.update(stats);
687
-
688
828
  return observedInboundRtp;
689
829
  }
690
830
 
@@ -712,12 +852,15 @@ export class ObservedPeerConnection extends EventEmitter {
712
852
  mediaPlayout,
713
853
  );
714
854
 
855
+ observedInboundTrack.update(stats);
856
+
715
857
  this.observedInboundTracks.set(stats.id, observedInboundTrack);
716
858
  this.emit('added-inbound-track', observedInboundTrack);
859
+ } else {
860
+ observedInboundTrack.update(stats);
861
+ this.emit('updated-inbound-track', observedInboundTrack);
717
862
  }
718
863
 
719
- observedInboundTrack.update(stats);
720
-
721
864
  return observedInboundTrack;
722
865
  }
723
866
 
@@ -739,12 +882,15 @@ export class ObservedPeerConnection extends EventEmitter {
739
882
  this
740
883
  );
741
884
 
885
+ observedMediaPlayout.update(stats);
886
+
742
887
  this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
743
888
  this.emit('added-media-playout', observedMediaPlayout);
889
+ } else {
890
+ observedMediaPlayout.update(stats);
891
+ this.emit('updated-media-playout', observedMediaPlayout);
744
892
  }
745
893
 
746
- observedMediaPlayout.update(stats);
747
-
748
894
  return observedMediaPlayout;
749
895
  }
750
896
 
@@ -766,12 +912,15 @@ export class ObservedPeerConnection extends EventEmitter {
766
912
  this
767
913
  );
768
914
 
915
+ observedMediaSource.update(stats);
916
+
769
917
  this.observedMediaSources.set(stats.id, observedMediaSource);
770
918
  this.emit('added-media-source', observedMediaSource);
919
+ } else {
920
+ observedMediaSource.update(stats);
921
+ this.emit('updated-media-source', observedMediaSource);
771
922
  }
772
923
 
773
- observedMediaSource.update(stats);
774
-
775
924
  return observedMediaSource;
776
925
  }
777
926
 
@@ -794,11 +943,14 @@ export class ObservedPeerConnection extends EventEmitter {
794
943
  this
795
944
  );
796
945
 
946
+ observedOutboundRtp.update(stats);
947
+
797
948
  this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
798
949
  this.emit('added-outbound-rtp', observedOutboundRtp);
950
+ } else {
951
+ observedOutboundRtp.update(stats);
952
+ this.emit('updated-outbound-rtp', observedOutboundRtp);
799
953
  }
800
-
801
- observedOutboundRtp.update(stats);
802
954
 
803
955
  return observedOutboundRtp;
804
956
  }
@@ -826,12 +978,15 @@ export class ObservedPeerConnection extends EventEmitter {
826
978
  observedMediaSource,
827
979
  );
828
980
 
981
+ observedOutboundTrack.update(stats);
982
+
829
983
  this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
830
984
  this.emit('added-outbound-track', observedOutboundTrack);
985
+ } else {
986
+ observedOutboundTrack.update(stats);
987
+ this.emit('updated-outbound-track', observedOutboundTrack);
831
988
  }
832
989
 
833
- observedOutboundTrack.update(stats);
834
-
835
990
  return observedOutboundTrack;
836
991
  }
837
992
 
@@ -848,12 +1003,15 @@ export class ObservedPeerConnection extends EventEmitter {
848
1003
 
849
1004
  observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
850
1005
 
1006
+ observedPeerConnectionTransport.update(stats);
1007
+
851
1008
  this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
852
1009
  this.emit('added-peer-connection-transport', observedPeerConnectionTransport);
1010
+ } else {
1011
+ observedPeerConnectionTransport.update(stats);
1012
+ this.emit('updated-peer-connection-transport', observedPeerConnectionTransport);
853
1013
  }
854
1014
 
855
- observedPeerConnectionTransport.update(stats);
856
-
857
1015
  return observedPeerConnectionTransport;
858
1016
  }
859
1017
 
@@ -876,12 +1034,15 @@ export class ObservedPeerConnection extends EventEmitter {
876
1034
  this
877
1035
  );
878
1036
 
1037
+ observedRemoteInboundRtp.update(stats);
1038
+
879
1039
  this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
880
1040
  this.emit('added-remote-inbound-rtp', observedRemoteInboundRtp);
1041
+ } else {
1042
+ observedRemoteInboundRtp.update(stats);
1043
+ this.emit('updated-remote-inbound-rtp', observedRemoteInboundRtp);
881
1044
  }
882
1045
 
883
- observedRemoteInboundRtp.update(stats);
884
-
885
1046
  return observedRemoteInboundRtp;
886
1047
  }
887
1048
 
@@ -904,12 +1065,15 @@ export class ObservedPeerConnection extends EventEmitter {
904
1065
  this
905
1066
  );
906
1067
 
1068
+ observedRemoteOutboundRtp.update(stats);
1069
+
907
1070
  this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
908
1071
  this.emit('added-remote-outbound-rtp', observedRemoteOutboundRtp);
1072
+ } else {
1073
+ observedRemoteOutboundRtp.update(stats);
1074
+ this.emit('updated-remote-outbound-rtp', observedRemoteOutboundRtp);
909
1075
  }
910
1076
 
911
- observedRemoteOutboundRtp.update(stats);
912
-
913
1077
  return observedRemoteOutboundRtp;
914
1078
  }
915
1079
  }
@@ -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
+ }