@observertc/observer-js 1.0.1-c3edea3.0 → 1.0.1-c665f5a.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 (204) hide show
  1. package/README.md +535 -1
  2. package/lib/ObservedCall.d.ts +45 -7
  3. package/lib/ObservedCall.d.ts.map +1 -1
  4. package/lib/ObservedCall.js +156 -14
  5. package/lib/ObservedCallEventMonitor.d.ts +104 -0
  6. package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
  7. package/lib/ObservedCallEventMonitor.js +314 -0
  8. package/lib/ObservedCallSummary.d.ts +18 -0
  9. package/lib/ObservedCallSummary.d.ts.map +1 -0
  10. package/lib/ObservedClient.d.ts +78 -19
  11. package/lib/ObservedClient.d.ts.map +1 -1
  12. package/lib/ObservedClient.js +537 -80
  13. package/lib/ObservedClientEventMonitor.d.ts +91 -0
  14. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  15. package/lib/ObservedClientEventMonitor.js +254 -0
  16. package/lib/ObservedClientSummary.d.ts +23 -0
  17. package/lib/ObservedClientSummary.d.ts.map +1 -0
  18. package/lib/ObservedClientSummary.js +2 -0
  19. package/lib/ObservedTURN.d.ts +31 -0
  20. package/lib/ObservedTURN.d.ts.map +1 -0
  21. package/lib/ObservedTURN.js +59 -0
  22. package/lib/ObservedTurnServer.d.ts +25 -0
  23. package/lib/ObservedTurnServer.d.ts.map +1 -0
  24. package/lib/ObservedTurnServer.js +59 -0
  25. package/lib/Observer.d.ts +42 -22
  26. package/lib/Observer.d.ts.map +1 -1
  27. package/lib/Observer.js +115 -40
  28. package/lib/ObserverEventMonitor.d.ts +138 -0
  29. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  30. package/lib/ObserverEventMonitor.js +433 -0
  31. package/lib/ObserverSummary.d.ts +13 -0
  32. package/lib/ObserverSummary.d.ts.map +1 -0
  33. package/lib/ObserverSummary.js +2 -0
  34. package/lib/common/utils.d.ts +2 -1
  35. package/lib/common/utils.d.ts.map +1 -1
  36. package/lib/common/utils.js +11 -3
  37. package/lib/index.d.ts +22 -16
  38. package/lib/index.d.ts.map +1 -1
  39. package/lib/index.js +25 -17
  40. package/lib/mediasoup/ObservedMediasoupRouter.d.ts +7 -0
  41. package/lib/mediasoup/ObservedMediasoupRouter.d.ts.map +1 -0
  42. package/lib/mediasoup/ObservedMediasoupRouter.js +12 -0
  43. package/lib/schema/ClientEventTypes.d.ts +228 -0
  44. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  45. package/lib/schema/ClientEventTypes.js +41 -0
  46. package/lib/schema/{metaTypes.d.ts → ClientMetaTypes.d.ts} +2 -2
  47. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  48. package/lib/schema/ClientMetaTypes.js +16 -0
  49. package/lib/schema/ClientSample.d.ts +1 -1
  50. package/lib/schema/ClientSample.d.ts.map +1 -1
  51. package/lib/updaters/CallUpdater.d.ts.map +1 -0
  52. package/lib/updaters/CallUpdater.js +2 -0
  53. package/lib/updaters/ObserverUpdater.d.ts +5 -0
  54. package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
  55. package/lib/updaters/ObserverUpdater.js +2 -0
  56. package/lib/updaters/OnAllCallObserverUpdater.d.ts +15 -0
  57. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  58. package/lib/updaters/OnAllCallObserverUpdater.js +50 -0
  59. package/lib/updaters/OnAllClientCallUpdater.d.ts +15 -0
  60. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
  61. package/lib/updaters/OnAllClientCallUpdater.js +53 -0
  62. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +12 -0
  63. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  64. package/lib/updaters/OnAnyCallObserverUpdater.js +37 -0
  65. package/lib/updaters/OnAnyClientCallUpdater.d.ts +12 -0
  66. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
  67. package/lib/updaters/OnAnyClientCallUpdater.js +36 -0
  68. package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
  69. package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
  70. package/lib/updaters/OnIntervalUpdater.js +17 -0
  71. package/lib/updaters/Updater.d.ts +6 -0
  72. package/lib/updaters/Updater.d.ts.map +1 -0
  73. package/lib/updaters/Updater.js +2 -0
  74. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  75. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  76. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  77. package/lib/utils/RemoteTrackResolver.d.ts +2 -2
  78. package/lib/utils/RemoteTrackResolver.d.ts.map +1 -1
  79. package/lib/{ObservedCertificate.d.ts → webrtc/ObservedCertificate.d.ts} +1 -1
  80. package/lib/webrtc/ObservedCertificate.d.ts.map +1 -0
  81. package/lib/{ObservedCodec.d.ts → webrtc/ObservedCodec.d.ts} +1 -1
  82. package/lib/webrtc/ObservedCodec.d.ts.map +1 -0
  83. package/lib/{ObservedDataChannel.d.ts → webrtc/ObservedDataChannel.d.ts} +7 -1
  84. package/lib/webrtc/ObservedDataChannel.d.ts.map +1 -0
  85. package/lib/{ObservedDataChannel.js → webrtc/ObservedDataChannel.js} +30 -0
  86. package/lib/{ObservedIceCandidate.d.ts → webrtc/ObservedIceCandidate.d.ts} +1 -1
  87. package/lib/webrtc/ObservedIceCandidate.d.ts.map +1 -0
  88. package/lib/{ObservedIceCandidatePair.d.ts → webrtc/ObservedIceCandidatePair.d.ts} +7 -2
  89. package/lib/webrtc/ObservedIceCandidatePair.d.ts.map +1 -0
  90. package/lib/{ObservedIceCandidatePair.js → webrtc/ObservedIceCandidatePair.js} +45 -1
  91. package/lib/{ObservedIceTransport.d.ts → webrtc/ObservedIceTransport.d.ts} +5 -1
  92. package/lib/webrtc/ObservedIceTransport.d.ts.map +1 -0
  93. package/lib/{ObservedIceTransport.js → webrtc/ObservedIceTransport.js} +28 -0
  94. package/lib/{ObservedInboundRtp.d.ts → webrtc/ObservedInboundRtp.d.ts} +2 -2
  95. package/lib/webrtc/ObservedInboundRtp.d.ts.map +1 -0
  96. package/lib/{ObservedInboundTrack.d.ts → webrtc/ObservedInboundTrack.d.ts} +7 -4
  97. package/lib/webrtc/ObservedInboundTrack.d.ts.map +1 -0
  98. package/lib/{ObservedInboundTrack.js → webrtc/ObservedInboundTrack.js} +4 -1
  99. package/lib/{ObservedMediaPlayout.d.ts → webrtc/ObservedMediaPlayout.d.ts} +2 -2
  100. package/lib/webrtc/ObservedMediaPlayout.d.ts.map +1 -0
  101. package/lib/{ObservedMediaSource.d.ts → webrtc/ObservedMediaSource.d.ts} +2 -2
  102. package/lib/webrtc/ObservedMediaSource.d.ts.map +1 -0
  103. package/lib/{ObservedOutboundRtp.d.ts → webrtc/ObservedOutboundRtp.d.ts} +2 -2
  104. package/lib/webrtc/ObservedOutboundRtp.d.ts.map +1 -0
  105. package/lib/{ObservedOutboundTrack.d.ts → webrtc/ObservedOutboundTrack.d.ts} +7 -4
  106. package/lib/webrtc/ObservedOutboundTrack.d.ts.map +1 -0
  107. package/lib/{ObservedOutboundTrack.js → webrtc/ObservedOutboundTrack.js} +4 -1
  108. package/lib/{ObservedPeerConnection.d.ts → webrtc/ObservedPeerConnection.d.ts} +39 -5
  109. package/lib/webrtc/ObservedPeerConnection.d.ts.map +1 -0
  110. package/lib/{ObservedPeerConnection.js → webrtc/ObservedPeerConnection.js} +185 -30
  111. package/lib/{ObservedPeerConnectionTransport.d.ts → webrtc/ObservedPeerConnectionTransport.d.ts} +1 -1
  112. package/lib/webrtc/ObservedPeerConnectionTransport.d.ts.map +1 -0
  113. package/lib/{ObservedRemoteInboundRtp.d.ts → webrtc/ObservedRemoteInboundRtp.d.ts} +2 -2
  114. package/lib/webrtc/ObservedRemoteInboundRtp.d.ts.map +1 -0
  115. package/lib/{ObservedRemoteOutboundRtp.d.ts → webrtc/ObservedRemoteOutboundRtp.d.ts} +2 -2
  116. package/lib/webrtc/ObservedRemoteOutboundRtp.d.ts.map +1 -0
  117. package/package.json +1 -1
  118. package/src/ObservedCall.ts +233 -17
  119. package/src/ObservedCallEventMonitor.ts +392 -0
  120. package/src/ObservedCallSummary.ts +22 -0
  121. package/src/ObservedClient.ts +644 -92
  122. package/src/ObservedClientEventMonitor.ts +309 -0
  123. package/src/ObservedClientSummary.ts +24 -0
  124. package/src/ObservedTURN.ts +87 -0
  125. package/src/ObservedTurnServer.ts +72 -0
  126. package/src/Observer.ts +168 -64
  127. package/src/ObserverEventMonitor.ts +551 -0
  128. package/src/ObserverSummary.ts +15 -0
  129. package/src/common/utils.ts +12 -2
  130. package/src/index.ts +22 -17
  131. package/src/mediasoup/ObservedMediasoupRouter.ts +12 -0
  132. package/src/schema/ClientEventTypes.ts +280 -0
  133. package/src/schema/{metaTypes.ts → ClientMetaTypes.ts} +1 -1
  134. package/src/schema/ClientSample.ts +1 -1
  135. package/src/updaters/ObserverUpdater.ts +5 -0
  136. package/src/updaters/OnAllCallObserverUpdater.ts +59 -0
  137. package/src/updaters/OnAllClientCallUpdater.ts +61 -0
  138. package/src/updaters/OnAnyCallObserverUpdater.ts +41 -0
  139. package/src/updaters/OnAnyClientCallUpdater.ts +39 -0
  140. package/src/updaters/OnIntervalUpdater.ts +19 -0
  141. package/src/updaters/Updater.ts +5 -0
  142. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  143. package/src/utils/RemoteTrackResolver.ts +2 -2
  144. package/src/{ObservedCertificate.ts → webrtc/ObservedCertificate.ts} +1 -1
  145. package/src/{ObservedCodec.ts → webrtc/ObservedCodec.ts} +1 -1
  146. package/src/{ObservedDataChannel.ts → webrtc/ObservedDataChannel.ts} +33 -1
  147. package/src/{ObservedIceCandidate.ts → webrtc/ObservedIceCandidate.ts} +1 -1
  148. package/src/{ObservedIceCandidatePair.ts → webrtc/ObservedIceCandidatePair.ts} +45 -3
  149. package/src/{ObservedIceTransport.ts → webrtc/ObservedIceTransport.ts} +30 -1
  150. package/src/{ObservedInboundRtp.ts → webrtc/ObservedInboundRtp.ts} +2 -2
  151. package/src/{ObservedInboundTrack.ts → webrtc/ObservedInboundTrack.ts} +9 -4
  152. package/src/{ObservedMediaPlayout.ts → webrtc/ObservedMediaPlayout.ts} +2 -2
  153. package/src/{ObservedMediaSource.ts → webrtc/ObservedMediaSource.ts} +2 -2
  154. package/src/{ObservedOutboundRtp.ts → webrtc/ObservedOutboundRtp.ts} +2 -2
  155. package/src/{ObservedOutboundTrack.ts → webrtc/ObservedOutboundTrack.ts} +9 -4
  156. package/src/{ObservedPeerConnection.ts → webrtc/ObservedPeerConnection.ts} +240 -51
  157. package/src/{ObservedPeerConnectionTransport.ts → webrtc/ObservedPeerConnectionTransport.ts} +1 -1
  158. package/src/{ObservedRemoteInboundRtp.ts → webrtc/ObservedRemoteInboundRtp.ts} +2 -2
  159. package/src/{ObservedRemoteOutboundRtp.ts → webrtc/ObservedRemoteOutboundRtp.ts} +2 -2
  160. package/tsconfig.json +1 -1
  161. package/lib/ObservedCertificate.d.ts.map +0 -1
  162. package/lib/ObservedCodec.d.ts.map +0 -1
  163. package/lib/ObservedDataChannel.d.ts.map +0 -1
  164. package/lib/ObservedIceCandidate.d.ts.map +0 -1
  165. package/lib/ObservedIceCandidatePair.d.ts.map +0 -1
  166. package/lib/ObservedIceTransport.d.ts.map +0 -1
  167. package/lib/ObservedInboundRtp.d.ts.map +0 -1
  168. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  169. package/lib/ObservedMediaPlayout.d.ts.map +0 -1
  170. package/lib/ObservedMediaSource.d.ts.map +0 -1
  171. package/lib/ObservedOutboundRtp.d.ts.map +0 -1
  172. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
  173. package/lib/ObservedPeerConnection.d.ts.map +0 -1
  174. package/lib/ObservedPeerConnectionTransport.d.ts.map +0 -1
  175. package/lib/ObservedRemoteInboundRtp.d.ts.map +0 -1
  176. package/lib/ObservedRemoteOutboundRtp.d.ts.map +0 -1
  177. package/lib/schema/eventTypes.d.ts +0 -33
  178. package/lib/schema/eventTypes.d.ts.map +0 -1
  179. package/lib/schema/eventTypes.js +0 -38
  180. package/lib/schema/metaTypes.d.ts.map +0 -1
  181. package/lib/schema/metaTypes.js +0 -16
  182. package/lib/utils/CallUpdater.d.ts.map +0 -1
  183. package/lib/utils/OnAllClientCallUpdater.d.ts +0 -16
  184. package/lib/utils/OnAllClientCallUpdater.d.ts.map +0 -1
  185. package/lib/utils/OnAllClientCallUpdater.js +0 -58
  186. package/lib/utils/onAnyClientCallUpdater.d.ts +0 -14
  187. package/lib/utils/onAnyClientCallUpdater.d.ts.map +0 -1
  188. package/lib/utils/onAnyClientCallUpdater.js +0 -51
  189. package/src/schema/eventTypes.ts +0 -36
  190. package/src/utils/OnAllClientCallUpdater.ts +0 -68
  191. package/src/utils/onAnyClientCallUpdater.ts +0 -59
  192. /package/lib/{utils/CallUpdater.js → ObservedCallSummary.js} +0 -0
  193. /package/lib/{utils → updaters}/CallUpdater.d.ts +0 -0
  194. /package/lib/{ObservedCertificate.js → webrtc/ObservedCertificate.js} +0 -0
  195. /package/lib/{ObservedCodec.js → webrtc/ObservedCodec.js} +0 -0
  196. /package/lib/{ObservedIceCandidate.js → webrtc/ObservedIceCandidate.js} +0 -0
  197. /package/lib/{ObservedInboundRtp.js → webrtc/ObservedInboundRtp.js} +0 -0
  198. /package/lib/{ObservedMediaPlayout.js → webrtc/ObservedMediaPlayout.js} +0 -0
  199. /package/lib/{ObservedMediaSource.js → webrtc/ObservedMediaSource.js} +0 -0
  200. /package/lib/{ObservedOutboundRtp.js → webrtc/ObservedOutboundRtp.js} +0 -0
  201. /package/lib/{ObservedPeerConnectionTransport.js → webrtc/ObservedPeerConnectionTransport.js} +0 -0
  202. /package/lib/{ObservedRemoteInboundRtp.js → webrtc/ObservedRemoteInboundRtp.js} +0 -0
  203. /package/lib/{ObservedRemoteOutboundRtp.js → webrtc/ObservedRemoteOutboundRtp.js} +0 -0
  204. /package/src/{utils → updaters}/CallUpdater.ts +0 -0
@@ -1,12 +1,12 @@
1
1
  import { EventEmitter } from 'events';
2
- import { ObservedPeerConnection } from './ObservedPeerConnection';
2
+ import { ObservedPeerConnection } from './webrtc/ObservedPeerConnection';
3
3
  import { createLogger } from './common/logger';
4
4
  // eslint-disable-next-line camelcase
5
5
  import { ClientEvent, ClientMetaData, ClientSample, PeerConnectionSample, ClientIssue, ExtensionStat } from './schema/ClientSample';
6
- import * as MetaData from './schema/metaTypes';
7
- import { ClientEventType } from './schema/eventTypes';
6
+ import * as MetaData from './schema/ClientMetaTypes';
7
+ import { ClientEventTypes } from './schema/ClientEventTypes';
8
8
  import { ObservedCall } from './ObservedCall';
9
- import { ClientMetaType } from './schema/metaTypes';
9
+ import { ClientMetaTypes } from './schema/ClientMetaTypes';
10
10
  import { parseJsonAs } from './common/utils';
11
11
  import { CalculatedScore } from './scores/CalculatedScore';
12
12
  import { Detectors } from './detectors/Detectors';
@@ -15,24 +15,23 @@ const logger = createLogger('ObservedClient');
15
15
 
16
16
  export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
17
17
  clientId: string;
18
+ maxIdleTimeMs?: number;
18
19
  appData?: AppData;
19
20
  };
20
21
 
21
22
  export type ObservedClientEvents = {
22
- update: [
23
- {
24
- sample: ClientSample;
25
- elapsedTimeInMs: number;
26
- }
27
- ];
23
+ update: [sample: ClientSample, elapsedTimeInMs: number];
28
24
  close: [];
29
25
  joined: [];
26
+ issue: [ClientIssue];
27
+ metaData: [ClientMetaData];
30
28
  rejoined: [timestamp: number];
31
29
  left: [];
32
30
  usingturn: [boolean];
33
31
  usermediaerror: [string];
32
+ extensionStats: [ExtensionStat];
33
+ clientEvent: [ClientEvent];
34
34
 
35
- // do we need this?
36
35
  newpeerconnection: [ObservedPeerConnection];
37
36
  };
38
37
 
@@ -46,14 +45,12 @@ export declare interface ObservedClient {
46
45
  export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
47
46
  public readonly detectors: Detectors;
48
47
 
49
- public readonly clientId: string;
50
48
  public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
51
49
  public readonly calculatedScore: CalculatedScore = {
52
50
  weight: 1,
53
51
  value: undefined,
54
52
  };
55
53
 
56
- public appData?: AppData;
57
54
  public attachments?: Record<string, unknown>;
58
55
 
59
56
  public updated = Date.now();
@@ -62,6 +59,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
62
59
 
63
60
  public joinedAt?: number;
64
61
  public leftAt?: number;
62
+ public closedAt?: number;
63
+ public lastSampleTimestamp?: number;
65
64
  // the timestamp of the CLIENT_JOINED event
66
65
  public operationSystem?: MetaData.OperationSystem;
67
66
  public engine?: MetaData.Engine;
@@ -70,48 +69,96 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
70
69
  public mediaConstraints: string[] = [];
71
70
 
72
71
  public usingTURN = false;
72
+ public usingTCP = false;
73
73
  public availableOutgoingBitrate = 0;
74
74
  public availableIncomingBitrate = 0;
75
- // public totalInboundPacketsLost = 0;
76
- // public totalInboundPacketsReceived = 0;
77
- // public totalOutboundPacketsSent = 0;
78
- // public totalDataChannelBytesSent = 0;
79
- // public totalDataChannelBytesReceived = 0;
80
- // public totalSentBytes = 0;
81
- // public totalReceivedBytes = 0;
82
- // public totalReceivedAudioBytes = 0;
83
- // public totalReceivedVideoBytes = 0;
84
- // public totalSentAudioBytes = 0;
85
- // public totalSentVideoBytes = 0;
86
-
87
- // public deltaReceivedAudioBytes = 0;
88
- // public deltaReceivedVideoBytes = 0;
89
- // public deltaSentAudioBytes = 0;
90
- // public deltaSentVideoBytes = 0;
91
- // public deltaDataChannelBytesSent = 0;
92
- // public deltaDataChannelBytesReceived = 0;
93
- // public deltaInboundPacketsLost = 0;
94
- // public deltaInboundPacketsReceived = 0;
95
- // public deltaOutboundPacketsSent = 0;
96
-
97
- public avgRttInMs?: number;
98
- public outboundAudioBitrate?: number;
99
- public outboundVideoBitrate?: number;
100
- public inboundAudioBitrate?: number;
101
- public inboundVideoBitrate?: number;
75
+
76
+ public totalInboundPacketsLost = 0;
77
+ public totalInboundPacketsReceived = 0;
78
+ public totalOutboundPacketsSent = 0;
79
+ public totalDataChannelBytesSent = 0;
80
+ public totalDataChannelBytesReceived = 0;
81
+ public totalDataChannelMessagesSent = 0;
82
+ public totalDataChannelMessagesReceived = 0;
83
+ public totalReceivedAudioBytes = 0;
84
+ public totalReceivedVideoBytes = 0;
85
+ public totalSentAudioBytes = 0;
86
+ public totalSentVideoBytes = 0;
87
+ public totalSentBytes = 0;
88
+ public totalReceivedBytes = 0;
89
+
90
+ public deltaReceivedAudioBytes = 0;
91
+ public deltaReceivedVideoBytes = 0;
92
+ public deltaSentAudioBytes = 0;
93
+ public deltaSentVideoBytes = 0;
94
+ public deltaDataChannelBytesSent = 0;
95
+ public deltaDataChannelBytesReceived = 0;
96
+ public deltaDataChannelMessagesSent = 0;
97
+ public deltaDataChannelMessagesReceived = 0;
98
+ public deltaInboundPacketsLost = 0;
99
+ public deltaInboundPacketsReceived = 0;
100
+ public deltaOutboundPacketsSent = 0;
101
+ public deltaTransportSentBytes = 0;
102
+ public deltaTransportReceivedBytes = 0;
103
+ public deltaRttLt50Measurements = 0;
104
+ public deltaRttLt150Measurements = 0;
105
+ public deltaRttLt300Measurements = 0;
106
+ public deltaRttGtOrEq300Measurements = 0;
107
+
108
+ public currentMaxRttInMs?: number;
109
+ public currentMinRttInMs?: number;
110
+ public currentAvgRttInMs?: number;
111
+
112
+ public sendingAudioBitrate = 0;
113
+ public sendingVideoBitrate = 0;
114
+ public receivingAudioBitrate = 0;
115
+ public receivingVideoBitrate = 0;
116
+
117
+ public numberOfInboundRtpStreams = 0;
118
+ public numberOfInbundTracks = 0;
119
+ public numberOfOutboundRtpStreams = 0;
120
+ public numberOfOutboundTracks = 0;
121
+ public numberOfDataChannels = 0;
122
+
123
+ public totalRttLt50Measurements = 0;
124
+ public totalRttLt150Measurements = 0;
125
+ public totalRttLt300Measurements = 0;
126
+ public totalRttGtOrEq300Measurements = 0;
127
+ public deltaNumberOfIssues = 0;
128
+
129
+ public totalScoreSum = 0;
130
+ public numberOfScoreMeasurements = 0;
131
+ public totalNumberOfIssues = 0;
102
132
 
103
133
  public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
104
134
  public issues: ClientIssue[] = [];
105
135
 
106
- public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
136
+ private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
137
+ private _autoCloseTimer?: ReturnType<typeof setTimeout>;
138
+
139
+ public constructor(
140
+ public readonly settings: ObservedClientSettings<AppData>,
141
+ public readonly call: ObservedCall
142
+ ) {
107
143
  super();
108
144
  this.setMaxListeners(Infinity);
145
+ this._createAutoClose();
109
146
 
110
- this.clientId = settings.clientId;
111
- this.appData = settings.appData;
112
147
  this.detectors = new Detectors();
113
148
  }
114
149
 
150
+ public get clientId() {
151
+ return this.settings.clientId;
152
+ }
153
+
154
+ public get appData(): AppData {
155
+ return this.settings.appData ?? {} as AppData;
156
+ }
157
+
158
+ public get numberOfPeerConnections() {
159
+ return this.observedPeerConnections.size;
160
+ }
161
+
115
162
  public get score() {
116
163
  return this.calculatedScore.value;
117
164
  }
@@ -120,47 +167,216 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
120
167
  if (this.closed) return;
121
168
  this.closed = true;
122
169
 
170
+ this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
171
+ this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
172
+ this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
173
+ this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
174
+
123
175
  Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
176
+ if (!this.leftAt) {
177
+ this.leftAt = this.lastSampleTimestamp;
178
+
179
+ if (this.leftAt) {
180
+ this.emit('left');
181
+ }
182
+ }
183
+ this.closedAt = Date.now();
124
184
 
125
185
  this.emit('close');
126
186
  }
127
187
 
188
+ private _createAutoClose() {
189
+ if (this.closed) return;
190
+ if (this.settings.maxIdleTimeMs === undefined || this.settings.maxIdleTimeMs < 1) return;
191
+
192
+ this._autoCloseTimer = setTimeout(() => {
193
+ if (this.closed) return;
194
+
195
+ logger.debug(`Client ${this.clientId} is idle for ${this.settings.maxIdleTimeMs}ms, closing...`);
196
+ this.close();
197
+ }, this.settings.maxIdleTimeMs);
198
+ }
199
+
128
200
  public accept(sample: ClientSample): void {
129
201
  if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
202
+
203
+ this._createAutoClose();
130
204
 
131
205
  const now = Date.now();
206
+ const elapsedInMs = now - this.updated;
207
+ const elapsedInSeconds = elapsedInMs / 1000;
208
+ let sumOfRtts = 0;
209
+ let numberOfRttMeasurements = 0;
210
+
211
+ ++this.acceptedSamples;
212
+
213
+ this.availableIncomingBitrate = 0;
214
+ this.availableOutgoingBitrate = 0;
215
+ this.deltaDataChannelBytesReceived = 0;
216
+ this.deltaDataChannelBytesSent = 0;
217
+ this.deltaDataChannelMessagesReceived = 0;
218
+ this.deltaDataChannelMessagesSent = 0;
219
+ this.deltaInboundPacketsLost = 0;
220
+ this.deltaInboundPacketsReceived = 0;
221
+ this.deltaOutboundPacketsSent = 0;
222
+ this.deltaReceivedAudioBytes = 0;
223
+ this.deltaReceivedVideoBytes = 0;
224
+ this.deltaSentAudioBytes = 0;
225
+ this.deltaSentVideoBytes = 0;
226
+ this.deltaTransportReceivedBytes = 0;
227
+ this.deltaTransportSentBytes = 0;
228
+ this.deltaRttLt50Measurements = 0;
229
+ this.deltaRttLt150Measurements = 0;
230
+ this.deltaRttLt300Measurements = 0;
231
+ this.deltaRttGtOrEq300Measurements = 0;
232
+ this.deltaNumberOfIssues = 0;
233
+
234
+ this.numberOfDataChannels = 0;
235
+ this.numberOfInboundRtpStreams = 0;
236
+ this.numberOfInbundTracks = 0;
237
+ this.numberOfOutboundRtpStreams = 0;
238
+ this.numberOfOutboundTracks = 0;
239
+ this.usingTURN = false;
240
+ this.usingTCP = false;
241
+ this.currentMinRttInMs = undefined;
242
+ this.currentMaxRttInMs = undefined;
243
+
244
+ this._mergeInjections(sample);
245
+
246
+ const clientEventsPostBuffer: ClientEvent[] = [];
247
+
248
+ for (const clientEvent of sample.clientEvents ?? []) {
249
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
250
+
251
+ this.call.observer.emit('client-event', this, clientEvent);
252
+ }
253
+
254
+ for (const metaData of sample.clientMetaItems ?? []) {
255
+ this.addMetadata(metaData);
256
+ }
132
257
 
133
- sample.clientEvents?.forEach(this._processClientEvent.bind(this));
134
- sample.clientMetaItems?.forEach(this.processMetadata.bind(this));
135
- sample.clientIssues?.forEach(this.processIssue.bind(this));
136
- sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
137
- sample.extensionStats?.forEach(this.processExtensionStats.bind(this));
258
+ for (const issue of sample.clientIssues ?? []) {
259
+ this.addIssue(issue);
260
+
261
+ ++this.deltaNumberOfIssues;
262
+ }
263
+
264
+ for (const extensionStat of sample.extensionStats ?? []) {
265
+ this.addExtensionStats(extensionStat);
266
+ }
267
+
268
+ for (const pcSample of sample.peerConnections ?? []) {
269
+ const observedPeerConnection = this._updatePeerConnection(pcSample);
270
+
271
+ if (!observedPeerConnection) continue;
272
+
273
+ this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
274
+ this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
275
+ this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
276
+ this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
277
+ this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
278
+ this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
279
+ this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
280
+ this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
281
+ this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
282
+ this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
283
+ this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
284
+ this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
285
+ this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
286
+
287
+ this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
288
+ this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
289
+
290
+ this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
291
+ this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
292
+ this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
293
+ this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
294
+ this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
295
+
296
+ if (observedPeerConnection.usingTURN) {
297
+ this.usingTURN = true;
298
+ }
299
+ if (observedPeerConnection.usingTCP) {
300
+ this.usingTCP = true;
301
+ }
302
+
303
+ if (observedPeerConnection.currentRttInMs) {
304
+ if (this.currentMinRttInMs === undefined || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
305
+ this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
306
+ }
307
+ if (this.currentMaxRttInMs === undefined || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
308
+ this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
309
+ }
310
+ if (observedPeerConnection.currentRttInMs < 50) {
311
+ this.deltaRttLt50Measurements += 1;
312
+ } else if (observedPeerConnection.currentRttInMs < 150) {
313
+ this.deltaRttLt150Measurements += 1;
314
+ } else if (observedPeerConnection.currentRttInMs < 300) {
315
+ this.deltaRttLt300Measurements += 1;
316
+ } else if (300 <= observedPeerConnection.currentRttInMs) {
317
+ this.deltaRttGtOrEq300Measurements += 1;
318
+ }
319
+
320
+ sumOfRtts += observedPeerConnection.currentRttInMs;
321
+ ++numberOfRttMeasurements;
322
+ }
323
+ }
324
+
325
+ for (const clientEvent of clientEventsPostBuffer) {
326
+ this._processClientEvent(clientEvent);
327
+ }
138
328
 
139
329
  // emit new attachments?
140
330
  this.attachments = sample.attachments;
141
331
 
332
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
333
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
334
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
335
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
336
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
337
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
338
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
339
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
340
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
341
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
342
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
343
+ this.totalReceivedBytes += this.deltaTransportReceivedBytes;
344
+ this.totalSentBytes += this.deltaTransportSentBytes;
345
+ this.totalRttLt50Measurements += this.deltaRttLt50Measurements;
346
+ this.totalRttLt150Measurements += this.deltaRttLt150Measurements;
347
+ this.totalRttLt300Measurements += this.deltaRttLt300Measurements;
348
+ this.totalRttGtOrEq300Measurements += this.deltaRttGtOrEq300Measurements;
349
+ this.totalNumberOfIssues += this.deltaNumberOfIssues;
350
+
351
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
352
+ this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
353
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
354
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
355
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : undefined;
356
+
142
357
  this.calculatedScore.value = sample.score;
143
358
  this.detectors.update();
144
359
 
360
+ this.lastSampleTimestamp = sample.timestamp;
145
361
  // emit update
146
- this.emit('update', {
362
+ this.emit('update',
147
363
  sample,
148
- elapsedTimeInMs: now - this.updated,
149
- });
364
+ now - this.updated,
365
+ );
150
366
  this.updated = now;
367
+
368
+ // if result changed after update
369
+ if (this.calculatedScore.value) {
370
+ this.totalScoreSum += this.calculatedScore.value;
371
+ ++this.numberOfScoreMeasurements;
372
+ }
151
373
  }
152
374
 
153
- private _processClientEvent(event: ClientEvent) {
375
+ private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
376
+ // eslint-disable-next-line no-console
377
+ // console.warn('ClientEvent', event);
154
378
  switch (event.type) {
155
- case ClientEventType.CALL_STARTED: {
156
- // update call timestamp if it's the first time
157
- break;
158
- }
159
- case ClientEventType.CALL_ENDED: {
160
- // update call timestamp if it's the first time
161
- break;
162
- }
163
- case ClientEventType.CLIENT_JOINED: {
379
+ case ClientEventTypes.CLIENT_JOINED: {
164
380
  if (event.timestamp) {
165
381
  if (!this.joinedAt) {
166
382
  this.joinedAt = event.timestamp;
@@ -172,78 +388,321 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
172
388
  logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
173
389
  }
174
390
  }
391
+ logger.debug('Client %s joined at %o', this.clientId, event);
175
392
  break;
176
393
  }
177
- case ClientEventType.CLIENT_LEFT: {
394
+ case ClientEventTypes.CLIENT_LEFT: {
178
395
  if (event.timestamp) {
179
396
  if (!this.leftAt) {
180
397
  this.leftAt = event.timestamp;
181
398
  this.emit('left');
399
+ } else {
400
+ logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
401
+ }
402
+
403
+ }
404
+ logger.debug('Client %s left at %o', this.clientId, event);
405
+ break;
406
+ }
407
+ case ClientEventTypes.PEER_CONNECTION_OPENED: {
408
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
409
+
410
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
411
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
412
+
413
+ if (observedPeerConnection) {
414
+ observedPeerConnection.openedAt = event.timestamp;
415
+ } else if (postBuffer) {
416
+ postBuffer.push(event);
417
+ } else {
418
+ logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o', event);
419
+ }
420
+
421
+ }
422
+ break;
423
+ }
424
+ case ClientEventTypes.PEER_CONNECTION_CLOSED: {
425
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
426
+
427
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
428
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
429
+
430
+ if (observedPeerConnection) {
431
+ observedPeerConnection.closedAt = event.timestamp;
432
+ } else if (postBuffer) {
433
+ postBuffer.push(event);
434
+ } else {
435
+ logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o', event);
436
+ }
437
+ }
438
+ break;
439
+ }
440
+ case ClientEventTypes.MEDIA_TRACK_ADDED: {
441
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
442
+
443
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
444
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
445
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
446
+
447
+ if (observedTrack) {
448
+ observedTrack.addedAt = event.timestamp;
449
+ } else if (postBuffer) {
450
+ postBuffer.push(event);
451
+ } else {
452
+ logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o', event);
453
+ }
454
+ }
455
+ break;
456
+ }
457
+ case ClientEventTypes.MEDIA_TRACK_REMOVED: {
458
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
459
+
460
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
461
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
462
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
463
+
464
+ if (observedTrack) {
465
+ observedTrack.removedAt = event.timestamp;
466
+ } else if (postBuffer) {
467
+ postBuffer.push(event);
468
+ } else {
469
+ logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o', event);
470
+ }
471
+ }
472
+ break;
473
+ }
474
+ case ClientEventTypes.DATA_CHANNEL_OPEN: {
475
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
476
+
477
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
478
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
479
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
480
+
481
+ if (observedDataChannel) {
482
+ observedDataChannel.addedAt = event.timestamp;
483
+ } else if (postBuffer) {
484
+ postBuffer.push(event);
485
+ } else {
486
+ logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
487
+ }
488
+ }
489
+ break;
490
+ }
491
+ case ClientEventTypes.DATA_CHANNEL_CLOSED: {
492
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
493
+
494
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
495
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
496
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
497
+
498
+ if (observedDataChannel) {
499
+ observedDataChannel.removedAt = event.timestamp;
500
+ } else if (postBuffer) {
501
+ postBuffer.push(event);
502
+ } else {
503
+ logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
504
+ }
505
+ }
506
+ break;
507
+ }
508
+ case ClientEventTypes.MEDIA_TRACK_MUTED: {
509
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
510
+
511
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
512
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
513
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
514
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
515
+
516
+ if (observedPeerConnection) {
517
+ if (observedInboundTrack) {
518
+ observedInboundTrack.muted = true;
519
+ observedPeerConnection?.emit('muted-inbound-track', observedInboundTrack);
520
+ } else if (observedOutboundTrack) {
521
+ observedOutboundTrack.muted = true;
522
+ observedPeerConnection?.emit('muted-outbound-track', observedOutboundTrack);
523
+ }
524
+ } else if (postBuffer) {
525
+ postBuffer.push(event);
526
+ } else {
527
+ logger.warn('Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
528
+ }
529
+ }
530
+
531
+ break;
532
+ }
533
+ case ClientEventTypes.MEDIA_TRACK_UNMUTED: {
534
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
535
+
536
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
537
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
538
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
539
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
540
+
541
+ if (observedPeerConnection) {
542
+ if (observedInboundTrack) {
543
+ observedInboundTrack.muted = false;
544
+ observedPeerConnection?.emit('unmuted-inbound-track', observedInboundTrack);
545
+ } else if (observedOutboundTrack) {
546
+ observedOutboundTrack.muted = false;
547
+ observedPeerConnection?.emit('unmuted-outbound-track', observedOutboundTrack);
548
+ }
549
+ } else if (postBuffer) {
550
+ postBuffer.push(event);
551
+ } else {
552
+ logger.warn('Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
553
+ }
554
+ }
555
+ break;
556
+ }
557
+ case ClientEventTypes.ICE_CONNECTION_STATE_CHANGED: {
558
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
559
+
560
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceConnectionState && typeof payload.iceConnectionState === 'string') {
561
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
562
+
563
+ if (observedPeerConnection) {
564
+ observedPeerConnection.iceConnectionState = payload.iceConnectionState;
565
+ observedPeerConnection.emit('iceconnectionstatechange', {
566
+ state: payload.iceConnectionState,
567
+ });
568
+ } else if (postBuffer) {
569
+ postBuffer.push(event);
570
+ } else {
571
+ logger.warn('Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
572
+ }
573
+ }
574
+ break;
575
+ }
576
+ case ClientEventTypes.ICE_GATHERING_STATE_CHANGED: {
577
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
578
+
579
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceGatheringState && typeof payload.iceGatheringState === 'string') {
580
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
581
+
582
+ if (observedPeerConnection) {
583
+ observedPeerConnection.iceGatheringState = payload.iceGatheringState;
584
+ observedPeerConnection.emit('icegatheringstatechange', {
585
+ state: payload.iceGatheringState,
586
+ });
587
+ } else if (postBuffer) {
588
+ postBuffer.push(event);
589
+ } else {
590
+ logger.warn('Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
591
+ }
592
+ }
593
+ break;
594
+ }
595
+ case ClientEventTypes.PEER_CONNECTION_STATE_CHANGED: {
596
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
597
+
598
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.peerConnectionState && typeof payload.peerConnectionState === 'string') {
599
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
600
+
601
+ if (observedPeerConnection) {
602
+ observedPeerConnection.connectionState = payload.peerConnectionState;
603
+ observedPeerConnection.emit('connectionstatechange', {
604
+ state: payload.peerConnectionState,
605
+ });
606
+ } else if (postBuffer) {
607
+ postBuffer.push(event);
608
+ } else {
609
+ logger.warn('Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
182
610
  }
183
611
  }
184
612
  break;
185
613
  }
186
614
  }
615
+
616
+ this.emit('clientEvent', event);
617
+ }
618
+
619
+ public injectMetaData(metaData: ClientMetaData) {
620
+ if (this.closed) return;
621
+
622
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
187
623
 
188
- this.call.observer.processors.events.process({
189
- call: this.call,
190
- client: this,
191
- data: event,
192
- });
624
+ this._injections.clientMetaItems.push(metaData);
193
625
  }
194
626
 
195
- public processMetadata(metadata: ClientMetaData) {
627
+ public injectEvent(event: ClientEvent) {
628
+ if (this.closed) return;
629
+
630
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
631
+
632
+ this._injections.clientEvents.push(event);
633
+ }
634
+
635
+ public injectIssue(issue: ClientIssue) {
636
+ if (this.closed) return;
637
+
638
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
639
+
640
+ this._injections.clientIssues.push(issue);
641
+ }
642
+
643
+ public injectExtensionStat(stat: ExtensionStat) {
644
+ if (this.closed) return;
645
+
646
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
647
+
648
+ this._injections.extensionStats.push(stat);
649
+ }
650
+
651
+ public injectAttachment(key: string, value: unknown) {
652
+ if (this.closed) return;
653
+
654
+ if (!this._injections.attachments) this._injections.attachments = {};
655
+
656
+ this._injections.attachments[key] = value;
657
+
658
+ }
659
+
660
+ public addMetadata(metadata: ClientMetaData) {
661
+ if (this.closed) return;
662
+
196
663
  switch (metadata.type) {
197
- case ClientMetaType.BROWSER: {
664
+ case ClientMetaTypes.BROWSER: {
198
665
  this.browser = parseJsonAs(metadata.payload);
199
666
  break;
200
667
  }
201
- case ClientMetaType.ENGINE: {
668
+ case ClientMetaTypes.ENGINE: {
202
669
  this.engine = parseJsonAs(metadata.payload);
203
670
  break;
204
671
  }
205
- case ClientMetaType.PLATFORM: {
672
+ case ClientMetaTypes.PLATFORM: {
206
673
  this.platform = parseJsonAs(metadata.payload);
207
674
  break;
208
675
  }
209
- case ClientMetaType.OPERATION_SYSTEM: {
676
+ case ClientMetaTypes.OPERATION_SYSTEM: {
210
677
  this.operationSystem = parseJsonAs(metadata.payload);
211
678
  break;
212
679
  }
213
680
  }
214
681
 
215
- this.call.observer.processors.metaData.process({
216
- call: this.call,
217
- client: this,
218
- data: metadata,
219
- });
682
+ this.call.observer.emit('client-metadata', this, metadata);
220
683
  }
221
684
 
222
- public processIssue(issue: ClientIssue) {
223
- this.call.observer.processors.issues.process({
224
- call: this.call,
225
- client: this,
226
- data: issue,
227
- });
685
+ public addIssue(issue: ClientIssue) {
686
+ if (this.closed) return;
687
+
688
+ this.emit('issue', issue);
689
+ this.call.observer.emit('client-issue', this, issue);
228
690
  }
229
691
 
230
- public processExtensionStats(stats: ExtensionStat) {
231
- this.call.observer.processors.extensionStats.process({
232
- call: this.call,
233
- client: this,
234
- data: stats,
235
- });
692
+ public addExtensionStats(stats: ExtensionStat) {
693
+ this.call.observer.emit('client-extension-stats', this, stats);
694
+ this.emit('extensionStats', stats);
236
695
  }
237
696
 
238
- private _updatePeerConnection(sample: PeerConnectionSample) {
697
+ private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
239
698
  let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
240
699
 
241
700
  if (!observedPeerConnection) {
242
701
  if (!sample.peerConnectionId) {
243
- return logger.warn(
702
+ return (logger.warn(
244
703
  `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
245
704
  sample
246
- );
705
+ ), void 0);
247
706
  }
248
707
 
249
708
  observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
@@ -252,8 +711,101 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
252
711
  this.observedPeerConnections.delete(sample.peerConnectionId);
253
712
  });
254
713
  this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
714
+
715
+ this.emit('newpeerconnection', observedPeerConnection);
255
716
  }
256
717
 
257
718
  observedPeerConnection.accept(sample);
719
+
720
+ return observedPeerConnection;
258
721
  }
722
+
723
+ private _mergeInjections(sample: ClientSample): ClientSample {
724
+ if (this.closed) return sample;
725
+
726
+ if (this._injections.clientEvents) {
727
+ if (!sample.clientEvents) sample.clientEvents = [];
728
+ sample.clientEvents.push(...this._injections.clientEvents);
729
+
730
+ this._injections.clientEvents = undefined;
731
+ }
732
+
733
+ if (this._injections.clientIssues) {
734
+ if (!sample.clientIssues) sample.clientIssues = [];
735
+ sample.clientIssues.push(...this._injections.clientIssues);
736
+
737
+ this._injections.clientIssues = undefined;
738
+ }
739
+
740
+ if (this._injections.extensionStats) {
741
+ if (!sample.extensionStats) sample.extensionStats = [];
742
+ sample.extensionStats.push(...this._injections.extensionStats);
743
+
744
+ this._injections.extensionStats = undefined;
745
+ }
746
+
747
+ if (this._injections.attachments) {
748
+ if (!sample.attachments) sample.attachments = {};
749
+ Object.assign(sample.attachments, this._injections.attachments);
750
+
751
+ this._injections.attachments = undefined;
752
+ }
753
+
754
+ if (this._injections.clientMetaItems) {
755
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
756
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
757
+
758
+ this._injections.clientMetaItems = undefined;
759
+ }
760
+
761
+ return sample;
762
+ }
763
+
764
+ // public resetSummaryMetrics() {
765
+ // this.totalDataChannelBytesReceived = 0;
766
+ // this.totalDataChannelBytesSent = 0;
767
+ // this.totalDataChannelMessagesReceived = 0;
768
+ // this.totalDataChannelMessagesSent = 0;
769
+ // this.totalInboundPacketsLost = 0;
770
+ // this.totalInboundPacketsReceived = 0;
771
+ // this.totalOutboundPacketsSent = 0;
772
+ // this.totalReceivedAudioBytes = 0;
773
+ // this.totalReceivedVideoBytes = 0;
774
+ // this.totalSentAudioBytes = 0;
775
+ // this.totalSentVideoBytes = 0;
776
+ // this.totalSentBytes = 0;
777
+ // this.totalReceivedBytes = 0;
778
+
779
+ // this.totalNumberOfIssues = 0;
780
+
781
+ // this.totalScoreSum = 0;
782
+ // this.numberOfScoreMeasurements = 0;
783
+ // }
784
+
785
+ // public createSummary(): ObservedClientSummary {
786
+ // return {
787
+ // totalRttLt50Measurements: this.totalRttLt50Measurements,
788
+ // totalRttLt150Measurements: this.totalRttLt150Measurements,
789
+ // totalRttLt300Measurements: this.totalRttLt300Measurements,
790
+ // totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
791
+ // totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
792
+ // totalDataChannelBytesSent: this.totalDataChannelBytesSent,
793
+ // totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
794
+ // totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
795
+ // totalInboundPacketsLost: this.totalInboundPacketsLost,
796
+ // totalInboundPacketsReceived: this.totalInboundPacketsReceived,
797
+ // totalOutboundPacketsSent: this.totalOutboundPacketsSent,
798
+ // totalReceivedAudioBytes: this.totalReceivedAudioBytes,
799
+ // totalReceivedVideoBytes: this.totalReceivedVideoBytes,
800
+ // totalSentAudioBytes: this.totalSentAudioBytes,
801
+ // totalSentVideoBytes: this.totalSentVideoBytes,
802
+ // totalSentBytes: this.totalSentBytes,
803
+ // totalReceivedBytes: this.totalReceivedBytes,
804
+
805
+ // numberOfIssues: this.totalNumberOfIssues,
806
+
807
+ // totalScoreSum: this.totalScoreSum,
808
+ // numberOfScoreMeasurements: this.numberOfScoreMeasurements,
809
+ // };
810
+ // }
259
811
  }