@observertc/observer-js 1.0.0-beta.3 → 1.0.0-beta.5

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 (230) hide show
  1. package/README.md +790 -386
  2. package/dist/index.d.mts +2795 -0
  3. package/dist/index.d.ts +2795 -0
  4. package/dist/index.js +3765 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +3699 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +28 -15
  9. package/.eslintrc.json +0 -143
  10. package/.prettierignore +0 -5
  11. package/.prettierrc +0 -7
  12. package/jest.config.js +0 -9
  13. package/lib/ObservedCall.d.ts +0 -70
  14. package/lib/ObservedCall.d.ts.map +0 -1
  15. package/lib/ObservedCall.js +0 -196
  16. package/lib/ObservedCallEventMonitor.d.ts +0 -107
  17. package/lib/ObservedCallEventMonitor.d.ts.map +0 -1
  18. package/lib/ObservedCallEventMonitor.js +0 -321
  19. package/lib/ObservedCallSummary.d.ts +0 -18
  20. package/lib/ObservedCallSummary.d.ts.map +0 -1
  21. package/lib/ObservedCallSummary.js +0 -2
  22. package/lib/ObservedCertificate.d.ts +0 -19
  23. package/lib/ObservedCertificate.d.ts.map +0 -1
  24. package/lib/ObservedCertificate.js +0 -38
  25. package/lib/ObservedClient.d.ts +0 -115
  26. package/lib/ObservedClient.d.ts.map +0 -1
  27. package/lib/ObservedClient.js +0 -714
  28. package/lib/ObservedClientEventMonitor.d.ts +0 -91
  29. package/lib/ObservedClientEventMonitor.d.ts.map +0 -1
  30. package/lib/ObservedClientEventMonitor.js +0 -254
  31. package/lib/ObservedClientSummary.d.ts +0 -23
  32. package/lib/ObservedClientSummary.d.ts.map +0 -1
  33. package/lib/ObservedClientSummary.js +0 -2
  34. package/lib/ObservedCodec.d.ts +0 -22
  35. package/lib/ObservedCodec.d.ts.map +0 -1
  36. package/lib/ObservedCodec.js +0 -45
  37. package/lib/ObservedDataChannel.d.ts +0 -30
  38. package/lib/ObservedDataChannel.d.ts.map +0 -1
  39. package/lib/ObservedDataChannel.js +0 -75
  40. package/lib/ObservedIceCandidate.d.ts +0 -29
  41. package/lib/ObservedIceCandidate.d.ts.map +0 -1
  42. package/lib/ObservedIceCandidate.js +0 -59
  43. package/lib/ObservedIceCandidatePair.d.ts +0 -45
  44. package/lib/ObservedIceCandidatePair.d.ts.map +0 -1
  45. package/lib/ObservedIceCandidatePair.js +0 -127
  46. package/lib/ObservedIceTransport.d.ts +0 -36
  47. package/lib/ObservedIceTransport.d.ts.map +0 -1
  48. package/lib/ObservedIceTransport.js +0 -93
  49. package/lib/ObservedInboundRtp.d.ts +0 -92
  50. package/lib/ObservedInboundRtp.d.ts.map +0 -1
  51. package/lib/ObservedInboundRtp.js +0 -193
  52. package/lib/ObservedInboundTrack.d.ts +0 -34
  53. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  54. package/lib/ObservedInboundTrack.js +0 -92
  55. package/lib/ObservedMediaPlayout.d.ts +0 -22
  56. package/lib/ObservedMediaPlayout.d.ts.map +0 -1
  57. package/lib/ObservedMediaPlayout.js +0 -42
  58. package/lib/ObservedMediaSource.d.ts +0 -28
  59. package/lib/ObservedMediaSource.d.ts.map +0 -1
  60. package/lib/ObservedMediaSource.js +0 -55
  61. package/lib/ObservedOutboundRtp.d.ts +0 -62
  62. package/lib/ObservedOutboundRtp.d.ts.map +0 -1
  63. package/lib/ObservedOutboundRtp.js +0 -144
  64. package/lib/ObservedOutboundTrack.d.ts +0 -34
  65. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
  66. package/lib/ObservedOutboundTrack.js +0 -65
  67. package/lib/ObservedPeerConnection.d.ts +0 -207
  68. package/lib/ObservedPeerConnection.d.ts.map +0 -1
  69. package/lib/ObservedPeerConnection.js +0 -788
  70. package/lib/ObservedPeerConnectionTransport.d.ts +0 -17
  71. package/lib/ObservedPeerConnectionTransport.d.ts.map +0 -1
  72. package/lib/ObservedPeerConnectionTransport.js +0 -34
  73. package/lib/ObservedRemoteInboundRtp.d.ts +0 -30
  74. package/lib/ObservedRemoteInboundRtp.d.ts.map +0 -1
  75. package/lib/ObservedRemoteInboundRtp.js +0 -60
  76. package/lib/ObservedRemoteOutboundRtp.d.ts +0 -30
  77. package/lib/ObservedRemoteOutboundRtp.d.ts.map +0 -1
  78. package/lib/ObservedRemoteOutboundRtp.js +0 -60
  79. package/lib/ObservedTURN.d.ts +0 -31
  80. package/lib/ObservedTURN.d.ts.map +0 -1
  81. package/lib/ObservedTURN.js +0 -58
  82. package/lib/ObservedTurnServer.d.ts +0 -25
  83. package/lib/ObservedTurnServer.d.ts.map +0 -1
  84. package/lib/ObservedTurnServer.js +0 -59
  85. package/lib/Observer.d.ts +0 -62
  86. package/lib/Observer.d.ts.map +0 -1
  87. package/lib/Observer.js +0 -160
  88. package/lib/ObserverEventMonitor.d.ts +0 -141
  89. package/lib/ObserverEventMonitor.d.ts.map +0 -1
  90. package/lib/ObserverEventMonitor.js +0 -440
  91. package/lib/ObserverSummary.d.ts +0 -13
  92. package/lib/ObserverSummary.d.ts.map +0 -1
  93. package/lib/ObserverSummary.js +0 -2
  94. package/lib/Reports.d.ts +0 -85
  95. package/lib/Reports.d.ts.map +0 -1
  96. package/lib/Reports.js +0 -2
  97. package/lib/common/Middleware.d.ts +0 -17
  98. package/lib/common/Middleware.d.ts.map +0 -1
  99. package/lib/common/Middleware.js +0 -60
  100. package/lib/common/SingleExecutor.d.ts +0 -3
  101. package/lib/common/SingleExecutor.d.ts.map +0 -1
  102. package/lib/common/SingleExecutor.js +0 -31
  103. package/lib/common/logger.d.ts +0 -17
  104. package/lib/common/logger.d.ts.map +0 -1
  105. package/lib/common/logger.js +0 -50
  106. package/lib/common/types.d.ts +0 -3
  107. package/lib/common/types.d.ts.map +0 -1
  108. package/lib/common/types.js +0 -3
  109. package/lib/common/utils.d.ts +0 -11
  110. package/lib/common/utils.d.ts.map +0 -1
  111. package/lib/common/utils.js +0 -63
  112. package/lib/detectors/Detector.d.ts +0 -5
  113. package/lib/detectors/Detector.d.ts.map +0 -1
  114. package/lib/detectors/Detector.js +0 -3
  115. package/lib/detectors/Detectors.d.ts +0 -11
  116. package/lib/detectors/Detectors.d.ts.map +0 -1
  117. package/lib/detectors/Detectors.js +0 -34
  118. package/lib/index.d.ts +0 -29
  119. package/lib/index.d.ts.map +0 -1
  120. package/lib/index.js +0 -49
  121. package/lib/mediasoup/ObservedMediaRouter.d.ts +0 -10
  122. package/lib/mediasoup/ObservedMediaRouter.d.ts.map +0 -1
  123. package/lib/mediasoup/ObservedMediaRouter.js +0 -6
  124. package/lib/monitors/TurnUsageMonitor.d.ts +0 -1
  125. package/lib/monitors/TurnUsageMonitor.d.ts.map +0 -1
  126. package/lib/monitors/TurnUsageMonitor.js +0 -146
  127. package/lib/schema/ClientEventTypes.d.ts +0 -228
  128. package/lib/schema/ClientEventTypes.d.ts.map +0 -1
  129. package/lib/schema/ClientEventTypes.js +0 -41
  130. package/lib/schema/ClientMetaTypes.d.ts +0 -34
  131. package/lib/schema/ClientMetaTypes.d.ts.map +0 -1
  132. package/lib/schema/ClientMetaTypes.js +0 -16
  133. package/lib/schema/ClientSample.d.ts +0 -1333
  134. package/lib/schema/ClientSample.d.ts.map +0 -1
  135. package/lib/schema/ClientSample.js +0 -4
  136. package/lib/scores/CalculatedScore.d.ts +0 -6
  137. package/lib/scores/CalculatedScore.d.ts.map +0 -1
  138. package/lib/scores/CalculatedScore.js +0 -2
  139. package/lib/scores/DefaultCallScoreCalculator.d.ts +0 -7
  140. package/lib/scores/DefaultCallScoreCalculator.d.ts.map +0 -1
  141. package/lib/scores/DefaultCallScoreCalculator.js +0 -21
  142. package/lib/scores/ScoreCalculator.d.ts +0 -4
  143. package/lib/scores/ScoreCalculator.d.ts.map +0 -1
  144. package/lib/scores/ScoreCalculator.js +0 -2
  145. package/lib/updaters/CallUpdater.d.ts +0 -5
  146. package/lib/updaters/CallUpdater.d.ts.map +0 -1
  147. package/lib/updaters/CallUpdater.js +0 -2
  148. package/lib/updaters/ObserverUpdater.d.ts +0 -5
  149. package/lib/updaters/ObserverUpdater.d.ts.map +0 -1
  150. package/lib/updaters/ObserverUpdater.js +0 -2
  151. package/lib/updaters/OnAllCallObserverUpdater.d.ts +0 -15
  152. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +0 -1
  153. package/lib/updaters/OnAllCallObserverUpdater.js +0 -50
  154. package/lib/updaters/OnAllClientCallUpdater.d.ts +0 -15
  155. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +0 -1
  156. package/lib/updaters/OnAllClientCallUpdater.js +0 -53
  157. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +0 -12
  158. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +0 -1
  159. package/lib/updaters/OnAnyCallObserverUpdater.js +0 -37
  160. package/lib/updaters/OnAnyClientCallUpdater.d.ts +0 -12
  161. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +0 -1
  162. package/lib/updaters/OnAnyClientCallUpdater.js +0 -36
  163. package/lib/updaters/OnIntervalUpdater.d.ts +0 -10
  164. package/lib/updaters/OnIntervalUpdater.d.ts.map +0 -1
  165. package/lib/updaters/OnIntervalUpdater.js +0 -17
  166. package/lib/updaters/Updater.d.ts +0 -6
  167. package/lib/updaters/Updater.d.ts.map +0 -1
  168. package/lib/updaters/Updater.js +0 -2
  169. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +0 -25
  170. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +0 -1
  171. package/lib/utils/MediasoupRemoteTrackResolver.js +0 -110
  172. package/lib/utils/RemoteTrackResolver.d.ts +0 -7
  173. package/lib/utils/RemoteTrackResolver.d.ts.map +0 -1
  174. package/lib/utils/RemoteTrackResolver.js +0 -2
  175. package/src/ObservedCall.ts +0 -308
  176. package/src/ObservedCallEventMonitor.ts +0 -402
  177. package/src/ObservedCallSummary.ts +0 -22
  178. package/src/ObservedCertificate.ts +0 -43
  179. package/src/ObservedClient.ts +0 -883
  180. package/src/ObservedClientEventMonitor.ts +0 -309
  181. package/src/ObservedClientSummary.ts +0 -24
  182. package/src/ObservedCodec.ts +0 -49
  183. package/src/ObservedDataChannel.ts +0 -85
  184. package/src/ObservedIceCandidate.ts +0 -64
  185. package/src/ObservedIceCandidatePair.ts +0 -134
  186. package/src/ObservedIceTransport.ts +0 -99
  187. package/src/ObservedInboundRtp.ts +0 -205
  188. package/src/ObservedInboundTrack.ts +0 -105
  189. package/src/ObservedMediaPlayout.ts +0 -49
  190. package/src/ObservedMediaSource.ts +0 -60
  191. package/src/ObservedOutboundRtp.ts +0 -156
  192. package/src/ObservedOutboundTrack.ts +0 -82
  193. package/src/ObservedPeerConnection.ts +0 -1104
  194. package/src/ObservedPeerConnectionTransport.ts +0 -38
  195. package/src/ObservedRemoteInboundRtp.ts +0 -64
  196. package/src/ObservedRemoteOutboundRtp.ts +0 -65
  197. package/src/ObservedTURN.ts +0 -86
  198. package/src/ObservedTurnServer.ts +0 -72
  199. package/src/Observer.ts +0 -248
  200. package/src/ObserverEventMonitor.ts +0 -561
  201. package/src/ObserverSummary.ts +0 -15
  202. package/src/Reports.ts +0 -87
  203. package/src/common/Middleware.ts +0 -84
  204. package/src/common/SingleExecutor.ts +0 -36
  205. package/src/common/logger.ts +0 -82
  206. package/src/common/types.ts +0 -4
  207. package/src/common/utils.ts +0 -69
  208. package/src/detectors/Detector.ts +0 -6
  209. package/src/detectors/Detectors.ts +0 -38
  210. package/src/index.ts +0 -32
  211. package/src/mediasoup/ObservedMediaRouter.ts +0 -10
  212. package/src/monitors/TurnUsageMonitor.ts +0 -187
  213. package/src/schema/ClientEventTypes.ts +0 -280
  214. package/src/schema/ClientMetaTypes.ts +0 -41
  215. package/src/schema/ClientSample.ts +0 -1682
  216. package/src/scores/CalculatedScore.ts +0 -6
  217. package/src/scores/DefaultCallScoreCalculator.ts +0 -23
  218. package/src/scores/ScoreCalculator.ts +0 -3
  219. package/src/updaters/CallUpdater.ts +0 -5
  220. package/src/updaters/ObserverUpdater.ts +0 -5
  221. package/src/updaters/OnAllCallObserverUpdater.ts +0 -59
  222. package/src/updaters/OnAllClientCallUpdater.ts +0 -61
  223. package/src/updaters/OnAnyCallObserverUpdater.ts +0 -41
  224. package/src/updaters/OnAnyClientCallUpdater.ts +0 -39
  225. package/src/updaters/OnIntervalUpdater.ts +0 -19
  226. package/src/updaters/Updater.ts +0 -5
  227. package/src/utils/MediasoupRemoteTrackResolver.ts +0 -155
  228. package/src/utils/RemoteTrackResolver.ts +0 -12
  229. package/tsconfig.json +0 -19
  230. package/tslint.json +0 -6
package/dist/index.mjs ADDED
@@ -0,0 +1,3699 @@
1
+ // src/common/logger.ts
2
+ var mainLogger = new class {
3
+ trace = () => void 0;
4
+ // trace(module: string, ...args: any[]) {
5
+ // // eslint-disable-next-line no-console
6
+ // console.log(`[TRACE] ${module}`, ...args);
7
+ // }
8
+ debug(module, ...args) {
9
+ console.log(`[DEBUG] ${module}`, ...args);
10
+ }
11
+ info(module, ...args) {
12
+ console.info(`[INFO] ${module}`, ...args);
13
+ }
14
+ warn(module, ...args) {
15
+ console.warn(`[WARN] ${module}`, ...args);
16
+ }
17
+ error(module, ...args) {
18
+ console.error(`[ERROR] ${module}`, ...args);
19
+ }
20
+ }();
21
+ function createLogger(moduleName) {
22
+ return new class {
23
+ trace(...args) {
24
+ mainLogger.trace(moduleName, ...args);
25
+ }
26
+ debug(...args) {
27
+ mainLogger.debug(moduleName, ...args);
28
+ }
29
+ info(...args) {
30
+ mainLogger.info(moduleName, ...args);
31
+ }
32
+ warn(...args) {
33
+ mainLogger.warn(moduleName, ...args);
34
+ }
35
+ error(...args) {
36
+ mainLogger.error(moduleName, ...args);
37
+ }
38
+ }();
39
+ }
40
+ function setObserverLogger(logger7) {
41
+ mainLogger = logger7;
42
+ }
43
+
44
+ // src/ObservedCall.ts
45
+ import { EventEmitter as EventEmitter3 } from "events";
46
+
47
+ // src/ObservedClient.ts
48
+ import { EventEmitter as EventEmitter2 } from "events";
49
+
50
+ // src/ObservedPeerConnection.ts
51
+ import { EventEmitter } from "events";
52
+
53
+ // src/ObservedInboundRtp.ts
54
+ var ObservedInboundRtp = class {
55
+ constructor(timestamp, id, ssrc, kind, trackIdentifier, _peerConnection) {
56
+ this.timestamp = timestamp;
57
+ this.id = id;
58
+ this.ssrc = ssrc;
59
+ this.kind = kind;
60
+ this.trackIdentifier = trackIdentifier;
61
+ this._peerConnection = _peerConnection;
62
+ }
63
+ timestamp;
64
+ id;
65
+ ssrc;
66
+ kind;
67
+ trackIdentifier;
68
+ _peerConnection;
69
+ appData;
70
+ _visited = false;
71
+ transportId;
72
+ codecId;
73
+ packetsReceived;
74
+ packetsLost;
75
+ mid;
76
+ remoteId;
77
+ framesDecoded;
78
+ keyFramesDecoded;
79
+ framesRendered;
80
+ framesDropped;
81
+ frameWidth;
82
+ frameHeight;
83
+ framesPerSecond;
84
+ qpSum;
85
+ totalDecodeTime;
86
+ totalInterFrameDelay;
87
+ totalSquaredInterFrameDelay;
88
+ pauseCount;
89
+ totalPausesDuration;
90
+ freezeCount;
91
+ totalFreezesDuration;
92
+ lastPacketReceivedTimestamp;
93
+ headerBytesReceived;
94
+ packetsDiscarded;
95
+ fecBytesReceived;
96
+ fecPacketsReceived;
97
+ fecPacketsDiscarded;
98
+ bytesReceived;
99
+ nackCount;
100
+ firCount;
101
+ pliCount;
102
+ totalProcessingDelay;
103
+ estimatedPlayoutTimestamp;
104
+ jitterBufferDelay;
105
+ jitterBufferTargetDelay;
106
+ jitterBufferEmittedCount;
107
+ jitterBufferMinimumDelay;
108
+ totalSamplesReceived;
109
+ concealedSamples;
110
+ silentConcealedSamples;
111
+ concealmentEvents;
112
+ insertedSamplesForDeceleration;
113
+ removedSamplesForAcceleration;
114
+ audioLevel;
115
+ totalAudioEnergy;
116
+ totalSamplesDuration;
117
+ framesReceived;
118
+ decoderImplementation;
119
+ playoutId;
120
+ powerEfficientDecoder;
121
+ framesAssembledFromMultiplePackets;
122
+ totalAssemblyTime;
123
+ retransmittedPacketsReceived;
124
+ retransmittedBytesReceived;
125
+ rtxSsrc;
126
+ fecSsrc;
127
+ totalCorruptionProbability;
128
+ totalSquaredCorruptionProbability;
129
+ corruptionMeasurements;
130
+ attachments;
131
+ jitter;
132
+ bitrate = 0;
133
+ fractionLost;
134
+ bitPerPixel = 0;
135
+ deltaLostPackets = 0;
136
+ deltaReceivedPackets = 0;
137
+ deltaBytesReceived = 0;
138
+ deltaReceivedSamples = 0;
139
+ deltaSilentConcealedSamples = 0;
140
+ // Derived from the corresponding remote-outbound-rtp (sender report), when present.
141
+ remoteRttInMs;
142
+ remoteBytesSent;
143
+ remotePacketsSent;
144
+ remoteTimestamp;
145
+ get visited() {
146
+ const visited = this._visited;
147
+ this._visited = false;
148
+ return visited;
149
+ }
150
+ getPeerConnection() {
151
+ return this._peerConnection;
152
+ }
153
+ getRemoteOutboundRtp() {
154
+ return this._peerConnection.observedRemoteOutboundRtps.get(this.ssrc);
155
+ }
156
+ getIceTransport() {
157
+ return this._peerConnection.observedIceTransports.get(this.transportId ?? "");
158
+ }
159
+ getCodec() {
160
+ return this._peerConnection.observedCodecs.get(this.codecId ?? "");
161
+ }
162
+ getMediaPlayout() {
163
+ return this._peerConnection.observedMediaPlayouts.get(this.playoutId ?? "");
164
+ }
165
+ getTrack() {
166
+ return this._peerConnection.observedInboundTracks.get(this.trackIdentifier);
167
+ }
168
+ update(stats) {
169
+ this._visited = true;
170
+ this.deltaBytesReceived = 0;
171
+ this.deltaLostPackets = 0;
172
+ this.deltaReceivedPackets = 0;
173
+ this.deltaReceivedSamples = 0;
174
+ this.deltaSilentConcealedSamples = 0;
175
+ this.remoteRttInMs = void 0;
176
+ this.remoteBytesSent = void 0;
177
+ this.remotePacketsSent = void 0;
178
+ this.remoteTimestamp = void 0;
179
+ this.bitrate = 0;
180
+ this.jitter = void 0;
181
+ this.fractionLost = void 0;
182
+ this.bitPerPixel = 0;
183
+ const elapsedTimeInMs = stats.timestamp - this.timestamp;
184
+ if (elapsedTimeInMs) {
185
+ if (this.bytesReceived && stats.bytesReceived && this.bytesReceived < stats.bytesReceived) {
186
+ this.bitrate = (stats.bytesReceived - (this.bytesReceived ?? 0)) * 8 / elapsedTimeInMs;
187
+ }
188
+ if (this.packetsLost && stats.packetsLost && this.packetsLost < stats.packetsLost) {
189
+ this.deltaLostPackets = stats.packetsLost - this.packetsLost;
190
+ }
191
+ if (this.packetsReceived && stats.packetsReceived && this.packetsReceived < stats.packetsReceived) {
192
+ this.deltaReceivedPackets = stats.packetsReceived - this.packetsReceived;
193
+ }
194
+ if (this.totalSamplesReceived && stats.totalSamplesReceived && this.totalSamplesReceived < stats.totalSamplesReceived) {
195
+ this.deltaReceivedSamples = stats.totalSamplesReceived - this.totalSamplesReceived;
196
+ }
197
+ if (this.silentConcealedSamples && stats.silentConcealedSamples && this.silentConcealedSamples < stats.silentConcealedSamples) {
198
+ this.deltaSilentConcealedSamples = stats.silentConcealedSamples - this.silentConcealedSamples;
199
+ }
200
+ if (stats.bytesReceived && this.framesReceived && stats.framesReceived && this.framesReceived < stats.framesReceived) {
201
+ this.bitPerPixel = (stats.bytesReceived - (this.bytesReceived ?? 0)) / (stats.framesReceived - this.framesReceived);
202
+ }
203
+ if (this.deltaLostPackets && this.deltaReceivedPackets) {
204
+ this.fractionLost = this.deltaLostPackets / (this.deltaLostPackets + this.deltaReceivedPackets);
205
+ }
206
+ }
207
+ this.timestamp = stats.timestamp;
208
+ this.transportId = stats.transportId;
209
+ this.codecId = stats.codecId;
210
+ this.packetsReceived = stats.packetsReceived;
211
+ this.packetsLost = stats.packetsLost;
212
+ this.mid = stats.mid;
213
+ this.remoteId = stats.remoteId;
214
+ this.framesDecoded = stats.framesDecoded;
215
+ this.keyFramesDecoded = stats.keyFramesDecoded;
216
+ this.framesRendered = stats.framesRendered;
217
+ this.framesDropped = stats.framesDropped;
218
+ this.frameWidth = stats.frameWidth;
219
+ this.frameHeight = stats.frameHeight;
220
+ this.framesPerSecond = stats.framesPerSecond;
221
+ this.qpSum = stats.qpSum;
222
+ this.totalDecodeTime = stats.totalDecodeTime;
223
+ this.totalInterFrameDelay = stats.totalInterFrameDelay;
224
+ this.totalSquaredInterFrameDelay = stats.totalSquaredInterFrameDelay;
225
+ this.pauseCount = stats.pauseCount;
226
+ this.totalPausesDuration = stats.totalPausesDuration;
227
+ this.freezeCount = stats.freezeCount;
228
+ this.totalFreezesDuration = stats.totalFreezesDuration;
229
+ this.lastPacketReceivedTimestamp = stats.lastPacketReceivedTimestamp;
230
+ this.headerBytesReceived = stats.headerBytesReceived;
231
+ this.packetsDiscarded = stats.packetsDiscarded;
232
+ this.fecBytesReceived = stats.fecBytesReceived;
233
+ this.fecPacketsReceived = stats.fecPacketsReceived;
234
+ this.fecPacketsDiscarded = stats.fecPacketsDiscarded;
235
+ this.bytesReceived = stats.bytesReceived;
236
+ this.nackCount = stats.nackCount;
237
+ this.firCount = stats.firCount;
238
+ this.pliCount = stats.pliCount;
239
+ this.totalProcessingDelay = stats.totalProcessingDelay;
240
+ this.estimatedPlayoutTimestamp = stats.estimatedPlayoutTimestamp;
241
+ this.jitterBufferDelay = stats.jitterBufferDelay;
242
+ this.jitterBufferTargetDelay = stats.jitterBufferTargetDelay;
243
+ this.jitterBufferEmittedCount = stats.jitterBufferEmittedCount;
244
+ this.jitterBufferMinimumDelay = stats.jitterBufferMinimumDelay;
245
+ this.totalSamplesReceived = stats.totalSamplesReceived;
246
+ this.concealedSamples = stats.concealedSamples;
247
+ this.silentConcealedSamples = stats.silentConcealedSamples;
248
+ this.concealmentEvents = stats.concealmentEvents;
249
+ }
250
+ };
251
+
252
+ // src/ObservedOutboundRtp.ts
253
+ var ObservedOutboundRtp = class {
254
+ constructor(timestamp, id, ssrc, kind, _peerConnection) {
255
+ this.timestamp = timestamp;
256
+ this.id = id;
257
+ this.ssrc = ssrc;
258
+ this.kind = kind;
259
+ this._peerConnection = _peerConnection;
260
+ }
261
+ timestamp;
262
+ id;
263
+ ssrc;
264
+ kind;
265
+ _peerConnection;
266
+ _visited = false;
267
+ appData;
268
+ transportId;
269
+ codecId;
270
+ packetsSent;
271
+ bytesSent;
272
+ mid;
273
+ mediaSourceId;
274
+ remoteId;
275
+ rid;
276
+ headerBytesSent;
277
+ retransmittedPacketsSent;
278
+ retransmittedBytesSent;
279
+ rtxSsrc;
280
+ targetBitrate;
281
+ totalEncodedBytesTarget;
282
+ frameWidth;
283
+ frameHeight;
284
+ framesPerSecond;
285
+ framesSent;
286
+ hugeFramesSent;
287
+ framesEncoded;
288
+ keyFramesEncoded;
289
+ qpSum;
290
+ totalEncodeTime;
291
+ totalPacketSendDelay;
292
+ qualityLimitationReason;
293
+ qualityLimitationResolutionChanges;
294
+ nackCount;
295
+ firCount;
296
+ pliCount;
297
+ encoderImplementation;
298
+ powerEfficientEncoder;
299
+ active;
300
+ scalabilityMode;
301
+ qualityLimitationDurations;
302
+ attachments;
303
+ // derived fields
304
+ bitrate = 0;
305
+ payloadBitrate = 0;
306
+ packetRate = 0;
307
+ bitPerPixel = 0;
308
+ deltaPacketsSent = 0;
309
+ deltaBytesSent = 0;
310
+ // Derived from the corresponding remote-inbound-rtp (receiver report), when present.
311
+ remoteRttInMs;
312
+ remoteFractionLost;
313
+ remoteJitter;
314
+ remotePacketsLost;
315
+ get visited() {
316
+ const visited = this._visited;
317
+ this._visited = false;
318
+ return visited;
319
+ }
320
+ getPeerConnection() {
321
+ return this._peerConnection;
322
+ }
323
+ getRemoteInboundRtp() {
324
+ return this._peerConnection.observedRemoteInboundRtps.get(this.ssrc);
325
+ }
326
+ getCodec() {
327
+ return this._peerConnection.observedCodecs.get(this.codecId ?? "");
328
+ }
329
+ getMediaSource() {
330
+ return this._peerConnection.observedMediaSources.get(this.mediaSourceId ?? "");
331
+ }
332
+ getTrack() {
333
+ return this.getMediaSource()?.getTrack();
334
+ }
335
+ update(stats) {
336
+ this._visited = true;
337
+ this.bitPerPixel = 0;
338
+ this.bitrate = 0;
339
+ this.payloadBitrate = 0;
340
+ this.packetRate = 0;
341
+ this.deltaPacketsSent = 0;
342
+ this.deltaBytesSent = 0;
343
+ this.remoteRttInMs = void 0;
344
+ this.remoteFractionLost = void 0;
345
+ this.remoteJitter = void 0;
346
+ this.remotePacketsLost = void 0;
347
+ const elapsedTimeInMs = stats.timestamp - this.timestamp;
348
+ if (elapsedTimeInMs) {
349
+ if (stats.packetsSent !== void 0 && this.packetsSent !== void 0 && stats.packetsSent >= this.packetsSent) {
350
+ this.deltaPacketsSent = stats.packetsSent - this.packetsSent;
351
+ this.packetRate = this.deltaPacketsSent / (elapsedTimeInMs / 1e3);
352
+ }
353
+ if (stats.bytesSent !== void 0 && this.bytesSent !== void 0 && stats.bytesSent >= this.bytesSent) {
354
+ this.deltaBytesSent = stats.bytesSent - this.bytesSent;
355
+ this.bitrate = this.deltaBytesSent * 8 / (elapsedTimeInMs / 1e3);
356
+ }
357
+ if (stats.headerBytesSent !== void 0 && this.headerBytesSent !== void 0) {
358
+ this.payloadBitrate = (this.deltaBytesSent ?? 0 - (stats.headerBytesSent - this.headerBytesSent)) * 8 / (elapsedTimeInMs / 1e3);
359
+ }
360
+ if (this.framesSent !== void 0 && stats.framesSent !== void 0) {
361
+ this.bitPerPixel = this.deltaBytesSent ? this.deltaBytesSent * 8 / (this.framesSent - stats.framesSent) : 0;
362
+ }
363
+ }
364
+ this.timestamp = stats.timestamp;
365
+ this.transportId = stats.transportId;
366
+ this.codecId = stats.codecId;
367
+ this.packetsSent = stats.packetsSent;
368
+ this.bytesSent = stats.bytesSent;
369
+ this.mid = stats.mid;
370
+ this.mediaSourceId = stats.mediaSourceId;
371
+ this.remoteId = stats.remoteId;
372
+ this.rid = stats.rid;
373
+ this.headerBytesSent = stats.headerBytesSent;
374
+ this.retransmittedPacketsSent = stats.retransmittedPacketsSent;
375
+ this.retransmittedBytesSent = stats.retransmittedBytesSent;
376
+ this.rtxSsrc = stats.rtxSsrc;
377
+ this.targetBitrate = stats.targetBitrate;
378
+ this.totalEncodedBytesTarget = stats.totalEncodedBytesTarget;
379
+ this.frameWidth = stats.frameWidth;
380
+ this.frameHeight = stats.frameHeight;
381
+ this.framesPerSecond = stats.framesPerSecond;
382
+ this.framesSent = stats.framesSent;
383
+ this.hugeFramesSent = stats.hugeFramesSent;
384
+ this.framesEncoded = stats.framesEncoded;
385
+ this.keyFramesEncoded = stats.keyFramesEncoded;
386
+ this.qpSum = stats.qpSum;
387
+ this.totalEncodeTime = stats.totalEncodeTime;
388
+ this.totalPacketSendDelay = stats.totalPacketSendDelay;
389
+ this.qualityLimitationReason = stats.qualityLimitationReason;
390
+ this.qualityLimitationResolutionChanges = stats.qualityLimitationResolutionChanges;
391
+ this.nackCount = stats.nackCount;
392
+ this.firCount = stats.firCount;
393
+ this.pliCount = stats.pliCount;
394
+ this.encoderImplementation = stats.encoderImplementation;
395
+ this.powerEfficientEncoder = stats.powerEfficientEncoder;
396
+ this.active = stats.active;
397
+ this.scalabilityMode = stats.scalabilityMode;
398
+ this.qualityLimitationDurations = stats.qualityLimitationDurations;
399
+ this.attachments = stats.attachments;
400
+ }
401
+ };
402
+
403
+ // src/ObservedCertificate.ts
404
+ var ObservedCertificate = class {
405
+ constructor(timestamp, id, _peerConnection) {
406
+ this.timestamp = timestamp;
407
+ this.id = id;
408
+ this._peerConnection = _peerConnection;
409
+ }
410
+ timestamp;
411
+ id;
412
+ _peerConnection;
413
+ appData;
414
+ _visited = false;
415
+ fingerprint;
416
+ fingerprintAlgorithm;
417
+ base64Certificate;
418
+ issuerCertificateId;
419
+ attachments;
420
+ get visited() {
421
+ const visited = this._visited;
422
+ this._visited = false;
423
+ return visited;
424
+ }
425
+ getPeerConnection() {
426
+ return this._peerConnection;
427
+ }
428
+ update(stats) {
429
+ this._visited = true;
430
+ this.timestamp = stats.timestamp;
431
+ this.fingerprint = stats.fingerprint;
432
+ this.fingerprintAlgorithm = stats.fingerprintAlgorithm;
433
+ this.base64Certificate = stats.base64Certificate;
434
+ this.issuerCertificateId = stats.issuerCertificateId;
435
+ this.attachments = stats.attachments;
436
+ }
437
+ };
438
+
439
+ // src/ObservedCodec.ts
440
+ var ObservedCodec = class {
441
+ constructor(timestamp, id, mimeType, _peerConnection) {
442
+ this.timestamp = timestamp;
443
+ this.id = id;
444
+ this.mimeType = mimeType;
445
+ this._peerConnection = _peerConnection;
446
+ }
447
+ timestamp;
448
+ id;
449
+ mimeType;
450
+ _peerConnection;
451
+ _visited = false;
452
+ appData;
453
+ payloadType;
454
+ transportId;
455
+ clockRate;
456
+ channels;
457
+ sdpFmtpLine;
458
+ attachments;
459
+ get visited() {
460
+ const visited = this._visited;
461
+ this._visited = false;
462
+ return visited;
463
+ }
464
+ getPeerConnection() {
465
+ return this._peerConnection;
466
+ }
467
+ getIceTransport() {
468
+ return this._peerConnection.observedIceTransports.get(this.transportId ?? "");
469
+ }
470
+ update(stats) {
471
+ this._visited = true;
472
+ this.timestamp = stats.timestamp;
473
+ this.payloadType = stats.payloadType;
474
+ this.transportId = stats.transportId;
475
+ this.clockRate = stats.clockRate;
476
+ this.channels = stats.channels;
477
+ this.sdpFmtpLine = stats.sdpFmtpLine;
478
+ this.attachments = stats.attachments;
479
+ }
480
+ };
481
+
482
+ // src/ObservedDataChannel.ts
483
+ var ObservedDataChannel = class {
484
+ constructor(timestamp, id, _peerConnection) {
485
+ this.timestamp = timestamp;
486
+ this.id = id;
487
+ this._peerConnection = _peerConnection;
488
+ }
489
+ timestamp;
490
+ id;
491
+ _peerConnection;
492
+ _visited = false;
493
+ label;
494
+ protocol;
495
+ dataChannelIdentifier;
496
+ state;
497
+ messagesSent;
498
+ bytesSent;
499
+ messagesReceived;
500
+ bytesReceived;
501
+ attachments;
502
+ addedAt;
503
+ removedAt;
504
+ deltaBytesSent = 0;
505
+ deltaBytesReceived = 0;
506
+ deltaMessagesSent = 0;
507
+ deltaMessagesReceived = 0;
508
+ appData;
509
+ get visited() {
510
+ const visited = this._visited;
511
+ this._visited = false;
512
+ return visited;
513
+ }
514
+ getPeerConnection() {
515
+ return this._peerConnection;
516
+ }
517
+ update(stats) {
518
+ this._visited = true;
519
+ if (this.messagesSent && stats.messagesSent && stats.messagesSent >= this.messagesSent) {
520
+ this.deltaMessagesSent = stats.messagesSent - this.messagesSent;
521
+ } else {
522
+ this.deltaMessagesSent = 0;
523
+ }
524
+ if (this.messagesReceived && stats.messagesReceived && stats.messagesReceived >= this.messagesReceived) {
525
+ this.deltaMessagesReceived = stats.messagesReceived - this.messagesReceived;
526
+ } else {
527
+ this.deltaMessagesReceived = 0;
528
+ }
529
+ if (this.bytesSent && stats.bytesSent && stats.bytesSent >= this.bytesSent) {
530
+ this.deltaBytesSent = stats.bytesSent - this.bytesSent;
531
+ } else {
532
+ this.deltaBytesSent = 0;
533
+ }
534
+ if (this.bytesReceived && stats.bytesReceived && stats.bytesReceived >= this.bytesReceived) {
535
+ this.deltaBytesReceived = stats.bytesReceived - this.bytesReceived;
536
+ } else {
537
+ this.deltaBytesReceived = 0;
538
+ }
539
+ this.label = stats.label;
540
+ this.protocol = stats.protocol;
541
+ this.dataChannelIdentifier = stats.dataChannelIdentifier;
542
+ this.state = stats.state;
543
+ this.messagesSent = stats.messagesSent;
544
+ this.bytesSent = stats.bytesSent;
545
+ this.messagesReceived = stats.messagesReceived;
546
+ this.bytesReceived = stats.bytesReceived;
547
+ this.attachments = stats.attachments;
548
+ }
549
+ };
550
+
551
+ // src/ObservedIceCandidate.ts
552
+ var ObservedIceCandidate = class {
553
+ constructor(timestamp, id, _peerConnection) {
554
+ this.timestamp = timestamp;
555
+ this.id = id;
556
+ this._peerConnection = _peerConnection;
557
+ }
558
+ timestamp;
559
+ id;
560
+ _peerConnection;
561
+ _visited = false;
562
+ appData;
563
+ transportId;
564
+ address;
565
+ port;
566
+ protocol;
567
+ candidateType;
568
+ priority;
569
+ url;
570
+ relayProtocol;
571
+ foundation;
572
+ relatedAddress;
573
+ relatedPort;
574
+ usernameFragment;
575
+ tcpType;
576
+ attachments;
577
+ get visited() {
578
+ const visited = this._visited;
579
+ this._visited = false;
580
+ return visited;
581
+ }
582
+ getPeerConnection() {
583
+ return this._peerConnection;
584
+ }
585
+ getIceTransport() {
586
+ return this._peerConnection.observedIceTransports.get(this.transportId ?? "");
587
+ }
588
+ update(stats) {
589
+ this._visited = true;
590
+ this.timestamp = stats.timestamp;
591
+ this.transportId = stats.transportId;
592
+ this.address = stats.address;
593
+ this.port = stats.port;
594
+ this.protocol = stats.protocol;
595
+ this.candidateType = stats.candidateType;
596
+ this.priority = stats.priority;
597
+ this.url = stats.url;
598
+ this.relayProtocol = stats.relayProtocol;
599
+ this.foundation = stats.foundation;
600
+ this.relatedAddress = stats.relatedAddress;
601
+ this.relatedPort = stats.relatedPort;
602
+ this.usernameFragment = stats.usernameFragment;
603
+ this.tcpType = stats.tcpType;
604
+ this.attachments = stats.attachments;
605
+ }
606
+ };
607
+
608
+ // src/ObservedIceCandidatePair.ts
609
+ var ObservedIceCandidatePair = class {
610
+ constructor(timestamp, id, _peerConnection) {
611
+ this.timestamp = timestamp;
612
+ this.id = id;
613
+ this._peerConnection = _peerConnection;
614
+ }
615
+ timestamp;
616
+ id;
617
+ _peerConnection;
618
+ _visited = false;
619
+ appData;
620
+ transportId;
621
+ localCandidateId;
622
+ remoteCandidateId;
623
+ state;
624
+ nominated;
625
+ packetsSent;
626
+ packetsReceived;
627
+ bytesSent;
628
+ bytesReceived;
629
+ lastPacketSentTimestamp;
630
+ lastPacketReceivedTimestamp;
631
+ totalRoundTripTime;
632
+ currentRoundTripTime;
633
+ availableOutgoingBitrate;
634
+ availableIncomingBitrate;
635
+ requestsReceived;
636
+ requestsSent;
637
+ responsesReceived;
638
+ responsesSent;
639
+ consentRequestsSent;
640
+ packetsDiscardedOnSend;
641
+ bytesDiscardedOnSend;
642
+ attachments;
643
+ deltaBytesSent = 0;
644
+ deltaBytesReceived = 0;
645
+ deltaPacketsSent = 0;
646
+ deltaPacketsReceived = 0;
647
+ get visited() {
648
+ const visited = this._visited;
649
+ this._visited = false;
650
+ return visited;
651
+ }
652
+ getPeerConnection() {
653
+ return this._peerConnection;
654
+ }
655
+ getIceTransport() {
656
+ return this._peerConnection.observedIceTransports.get(this.transportId ?? "");
657
+ }
658
+ getLocalCandidate() {
659
+ return this._peerConnection.observedIceCandidates.get(this.localCandidateId ?? "");
660
+ }
661
+ getRemoteCandidate() {
662
+ return this._peerConnection.observedIceCandidates.get(this.remoteCandidateId ?? "");
663
+ }
664
+ update(stats) {
665
+ this._visited = true;
666
+ if (this.packetsSent && stats.packetsSent && stats.packetsSent >= this.packetsSent) {
667
+ this.deltaPacketsSent = stats.packetsSent - this.packetsSent;
668
+ } else {
669
+ this.deltaPacketsSent = 0;
670
+ }
671
+ if (this.packetsReceived && stats.packetsReceived && stats.packetsReceived >= this.packetsReceived) {
672
+ this.deltaPacketsReceived = stats.packetsReceived - this.packetsReceived;
673
+ } else {
674
+ this.deltaPacketsReceived = 0;
675
+ }
676
+ if (this.bytesSent && stats.bytesSent && stats.bytesSent >= this.bytesSent) {
677
+ this.deltaBytesSent = stats.bytesSent - this.bytesSent;
678
+ } else {
679
+ this.deltaBytesSent = 0;
680
+ }
681
+ if (this.bytesReceived && stats.bytesReceived && stats.bytesReceived >= this.bytesReceived) {
682
+ this.deltaBytesReceived = stats.bytesReceived - this.bytesReceived;
683
+ } else {
684
+ this.deltaBytesReceived = 0;
685
+ }
686
+ this.timestamp = stats.timestamp;
687
+ this.transportId = stats.transportId;
688
+ this.localCandidateId = stats.localCandidateId;
689
+ this.remoteCandidateId = stats.remoteCandidateId;
690
+ this.state = this._convertState(stats.state);
691
+ this.nominated = stats.nominated;
692
+ this.packetsSent = stats.packetsSent;
693
+ this.packetsReceived = stats.packetsReceived;
694
+ this.bytesSent = stats.bytesSent;
695
+ this.bytesReceived = stats.bytesReceived;
696
+ this.lastPacketSentTimestamp = stats.lastPacketSentTimestamp;
697
+ this.lastPacketReceivedTimestamp = stats.lastPacketReceivedTimestamp;
698
+ this.totalRoundTripTime = stats.totalRoundTripTime;
699
+ this.currentRoundTripTime = stats.currentRoundTripTime;
700
+ this.availableOutgoingBitrate = stats.availableOutgoingBitrate;
701
+ this.availableIncomingBitrate = stats.availableIncomingBitrate;
702
+ this.requestsReceived = stats.requestsReceived;
703
+ this.requestsSent = stats.requestsSent;
704
+ this.responsesReceived = stats.responsesReceived;
705
+ this.responsesSent = stats.responsesSent;
706
+ this.consentRequestsSent = stats.consentRequestsSent;
707
+ this.packetsDiscardedOnSend = stats.packetsDiscardedOnSend;
708
+ this.bytesDiscardedOnSend = stats.bytesDiscardedOnSend;
709
+ this.attachments = stats.attachments;
710
+ }
711
+ _convertState(state) {
712
+ switch (state) {
713
+ case "new":
714
+ case "in-progress":
715
+ case "waiting":
716
+ case "failed":
717
+ case "succeeded":
718
+ return state;
719
+ case "cancelled":
720
+ return "failed";
721
+ case "inprogress":
722
+ return "in-progress";
723
+ default:
724
+ return void 0;
725
+ }
726
+ }
727
+ };
728
+
729
+ // src/ObservedIceTransport.ts
730
+ var ObservedIceTransport = class {
731
+ constructor(timestamp, id, _peerConnection) {
732
+ this.timestamp = timestamp;
733
+ this.id = id;
734
+ this._peerConnection = _peerConnection;
735
+ }
736
+ timestamp;
737
+ id;
738
+ _peerConnection;
739
+ _visited = false;
740
+ appData;
741
+ packetsSent;
742
+ packetsReceived;
743
+ bytesSent;
744
+ bytesReceived;
745
+ iceRole;
746
+ iceLocalUsernameFragment;
747
+ dtlsState;
748
+ iceState;
749
+ selectedCandidatePairId;
750
+ localCertificateId;
751
+ remoteCertificateId;
752
+ tlsVersion;
753
+ dtlsCipher;
754
+ dtlsRole;
755
+ srtpCipher;
756
+ selectedCandidatePairChanges;
757
+ attachments;
758
+ deltaPacketsReceived = 0;
759
+ deltaPacketsSent = 0;
760
+ deltaBytesReceived = 0;
761
+ deltaBytesSent = 0;
762
+ get visited() {
763
+ const visited = this._visited;
764
+ this._visited = false;
765
+ return visited;
766
+ }
767
+ getPeerConnection() {
768
+ return this._peerConnection;
769
+ }
770
+ getSelectedCandidatePair() {
771
+ return this._peerConnection.observedIceCandidatesPair.get(this.selectedCandidatePairId ?? "");
772
+ }
773
+ update(stats) {
774
+ this._visited = true;
775
+ if (this.bytesReceived !== void 0 && stats.bytesReceived !== void 0 && this.bytesReceived <= stats.bytesReceived) {
776
+ this.deltaBytesReceived = stats.bytesReceived - this.bytesReceived;
777
+ } else {
778
+ this.deltaBytesReceived = 0;
779
+ }
780
+ if (this.bytesSent !== void 0 && stats.bytesSent !== void 0 && this.bytesSent <= stats.bytesSent) {
781
+ this.deltaBytesSent = stats.bytesSent - this.bytesSent;
782
+ } else {
783
+ this.deltaBytesSent = 0;
784
+ }
785
+ if (this.packetsReceived !== void 0 && stats.packetsReceived !== void 0 && this.packetsReceived <= stats.packetsReceived) {
786
+ this.deltaPacketsReceived = stats.packetsReceived - this.packetsReceived;
787
+ } else {
788
+ this.deltaPacketsReceived = 0;
789
+ }
790
+ if (this.packetsSent !== void 0 && stats.packetsSent !== void 0 && this.packetsSent <= stats.packetsSent) {
791
+ this.deltaPacketsSent = stats.packetsSent - this.packetsSent;
792
+ } else {
793
+ this.deltaPacketsSent = 0;
794
+ }
795
+ this.timestamp = stats.timestamp;
796
+ this.packetsSent = stats.packetsSent;
797
+ this.packetsReceived = stats.packetsReceived;
798
+ this.bytesSent = stats.bytesSent;
799
+ this.bytesReceived = stats.bytesReceived;
800
+ this.iceRole = stats.iceRole;
801
+ this.iceLocalUsernameFragment = stats.iceLocalUsernameFragment;
802
+ this.dtlsState = stats.dtlsState;
803
+ this.iceState = stats.iceState;
804
+ this.selectedCandidatePairId = stats.selectedCandidatePairId;
805
+ this.localCertificateId = stats.localCertificateId;
806
+ this.remoteCertificateId = stats.remoteCertificateId;
807
+ this.tlsVersion = stats.tlsVersion;
808
+ this.dtlsCipher = stats.dtlsCipher;
809
+ this.dtlsRole = stats.dtlsRole;
810
+ this.srtpCipher = stats.srtpCipher;
811
+ this.selectedCandidatePairChanges = stats.selectedCandidatePairChanges;
812
+ this.attachments = stats.attachments;
813
+ }
814
+ };
815
+
816
+ // src/ObservedMediaSource.ts
817
+ var ObservedMediaSource = class {
818
+ constructor(timestamp, id, kind, _peerConnection) {
819
+ this.timestamp = timestamp;
820
+ this.id = id;
821
+ this.kind = kind;
822
+ this._peerConnection = _peerConnection;
823
+ }
824
+ timestamp;
825
+ id;
826
+ kind;
827
+ _peerConnection;
828
+ _visited = false;
829
+ appData;
830
+ trackIdentifier;
831
+ audioLevel;
832
+ totalAudioEnergy;
833
+ totalSamplesDuration;
834
+ echoReturnLoss;
835
+ echoReturnLossEnhancement;
836
+ width;
837
+ height;
838
+ frames;
839
+ framesPerSecond;
840
+ attachments;
841
+ get visited() {
842
+ const visited = this._visited;
843
+ this._visited = false;
844
+ return visited;
845
+ }
846
+ getPeerConnection() {
847
+ return this._peerConnection;
848
+ }
849
+ getTrack() {
850
+ return this._peerConnection.observedOutboundTracks.get(this.trackIdentifier ?? "");
851
+ }
852
+ update(stats) {
853
+ this._visited = true;
854
+ this.timestamp = stats.timestamp;
855
+ this.trackIdentifier = stats.trackIdentifier;
856
+ this.audioLevel = stats.audioLevel;
857
+ this.totalAudioEnergy = stats.totalAudioEnergy;
858
+ this.totalSamplesDuration = stats.totalSamplesDuration;
859
+ this.echoReturnLoss = stats.echoReturnLoss;
860
+ this.echoReturnLossEnhancement = stats.echoReturnLossEnhancement;
861
+ this.width = stats.width;
862
+ this.height = stats.height;
863
+ this.frames = stats.frames;
864
+ this.framesPerSecond = stats.framesPerSecond;
865
+ this.attachments = stats.attachments;
866
+ }
867
+ };
868
+
869
+ // src/ObservedPeerConnectionTransport.ts
870
+ var ObservedPeerConnectionTransport = class {
871
+ constructor(timestamp, id, _peerConnection) {
872
+ this.timestamp = timestamp;
873
+ this.id = id;
874
+ this._peerConnection = _peerConnection;
875
+ }
876
+ timestamp;
877
+ id;
878
+ _peerConnection;
879
+ _visited = false;
880
+ appData;
881
+ dataChannelsOpened;
882
+ dataChannelsClosed;
883
+ attachments;
884
+ get visited() {
885
+ const visited = this._visited;
886
+ this._visited = false;
887
+ return visited;
888
+ }
889
+ getPeerConnection() {
890
+ return this._peerConnection;
891
+ }
892
+ update(stats) {
893
+ this._visited = true;
894
+ this.timestamp = stats.timestamp;
895
+ this.dataChannelsOpened = stats.dataChannelsOpened;
896
+ this.dataChannelsClosed = stats.dataChannelsClosed;
897
+ this.attachments = stats.attachments;
898
+ }
899
+ };
900
+
901
+ // src/ObservedMediaPlayout.ts
902
+ var ObservedMediaPlayout = class {
903
+ constructor(timestamp, id, kind, _peerConnection) {
904
+ this.timestamp = timestamp;
905
+ this.id = id;
906
+ this.kind = kind;
907
+ this._peerConnection = _peerConnection;
908
+ }
909
+ timestamp;
910
+ id;
911
+ kind;
912
+ _peerConnection;
913
+ _visited = false;
914
+ appData;
915
+ synthesizedSamplesDuration;
916
+ synthesizedSamplesEvents;
917
+ totalSamplesDuration;
918
+ totalPlayoutDelay;
919
+ totalSamplesCount;
920
+ attachments;
921
+ get visited() {
922
+ const visited = this._visited;
923
+ this._visited = false;
924
+ return visited;
925
+ }
926
+ getPeerConnection() {
927
+ return this._peerConnection;
928
+ }
929
+ update(stats) {
930
+ this._visited = true;
931
+ this.timestamp = stats.timestamp;
932
+ this.synthesizedSamplesDuration = stats.synthesizedSamplesDuration;
933
+ this.synthesizedSamplesEvents = stats.synthesizedSamplesEvents;
934
+ this.totalSamplesDuration = stats.totalSamplesDuration;
935
+ this.totalPlayoutDelay = stats.totalPlayoutDelay;
936
+ this.totalSamplesCount = stats.totalSamplesCount;
937
+ this.attachments = stats.attachments;
938
+ }
939
+ };
940
+
941
+ // src/ObservedRemoteInboundRtp.ts
942
+ var ObservedRemoteInboundRtp = class {
943
+ constructor(timestamp, id, ssrc, kind, _peerConnection) {
944
+ this.timestamp = timestamp;
945
+ this.id = id;
946
+ this.ssrc = ssrc;
947
+ this.kind = kind;
948
+ this._peerConnection = _peerConnection;
949
+ }
950
+ timestamp;
951
+ id;
952
+ ssrc;
953
+ kind;
954
+ _peerConnection;
955
+ _visited = false;
956
+ appData;
957
+ transportId;
958
+ codecId;
959
+ packetsReceived;
960
+ packetsLost;
961
+ jitter;
962
+ localId;
963
+ roundTripTime;
964
+ totalRoundTripTime;
965
+ fractionLost;
966
+ roundTripTimeMeasurements;
967
+ attachments;
968
+ get visited() {
969
+ const visited = this._visited;
970
+ this._visited = false;
971
+ return visited;
972
+ }
973
+ getPeerConnection() {
974
+ return this._peerConnection;
975
+ }
976
+ getOutboundRtp() {
977
+ return this._peerConnection.observedOutboundRtps.get(this.ssrc);
978
+ }
979
+ getCodec() {
980
+ return this._peerConnection.observedCodecs.get(this.codecId ?? "");
981
+ }
982
+ update(stats) {
983
+ this._visited = true;
984
+ this.timestamp = stats.timestamp;
985
+ this.transportId = stats.transportId;
986
+ this.codecId = stats.codecId;
987
+ this.packetsReceived = stats.packetsReceived;
988
+ this.packetsLost = stats.packetsLost;
989
+ this.jitter = stats.jitter;
990
+ this.localId = stats.localId;
991
+ this.roundTripTime = stats.roundTripTime;
992
+ this.totalRoundTripTime = stats.totalRoundTripTime;
993
+ this.fractionLost = stats.fractionLost;
994
+ this.roundTripTimeMeasurements = stats.roundTripTimeMeasurements;
995
+ this.attachments = stats.attachments;
996
+ }
997
+ };
998
+
999
+ // src/ObservedRemoteOutboundRtp.ts
1000
+ var ObservedRemoteOutboundRtp = class {
1001
+ constructor(timestamp, id, ssrc, kind, _peerConnection) {
1002
+ this.timestamp = timestamp;
1003
+ this.id = id;
1004
+ this.ssrc = ssrc;
1005
+ this.kind = kind;
1006
+ this._peerConnection = _peerConnection;
1007
+ }
1008
+ timestamp;
1009
+ id;
1010
+ ssrc;
1011
+ kind;
1012
+ _peerConnection;
1013
+ _visited = false;
1014
+ appData;
1015
+ transportId;
1016
+ codecId;
1017
+ packetsSent;
1018
+ bytesSent;
1019
+ localId;
1020
+ remoteTimestamp;
1021
+ reportsSent;
1022
+ roundTripTime;
1023
+ totalRoundTripTime;
1024
+ roundTripTimeMeasurements;
1025
+ attachments;
1026
+ get visited() {
1027
+ const visited = this._visited;
1028
+ this._visited = false;
1029
+ return visited;
1030
+ }
1031
+ getPeerConnection() {
1032
+ return this._peerConnection;
1033
+ }
1034
+ getInboundRtp() {
1035
+ return this._peerConnection.observedInboundRtps.get(this.ssrc);
1036
+ }
1037
+ getCodec() {
1038
+ return this._peerConnection.observedCodecs.get(this.codecId ?? "");
1039
+ }
1040
+ update(stats) {
1041
+ this._visited = true;
1042
+ this.timestamp = stats.timestamp;
1043
+ this.transportId = stats.transportId;
1044
+ this.codecId = stats.codecId;
1045
+ this.packetsSent = stats.packetsSent;
1046
+ this.bytesSent = stats.bytesSent;
1047
+ this.localId = stats.localId;
1048
+ this.remoteTimestamp = stats.remoteTimestamp;
1049
+ this.reportsSent = stats.reportsSent;
1050
+ this.roundTripTime = stats.roundTripTime;
1051
+ this.totalRoundTripTime = stats.totalRoundTripTime;
1052
+ this.roundTripTimeMeasurements = stats.roundTripTimeMeasurements;
1053
+ this.attachments = stats.attachments;
1054
+ }
1055
+ };
1056
+
1057
+ // src/ObservedInboundTrack.ts
1058
+ var ObservedInboundTrack = class {
1059
+ constructor(timestamp, id, kind, _peerConnection, _inboundRtp, _mediaPlayout) {
1060
+ this.timestamp = timestamp;
1061
+ this.id = id;
1062
+ this.kind = kind;
1063
+ this._peerConnection = _peerConnection;
1064
+ this._inboundRtp = _inboundRtp;
1065
+ this._mediaPlayout = _mediaPlayout;
1066
+ this.report = {
1067
+ trackId: this.id,
1068
+ kind: this.kind,
1069
+ fractionLostDistribution: {
1070
+ gtOrEq050: 0,
1071
+ lt001: 0,
1072
+ lt005: 0,
1073
+ lt010: 0,
1074
+ lt020: 0,
1075
+ lt050: 0,
1076
+ count: 0,
1077
+ sum: 0
1078
+ }
1079
+ };
1080
+ }
1081
+ timestamp;
1082
+ id;
1083
+ kind;
1084
+ _peerConnection;
1085
+ _inboundRtp;
1086
+ _mediaPlayout;
1087
+ calculatedScore = {
1088
+ weight: 1,
1089
+ value: void 0
1090
+ };
1091
+ appData;
1092
+ report;
1093
+ _visited = false;
1094
+ addedAt;
1095
+ removedAt;
1096
+ muted;
1097
+ attachments;
1098
+ get score() {
1099
+ return this.calculatedScore.value;
1100
+ }
1101
+ get visited() {
1102
+ const visited = this._visited;
1103
+ this._visited = false;
1104
+ return visited;
1105
+ }
1106
+ getPeerConnection() {
1107
+ return this._peerConnection;
1108
+ }
1109
+ getInboundRtp() {
1110
+ return this._inboundRtp;
1111
+ }
1112
+ getMediaPlayout() {
1113
+ return this._mediaPlayout;
1114
+ }
1115
+ getRemoteOutboundTrack() {
1116
+ return this._peerConnection.client.call.remoteTrackResolver?.resolveRemoteOutboundTrack(this);
1117
+ }
1118
+ update(stats) {
1119
+ this._visited = true;
1120
+ this.timestamp = stats.timestamp;
1121
+ this.calculatedScore.value = stats.score;
1122
+ this.attachments = stats.attachments;
1123
+ const fl = this._inboundRtp?.fractionLost;
1124
+ if (fl !== void 0) {
1125
+ if (fl < 0.01) this.report.fractionLostDistribution.lt001 += 1;
1126
+ else if (fl < 0.05) this.report.fractionLostDistribution.lt005 += 1;
1127
+ else if (fl < 0.1) this.report.fractionLostDistribution.lt010 += 1;
1128
+ else if (fl < 0.2) this.report.fractionLostDistribution.lt020 += 1;
1129
+ else if (fl < 0.5) this.report.fractionLostDistribution.lt050 += 1;
1130
+ else this.report.fractionLostDistribution.gtOrEq050 += 1;
1131
+ this.report.fractionLostDistribution.count += 1;
1132
+ this.report.fractionLostDistribution.sum += fl;
1133
+ }
1134
+ }
1135
+ };
1136
+
1137
+ // src/ObservedOutboundTrack.ts
1138
+ var ObservedOutboundTrack = class {
1139
+ constructor(timestamp, id, kind, _peerConnection, _outboundRtps, _mediaSource) {
1140
+ this.timestamp = timestamp;
1141
+ this.id = id;
1142
+ this.kind = kind;
1143
+ this._peerConnection = _peerConnection;
1144
+ this._outboundRtps = _outboundRtps;
1145
+ this._mediaSource = _mediaSource;
1146
+ this.report = {
1147
+ trackId: this.id,
1148
+ kind: this.kind
1149
+ };
1150
+ }
1151
+ timestamp;
1152
+ id;
1153
+ kind;
1154
+ _peerConnection;
1155
+ _outboundRtps;
1156
+ _mediaSource;
1157
+ _visited = false;
1158
+ appData;
1159
+ report;
1160
+ calculatedScore = {
1161
+ weight: 1,
1162
+ value: void 0
1163
+ };
1164
+ addedAt;
1165
+ removedAt;
1166
+ muted;
1167
+ attachments;
1168
+ get score() {
1169
+ return this.calculatedScore.value;
1170
+ }
1171
+ get visited() {
1172
+ const visited = this._visited;
1173
+ this._visited = false;
1174
+ return visited;
1175
+ }
1176
+ getPeerConnection() {
1177
+ return this._peerConnection;
1178
+ }
1179
+ getOutboundRtps() {
1180
+ return this._outboundRtps;
1181
+ }
1182
+ getMediaSource() {
1183
+ return this._mediaSource;
1184
+ }
1185
+ getRemoteInboundTracks() {
1186
+ return this._peerConnection.client.call.remoteTrackResolver?.resolveRemoteInboundTracks(this);
1187
+ }
1188
+ update(stats) {
1189
+ this._visited = true;
1190
+ this.timestamp = stats.timestamp;
1191
+ this.calculatedScore.value = stats.score;
1192
+ this.attachments = stats.attachments;
1193
+ }
1194
+ };
1195
+
1196
+ // src/common/utils.ts
1197
+ function getMedian(arr, copyArrayForSorting = true) {
1198
+ const sortedArr = copyArrayForSorting ? arr.slice().sort((a, b) => a - b) : arr.sort((a, b) => a - b);
1199
+ const mid = Math.floor(sortedArr.length / 2);
1200
+ if (sortedArr.length % 2 !== 0) {
1201
+ return sortedArr[mid];
1202
+ }
1203
+ return (sortedArr[mid - 1] + sortedArr[mid]) / 2;
1204
+ }
1205
+ function parseJsonAs(json) {
1206
+ if (!json) {
1207
+ return void 0;
1208
+ }
1209
+ try {
1210
+ return JSON.parse(json);
1211
+ } catch (error) {
1212
+ return void 0;
1213
+ }
1214
+ }
1215
+
1216
+ // src/ObservedPeerConnection.ts
1217
+ var logger = createLogger("ObservedPeerConnection");
1218
+ var ObservedPeerConnection = class extends EventEmitter {
1219
+ constructor(peerConnectionId, client) {
1220
+ super();
1221
+ this.peerConnectionId = peerConnectionId;
1222
+ this.client = client;
1223
+ this.setMaxListeners(Infinity);
1224
+ this.eventScope = {
1225
+ observer: client.call.observer,
1226
+ observedCall: client.call,
1227
+ observedClient: client,
1228
+ observedPeerConnection: this
1229
+ };
1230
+ }
1231
+ peerConnectionId;
1232
+ client;
1233
+ _visited = true;
1234
+ appData;
1235
+ calculatedScore = {
1236
+ weight: 1,
1237
+ value: void 0
1238
+ };
1239
+ closed = false;
1240
+ // timestamp of the PEER_CONNECTION_OPENED event
1241
+ openedAt;
1242
+ // timestamp of the PEER_CONNECTION_CLOSED event
1243
+ closedAt;
1244
+ updated = Date.now();
1245
+ connectionState;
1246
+ iceConnectionState;
1247
+ iceGatheringState;
1248
+ availableIncomingBitrate = 0;
1249
+ availableOutgoingBitrate = 0;
1250
+ totalInboundPacketsLost = 0;
1251
+ totalInboundPacketsReceived = 0;
1252
+ totalOutboundPacketsSent = 0;
1253
+ totalDataChannelBytesSent = 0;
1254
+ totalDataChannelBytesReceived = 0;
1255
+ totalDataChannelMessagesSent = 0;
1256
+ totalDataChannelMessagesReceived = 0;
1257
+ totalSentAudioBytes = 0;
1258
+ totalSentVideoBytes = 0;
1259
+ totalSentAudioPackets = 0;
1260
+ totalSentVideoPackets = 0;
1261
+ totalReceivedAudioPacktes = 0;
1262
+ totalReceivedVideoPackets = 0;
1263
+ totalReceivedAudioBytes = 0;
1264
+ totalReceivedVideoBytes = 0;
1265
+ deltaInboundPacketsLost = 0;
1266
+ deltaInboundPacketsReceived = 0;
1267
+ deltaOutboundPacketsSent = 0;
1268
+ deltaDataChannelBytesSent = 0;
1269
+ deltaDataChannelBytesReceived = 0;
1270
+ deltaDataChannelMessagesSent = 0;
1271
+ deltaDataChannelMessagesReceived = 0;
1272
+ deltaInboundReceivedBytes = 0;
1273
+ deltaOutboundSentBytes = 0;
1274
+ deltaReceivedAudioBytes = 0;
1275
+ deltaReceivedVideoBytes = 0;
1276
+ deltaReceivedAudioPackets = 0;
1277
+ deltaReceivedVideoPackets = 0;
1278
+ deltaSentAudioBytes = 0;
1279
+ deltaSentVideoBytes = 0;
1280
+ deltaSentAudioPackets = 0;
1281
+ deltaSentVideoPackets = 0;
1282
+ deltaTransportSentBytes = 0;
1283
+ deltaTransportReceivedBytes = 0;
1284
+ receivingPacketsPerSecond = 0;
1285
+ sendingPacketsPerSecond = 0;
1286
+ sendingAudioBitrate = 0;
1287
+ sendingVideoBitrate = 0;
1288
+ receivingAudioBitrate = 0;
1289
+ receivingVideoBitrate = 0;
1290
+ currentRttInMs;
1291
+ currentJitter;
1292
+ usingTCP = false;
1293
+ usingTURN = false;
1294
+ observedTurnServer;
1295
+ observedCertificates = /* @__PURE__ */ new Map();
1296
+ observedCodecs = /* @__PURE__ */ new Map();
1297
+ observedDataChannels = /* @__PURE__ */ new Map();
1298
+ observedIceCandidates = /* @__PURE__ */ new Map();
1299
+ observedIceCandidatesPair = /* @__PURE__ */ new Map();
1300
+ observedIceTransports = /* @__PURE__ */ new Map();
1301
+ observedInboundRtps = /* @__PURE__ */ new Map();
1302
+ observedInboundTracks = /* @__PURE__ */ new Map();
1303
+ observedMediaPlayouts = /* @__PURE__ */ new Map();
1304
+ observedMediaSources = /* @__PURE__ */ new Map();
1305
+ observedOutboundRtps = /* @__PURE__ */ new Map();
1306
+ observedOutboundTracks = /* @__PURE__ */ new Map();
1307
+ observedPeerConnectionTransports = /* @__PURE__ */ new Map();
1308
+ observedRemoteInboundRtps = /* @__PURE__ */ new Map();
1309
+ observedRemoteOutboundRtps = /* @__PURE__ */ new Map();
1310
+ eventScope;
1311
+ get score() {
1312
+ return this.calculatedScore.value;
1313
+ }
1314
+ get visited() {
1315
+ const visited = this._visited;
1316
+ this._visited = false;
1317
+ return visited;
1318
+ }
1319
+ get codecs() {
1320
+ return [...this.observedCodecs.values()];
1321
+ }
1322
+ get inboundRtps() {
1323
+ return [...this.observedInboundRtps.values()];
1324
+ }
1325
+ get remoteOutboundRtps() {
1326
+ return [...this.observedRemoteOutboundRtps.values()];
1327
+ }
1328
+ get outboundRtps() {
1329
+ return [...this.observedOutboundRtps.values()];
1330
+ }
1331
+ get remoteInboundRtps() {
1332
+ return [...this.observedRemoteInboundRtps.values()];
1333
+ }
1334
+ get mediaSources() {
1335
+ return [...this.observedMediaSources.values()];
1336
+ }
1337
+ get mediaPlayouts() {
1338
+ return [...this.observedMediaPlayouts.values()];
1339
+ }
1340
+ get dataChannels() {
1341
+ return [...this.observedDataChannels.values()];
1342
+ }
1343
+ get peerConnectionTransports() {
1344
+ return [...this.observedPeerConnectionTransports.values()];
1345
+ }
1346
+ get iceTransports() {
1347
+ return [...this.observedIceTransports.values()];
1348
+ }
1349
+ get iceCandidates() {
1350
+ return [...this.observedIceCandidates.values()];
1351
+ }
1352
+ get iceCandidatePairs() {
1353
+ return [...this.observedIceCandidatesPair.values()];
1354
+ }
1355
+ get certificates() {
1356
+ return [...this.observedCertificates.values()];
1357
+ }
1358
+ get selectedIceCandidatePairs() {
1359
+ return this.iceTransports.map((iceTransport) => iceTransport.getSelectedCandidatePair()).filter((pair) => pair !== void 0);
1360
+ }
1361
+ get selectedIceCandiadtePairForTurn() {
1362
+ return this.selectedIceCandidatePairs.filter(
1363
+ (pair) => pair.getLocalCandidate()?.candidateType === "relay" && pair.getRemoteCandidate()?.url?.startsWith("turn:")
1364
+ );
1365
+ }
1366
+ close() {
1367
+ if (this.closed) return;
1368
+ this.closed = true;
1369
+ this.observedCertificates.forEach((observedCertificate) => this._notify("certificate-removed", { ...this.eventScope, observedCertificate }));
1370
+ this.observedCodecs.forEach((observedCodec) => this._notify("codec-removed", { ...this.eventScope, observedCodec }));
1371
+ this.observedDataChannels.forEach((observedDataChannel) => this._notify("data-channel-removed", { ...this.eventScope, observedDataChannel }));
1372
+ this.observedIceCandidates.forEach((observedIceCandidate) => this._notify("ice-candidate-removed", { ...this.eventScope, observedIceCandidate }));
1373
+ this.observedIceCandidatesPair.forEach((pair) => this._notify("ice-candidate-pair-removed", { ...this.eventScope, observedIceCandidatePair: pair }));
1374
+ this.observedIceTransports.forEach((transport) => this._notify("ice-transport-removed", { ...this.eventScope, observedIceTransport: transport }));
1375
+ this.observedInboundRtps.forEach((rtp) => this._notify("inbound-rtp-removed", { ...this.eventScope, observedInboundRtp: rtp }));
1376
+ this.observedInboundTracks.forEach((track) => {
1377
+ this.emit("removed-inbound-track", track);
1378
+ this._notify("inbound-track-removed", { ...this.eventScope, observedInboundTrack: track });
1379
+ });
1380
+ this.observedMediaPlayouts.forEach((playout) => this._notify("media-playout-removed", { ...this.eventScope, observedMediaPlayout: playout }));
1381
+ this.observedMediaSources.forEach((source) => this._notify("media-source-removed", { ...this.eventScope, observedMediaSource: source }));
1382
+ this.observedOutboundRtps.forEach((rtp) => this._notify("outbound-rtp-removed", { ...this.eventScope, observedOutboundRtp: rtp }));
1383
+ this.observedOutboundTracks.forEach((track) => {
1384
+ this.emit("removed-outbound-track", track);
1385
+ this._notify("outbound-track-removed", { ...this.eventScope, observedOutboundTrack: track });
1386
+ });
1387
+ this.observedPeerConnectionTransports.forEach((transport) => this._notify("peer-connection-transport-removed", { ...this.eventScope, observedPeerConnectionTransport: transport }));
1388
+ this.observedRemoteInboundRtps.forEach((rtp) => this._notify("remote-inbound-rtp-removed", { ...this.eventScope, observedRemoteInboundRtp: rtp }));
1389
+ this.observedRemoteOutboundRtps.forEach((rtp) => this._notify("remote-outbound-rtp-removed", { ...this.eventScope, observedRemoteOutboundRtp: rtp }));
1390
+ this.observedCertificates.clear();
1391
+ this.observedCodecs.clear();
1392
+ this.observedDataChannels.clear();
1393
+ this.observedIceCandidates.clear();
1394
+ this.observedIceCandidatesPair.clear();
1395
+ this.observedIceTransports.clear();
1396
+ this.observedInboundRtps.clear();
1397
+ this.observedInboundTracks.clear();
1398
+ this.observedMediaPlayouts.clear();
1399
+ this.observedMediaSources.clear();
1400
+ this.observedOutboundRtps.clear();
1401
+ this.observedOutboundTracks.clear();
1402
+ this.observedPeerConnectionTransports.clear();
1403
+ this.observedRemoteInboundRtps.clear();
1404
+ this.observedRemoteOutboundRtps.clear();
1405
+ this.client.call.observer.observedTURN.removePeerConnection(this);
1406
+ if (!this.closedAt) this.closedAt = Date.now();
1407
+ this.emit("close");
1408
+ this._notify("peer-connection-closed", { ...this.eventScope });
1409
+ }
1410
+ accept(sample, context) {
1411
+ if (this.closed) return;
1412
+ this._visited = true;
1413
+ if (context) this.appData = { ...this.appData, ...context };
1414
+ this.availableIncomingBitrate = 0;
1415
+ this.availableOutgoingBitrate = 0;
1416
+ this.deltaInboundPacketsLost = 0;
1417
+ this.deltaInboundPacketsReceived = 0;
1418
+ this.deltaOutboundPacketsSent = 0;
1419
+ this.deltaDataChannelBytesSent = 0;
1420
+ this.deltaDataChannelBytesReceived = 0;
1421
+ this.deltaInboundReceivedBytes = 0;
1422
+ this.deltaOutboundSentBytes = 0;
1423
+ this.deltaReceivedAudioBytes = 0;
1424
+ this.deltaReceivedVideoBytes = 0;
1425
+ this.deltaReceivedAudioPackets = 0;
1426
+ this.deltaReceivedVideoPackets = 0;
1427
+ this.deltaSentAudioBytes = 0;
1428
+ this.deltaSentVideoBytes = 0;
1429
+ this.deltaSentAudioPackets = 0;
1430
+ this.deltaSentVideoPackets = 0;
1431
+ this.deltaTransportReceivedBytes = 0;
1432
+ this.deltaTransportSentBytes = 0;
1433
+ this.sendingAudioBitrate = 0;
1434
+ this.sendingVideoBitrate = 0;
1435
+ this.receivingAudioBitrate = 0;
1436
+ this.receivingVideoBitrate = 0;
1437
+ const now = Date.now();
1438
+ const elapsedTimeInMs = now - this.updated;
1439
+ const elapsedTimeInSec = elapsedTimeInMs / 1e3;
1440
+ const rttMeasurementsInSec = [];
1441
+ const jitterMeasurements = [];
1442
+ if (sample.certificates) {
1443
+ for (const certificate of sample.certificates) {
1444
+ this._updateCertificateStats(certificate);
1445
+ }
1446
+ }
1447
+ if (sample.codecs) {
1448
+ for (const codec of sample.codecs) {
1449
+ this._updateCodecStats(codec);
1450
+ }
1451
+ }
1452
+ if (sample.dataChannels) {
1453
+ for (const dataChannel of sample.dataChannels) {
1454
+ const observedDataChannel = this._updateDataChannelStats(dataChannel);
1455
+ if (!observedDataChannel) continue;
1456
+ this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent;
1457
+ this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived;
1458
+ this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent;
1459
+ this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived;
1460
+ }
1461
+ }
1462
+ if (sample.iceCandidates) {
1463
+ for (const iceCandidate of sample.iceCandidates) {
1464
+ this._updateIceCandidateStats(iceCandidate);
1465
+ }
1466
+ }
1467
+ if (sample.iceCandidatePairs) {
1468
+ for (const iceCandidatePair of sample.iceCandidatePairs) {
1469
+ const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair);
1470
+ if (!observedCandidatePair) continue;
1471
+ if (observedCandidatePair.currentRoundTripTime) {
1472
+ rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime);
1473
+ }
1474
+ if (observedCandidatePair.availableIncomingBitrate) {
1475
+ this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate;
1476
+ }
1477
+ if (observedCandidatePair.availableOutgoingBitrate) {
1478
+ this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate;
1479
+ }
1480
+ }
1481
+ }
1482
+ if (sample.iceTransports) {
1483
+ for (const iceTransport of sample.iceTransports) {
1484
+ const observedIceTransport = this._updateIceTransportStats(iceTransport);
1485
+ if (!observedIceTransport) continue;
1486
+ }
1487
+ }
1488
+ if (sample.inboundRtps) {
1489
+ for (const inboundRtp of sample.inboundRtps) {
1490
+ const observedInboundRtp = this._updateInboundRtpStats(inboundRtp);
1491
+ if (!observedInboundRtp) continue;
1492
+ this.deltaInboundPacketsLost += observedInboundRtp.deltaLostPackets;
1493
+ this.deltaInboundPacketsReceived += observedInboundRtp.deltaReceivedPackets;
1494
+ this.deltaInboundReceivedBytes += observedInboundRtp.deltaBytesReceived;
1495
+ switch (inboundRtp.kind) {
1496
+ case "audio":
1497
+ this.deltaReceivedAudioBytes += observedInboundRtp.deltaBytesReceived;
1498
+ this.deltaReceivedAudioPackets += observedInboundRtp.deltaReceivedPackets;
1499
+ break;
1500
+ case "video":
1501
+ this.deltaReceivedVideoBytes += observedInboundRtp.deltaBytesReceived;
1502
+ this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
1503
+ break;
1504
+ }
1505
+ if (observedInboundRtp.jitter) {
1506
+ jitterMeasurements.push(observedInboundRtp.jitter);
1507
+ }
1508
+ }
1509
+ }
1510
+ if (sample.mediaPlayouts) {
1511
+ for (const mediaPlayout of sample.mediaPlayouts) {
1512
+ this._updateMediaPlayoutStats(mediaPlayout);
1513
+ }
1514
+ }
1515
+ if (sample.mediaSources) {
1516
+ for (const mediaSource of sample.mediaSources) {
1517
+ this._updateMediaSourceStats(mediaSource);
1518
+ }
1519
+ }
1520
+ if (sample.outboundRtps) {
1521
+ for (const outboundRtp of sample.outboundRtps) {
1522
+ const observedOutboundRtp = this._updateOutboundRtpStats(outboundRtp);
1523
+ if (!observedOutboundRtp) continue;
1524
+ this.deltaOutboundPacketsSent += observedOutboundRtp.deltaPacketsSent ?? 0;
1525
+ this.deltaOutboundSentBytes += observedOutboundRtp.deltaBytesSent ?? 0;
1526
+ switch (outboundRtp.kind) {
1527
+ case "audio":
1528
+ this.deltaSentAudioBytes += observedOutboundRtp.deltaBytesSent;
1529
+ this.deltaSentAudioPackets += observedOutboundRtp.deltaPacketsSent;
1530
+ break;
1531
+ case "video":
1532
+ this.deltaSentVideoBytes += observedOutboundRtp.deltaBytesSent;
1533
+ this.deltaSentVideoPackets += observedOutboundRtp.deltaPacketsSent;
1534
+ break;
1535
+ }
1536
+ }
1537
+ }
1538
+ if (sample.peerConnectionTransports) {
1539
+ for (const peerConnectionTransport of sample.peerConnectionTransports) {
1540
+ const observedTransport = this._updatePeerConnectionTransportStats(peerConnectionTransport);
1541
+ if (!observedTransport) continue;
1542
+ }
1543
+ }
1544
+ if (sample.remoteInboundRtps) {
1545
+ for (const remoteInboundRtp of sample.remoteInboundRtps) {
1546
+ const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp);
1547
+ if (!observedRemoteInboundRtp) continue;
1548
+ if (observedRemoteInboundRtp.roundTripTime) {
1549
+ rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime);
1550
+ }
1551
+ const observedOutboundRtp = observedRemoteInboundRtp.getOutboundRtp();
1552
+ if (observedOutboundRtp) {
1553
+ if (observedRemoteInboundRtp.roundTripTime !== void 0) {
1554
+ observedOutboundRtp.remoteRttInMs = observedRemoteInboundRtp.roundTripTime * 1e3;
1555
+ }
1556
+ if (observedRemoteInboundRtp.fractionLost !== void 0) {
1557
+ observedOutboundRtp.remoteFractionLost = observedRemoteInboundRtp.fractionLost;
1558
+ }
1559
+ if (observedRemoteInboundRtp.jitter !== void 0) {
1560
+ observedOutboundRtp.remoteJitter = observedRemoteInboundRtp.jitter;
1561
+ }
1562
+ if (observedRemoteInboundRtp.packetsLost !== void 0) {
1563
+ observedOutboundRtp.remotePacketsLost = observedRemoteInboundRtp.packetsLost;
1564
+ }
1565
+ }
1566
+ }
1567
+ }
1568
+ if (sample.remoteOutboundRtps) {
1569
+ for (const remoteOutboundRtp of sample.remoteOutboundRtps) {
1570
+ const observedRemoteOutboundRtp = this._updateRemoteOutboundRtpStats(remoteOutboundRtp);
1571
+ if (!observedRemoteOutboundRtp) continue;
1572
+ const observedInboundRtp = observedRemoteOutboundRtp.getInboundRtp();
1573
+ if (observedInboundRtp) {
1574
+ if (observedRemoteOutboundRtp.roundTripTime !== void 0) {
1575
+ observedInboundRtp.remoteRttInMs = observedRemoteOutboundRtp.roundTripTime * 1e3;
1576
+ }
1577
+ if (observedRemoteOutboundRtp.bytesSent !== void 0) {
1578
+ observedInboundRtp.remoteBytesSent = observedRemoteOutboundRtp.bytesSent;
1579
+ }
1580
+ if (observedRemoteOutboundRtp.packetsSent !== void 0) {
1581
+ observedInboundRtp.remotePacketsSent = observedRemoteOutboundRtp.packetsSent;
1582
+ }
1583
+ if (observedRemoteOutboundRtp.remoteTimestamp !== void 0) {
1584
+ observedInboundRtp.remoteTimestamp = observedRemoteOutboundRtp.remoteTimestamp;
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+ if (sample.inboundTracks) {
1590
+ for (const inboundTrack of sample.inboundTracks) {
1591
+ this._updateInboundTrackSample(inboundTrack);
1592
+ }
1593
+ }
1594
+ if (sample.outboundTracks) {
1595
+ for (const outboundTrack of sample.outboundTracks) {
1596
+ this._updateOutboundTrackSample(outboundTrack);
1597
+ }
1598
+ }
1599
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
1600
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
1601
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
1602
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
1603
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
1604
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
1605
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
1606
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
1607
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
1608
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
1609
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
1610
+ this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets;
1611
+ this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets;
1612
+ this.totalSentAudioPackets += this.deltaSentAudioPackets;
1613
+ this.totalSentVideoPackets += this.deltaSentVideoPackets;
1614
+ this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
1615
+ this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec;
1616
+ this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedTimeInSec;
1617
+ this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedTimeInSec;
1618
+ this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedTimeInSec;
1619
+ this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedTimeInSec;
1620
+ if (rttMeasurementsInSec.length > 0) {
1621
+ this.currentRttInMs = getMedian(rttMeasurementsInSec, false) * 1e3;
1622
+ } else {
1623
+ this.currentRttInMs = void 0;
1624
+ }
1625
+ if (jitterMeasurements.length > 0) {
1626
+ this.currentJitter = getMedian(jitterMeasurements, false);
1627
+ } else {
1628
+ this.currentJitter = void 0;
1629
+ }
1630
+ const wasUsingTURN = this.usingTURN;
1631
+ const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
1632
+ const selectedCandidatePairForTurn = [];
1633
+ this.usingTCP = false;
1634
+ this.usingTURN = false;
1635
+ for (const selectedCandidatePair of selectedIceCandidatePairs) {
1636
+ if (selectedCandidatePair.getLocalCandidate()?.protocol === "tcp") {
1637
+ this.usingTCP = true;
1638
+ }
1639
+ if (selectedCandidatePair.getLocalCandidate()?.candidateType === "relay" && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith("turn:")) {
1640
+ selectedCandidatePairForTurn.push(selectedCandidatePair);
1641
+ this.usingTURN = true;
1642
+ }
1643
+ this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived;
1644
+ this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent;
1645
+ }
1646
+ if (this.usingTURN) {
1647
+ if (!this.observedTurnServer) {
1648
+ this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this);
1649
+ }
1650
+ this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn);
1651
+ } else if (wasUsingTURN) {
1652
+ if (!this.usingTURN) {
1653
+ this.client.call.observer.observedTURN.removePeerConnection(this);
1654
+ }
1655
+ }
1656
+ this.calculatedScore.value = sample.score;
1657
+ this.updated = now;
1658
+ this._checkVisited();
1659
+ this._notify("peer-connection-updated", { ...this.eventScope, context });
1660
+ }
1661
+ _checkVisited() {
1662
+ for (const certificate of [...this.observedCertificates.values()]) {
1663
+ if (certificate.visited) continue;
1664
+ this.observedCertificates.delete(certificate.id);
1665
+ }
1666
+ for (const observedCodec of [...this.observedCodecs.values()]) {
1667
+ if (observedCodec.visited) continue;
1668
+ this.observedCodecs.delete(observedCodec.id);
1669
+ this._notify("codec-removed", { ...this.eventScope, observedCodec });
1670
+ }
1671
+ for (const observedDataChannel of [...this.observedDataChannels.values()]) {
1672
+ if (observedDataChannel.visited) continue;
1673
+ this.observedDataChannels.delete(observedDataChannel.id);
1674
+ this._notify("data-channel-removed", { ...this.eventScope, observedDataChannel });
1675
+ }
1676
+ for (const observedIceCandidate of [...this.observedIceCandidates.values()]) {
1677
+ if (observedIceCandidate.visited) continue;
1678
+ this.observedIceCandidates.delete(observedIceCandidate.id);
1679
+ this._notify("ice-candidate-removed", { ...this.eventScope, observedIceCandidate });
1680
+ }
1681
+ for (const observedIceCandidatePair of [...this.observedIceCandidatesPair.values()]) {
1682
+ if (observedIceCandidatePair.visited) continue;
1683
+ this.observedIceCandidatesPair.delete(observedIceCandidatePair.id);
1684
+ this._notify("ice-candidate-pair-removed", { ...this.eventScope, observedIceCandidatePair });
1685
+ }
1686
+ for (const observedIceTransport of [...this.observedIceTransports.values()]) {
1687
+ if (observedIceTransport.visited) continue;
1688
+ this.observedIceTransports.delete(observedIceTransport.id);
1689
+ this._notify("ice-transport-removed", { ...this.eventScope, observedIceTransport });
1690
+ }
1691
+ for (const observedInboundRtp of [...this.observedInboundRtps.values()]) {
1692
+ if (observedInboundRtp.visited) continue;
1693
+ this.observedInboundRtps.delete(observedInboundRtp.ssrc);
1694
+ this._notify("inbound-rtp-removed", { ...this.eventScope, observedInboundRtp });
1695
+ }
1696
+ for (const observedInboundTrack of [...this.observedInboundTracks.values()]) {
1697
+ if (observedInboundTrack.visited) continue;
1698
+ this.observedInboundTracks.delete(observedInboundTrack.id);
1699
+ this.emit("removed-inbound-track", observedInboundTrack);
1700
+ this._notify("inbound-track-removed", { ...this.eventScope, observedInboundTrack });
1701
+ }
1702
+ for (const observedMediaPlayout of [...this.observedMediaPlayouts.values()]) {
1703
+ if (observedMediaPlayout.visited) continue;
1704
+ this.observedMediaPlayouts.delete(observedMediaPlayout.id);
1705
+ this._notify("media-playout-removed", { ...this.eventScope, observedMediaPlayout });
1706
+ }
1707
+ for (const observedMediaSource of [...this.observedMediaSources.values()]) {
1708
+ if (observedMediaSource.visited) continue;
1709
+ this.observedMediaSources.delete(observedMediaSource.id);
1710
+ this._notify("media-source-removed", { ...this.eventScope, observedMediaSource });
1711
+ }
1712
+ for (const observedOutboundRtp of [...this.observedOutboundRtps.values()]) {
1713
+ if (observedOutboundRtp.visited) continue;
1714
+ this.observedOutboundRtps.delete(observedOutboundRtp.ssrc);
1715
+ this._notify("outbound-rtp-removed", { ...this.eventScope, observedOutboundRtp });
1716
+ }
1717
+ for (const observedOutboundTrack of [...this.observedOutboundTracks.values()]) {
1718
+ if (observedOutboundTrack.visited) continue;
1719
+ this.observedOutboundTracks.delete(observedOutboundTrack.id);
1720
+ this.emit("removed-outbound-track", observedOutboundTrack);
1721
+ this._notify("outbound-track-removed", { ...this.eventScope, observedOutboundTrack });
1722
+ }
1723
+ for (const observedPeerConnectionTransport of [...this.observedPeerConnectionTransports.values()]) {
1724
+ if (observedPeerConnectionTransport.visited) continue;
1725
+ this.observedPeerConnectionTransports.delete(observedPeerConnectionTransport.id);
1726
+ this._notify("peer-connection-transport-removed", { ...this.eventScope, observedPeerConnectionTransport });
1727
+ }
1728
+ for (const observedRemoteInboundRtp of [...this.observedRemoteInboundRtps.values()]) {
1729
+ if (observedRemoteInboundRtp.visited) continue;
1730
+ this.observedRemoteInboundRtps.delete(observedRemoteInboundRtp.ssrc);
1731
+ this._notify("remote-inbound-rtp-removed", { ...this.eventScope, observedRemoteInboundRtp });
1732
+ }
1733
+ for (const observedRemoteOutboundRtp of [...this.observedRemoteOutboundRtps.values()]) {
1734
+ if (observedRemoteOutboundRtp.visited) continue;
1735
+ this.observedRemoteOutboundRtps.delete(observedRemoteOutboundRtp.ssrc);
1736
+ this._notify("remote-outbound-rtp-removed", { ...this.eventScope, observedRemoteOutboundRtp });
1737
+ }
1738
+ }
1739
+ _updateCertificateStats(stats) {
1740
+ let observedCertificate = this.observedCertificates.get(stats.id);
1741
+ if (!observedCertificate) {
1742
+ if (!stats.timestamp || !stats.id || !stats.fingerprint) {
1743
+ return logger.warn(
1744
+ `ObservedPeerConnection received an invalid CertificateStats (missing timestamp OR id OR fingerprint field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1745
+ stats
1746
+ );
1747
+ }
1748
+ observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
1749
+ observedCertificate.update(stats);
1750
+ this.observedCertificates.set(stats.id, observedCertificate);
1751
+ this._notify("certificate-added", { ...this.eventScope, observedCertificate });
1752
+ } else {
1753
+ observedCertificate.update(stats);
1754
+ this._notify("certificate-updated", { ...this.eventScope, observedCertificate });
1755
+ }
1756
+ return observedCertificate;
1757
+ }
1758
+ _updateCodecStats(stats) {
1759
+ let observedCodec = this.observedCodecs.get(stats.id);
1760
+ if (!observedCodec) {
1761
+ if (!stats.timestamp || !stats.id || !stats.mimeType) {
1762
+ return logger.warn(
1763
+ `ObservedPeerConnection received an invalid CodecStats (missing timestamp OR id OR mimeType field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1764
+ stats
1765
+ );
1766
+ }
1767
+ observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
1768
+ observedCodec.update(stats);
1769
+ this.observedCodecs.set(stats.id, observedCodec);
1770
+ this._notify("codec-added", { ...this.eventScope, observedCodec });
1771
+ } else {
1772
+ observedCodec.update(stats);
1773
+ }
1774
+ this._notify("codec-updated", { ...this.eventScope, observedCodec });
1775
+ return observedCodec;
1776
+ }
1777
+ _updateDataChannelStats(stats) {
1778
+ let observedDataChannel = this.observedDataChannels.get(stats.id);
1779
+ if (!observedDataChannel) {
1780
+ if (!stats.timestamp || !stats.id) {
1781
+ return logger.warn(
1782
+ `ObservedPeerConnection received an invalid DataChannelStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1783
+ stats
1784
+ );
1785
+ }
1786
+ observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
1787
+ observedDataChannel.update(stats);
1788
+ this.observedDataChannels.set(stats.id, observedDataChannel);
1789
+ this._notify("data-channel-added", { ...this.eventScope, observedDataChannel });
1790
+ } else {
1791
+ observedDataChannel.update(stats);
1792
+ }
1793
+ this._notify("data-channel-updated", { ...this.eventScope, observedDataChannel });
1794
+ return observedDataChannel;
1795
+ }
1796
+ _updateIceCandidateStats(stats) {
1797
+ let observedIceCandidate = this.observedIceCandidates.get(stats.id);
1798
+ if (!observedIceCandidate) {
1799
+ if (!stats.timestamp || !stats.id) {
1800
+ return logger.warn(
1801
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1802
+ stats
1803
+ );
1804
+ }
1805
+ observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
1806
+ observedIceCandidate.update(stats);
1807
+ this.observedIceCandidates.set(stats.id, observedIceCandidate);
1808
+ this._notify("ice-candidate-added", { ...this.eventScope, observedIceCandidate });
1809
+ } else {
1810
+ observedIceCandidate.update(stats);
1811
+ }
1812
+ this._notify("ice-candidate-updated", { ...this.eventScope, observedIceCandidate });
1813
+ return observedIceCandidate;
1814
+ }
1815
+ _updateIceCandidatePairStats(stats) {
1816
+ let observedIceCandidatePair = this.observedIceCandidatesPair.get(stats.id);
1817
+ if (!observedIceCandidatePair) {
1818
+ if (!stats.timestamp || !stats.id) {
1819
+ return logger.warn(
1820
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1821
+ stats
1822
+ );
1823
+ }
1824
+ observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
1825
+ observedIceCandidatePair.update(stats);
1826
+ this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
1827
+ this._notify("ice-candidate-pair-added", { ...this.eventScope, observedIceCandidatePair });
1828
+ } else {
1829
+ observedIceCandidatePair.update(stats);
1830
+ }
1831
+ this._notify("ice-candidate-pair-updated", { ...this.eventScope, observedIceCandidatePair });
1832
+ return observedIceCandidatePair;
1833
+ }
1834
+ _updateIceTransportStats(stats) {
1835
+ let observedIceTransport = this.observedIceTransports.get(stats.id);
1836
+ if (!observedIceTransport) {
1837
+ if (!stats.timestamp || !stats.id) {
1838
+ return logger.warn(
1839
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1840
+ stats
1841
+ );
1842
+ }
1843
+ observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
1844
+ observedIceTransport.update(stats);
1845
+ this.observedIceTransports.set(stats.id, observedIceTransport);
1846
+ this._notify("ice-transport-added", { ...this.eventScope, observedIceTransport });
1847
+ } else {
1848
+ observedIceTransport.update(stats);
1849
+ }
1850
+ this._notify("ice-transport-updated", { ...this.eventScope, observedIceTransport });
1851
+ return observedIceTransport;
1852
+ }
1853
+ _updateInboundRtpStats(stats) {
1854
+ let observedInboundRtp = this.observedInboundRtps.get(stats.ssrc);
1855
+ if (!observedInboundRtp) {
1856
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind || !stats.trackIdentifier) {
1857
+ return logger.warn(
1858
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR ssrc OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1859
+ stats
1860
+ );
1861
+ }
1862
+ observedInboundRtp = new ObservedInboundRtp(
1863
+ stats.timestamp,
1864
+ stats.id,
1865
+ stats.ssrc,
1866
+ stats.kind,
1867
+ stats.trackIdentifier,
1868
+ this
1869
+ );
1870
+ observedInboundRtp.update(stats);
1871
+ this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
1872
+ this._notify("inbound-rtp-added", { ...this.eventScope, observedInboundRtp });
1873
+ } else {
1874
+ observedInboundRtp.update(stats);
1875
+ }
1876
+ this._notify("inbound-rtp-updated", { ...this.eventScope, observedInboundRtp });
1877
+ return observedInboundRtp;
1878
+ }
1879
+ _updateInboundTrackSample(stats) {
1880
+ let observedInboundTrack = this.observedInboundTracks.get(stats.id);
1881
+ if (!observedInboundTrack) {
1882
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1883
+ return logger.warn(
1884
+ `ObservedPeerConnection received an invalid InboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1885
+ stats
1886
+ );
1887
+ }
1888
+ const inboundRtp = [...this.observedInboundRtps.values()].find((inbRtp) => inbRtp.trackIdentifier === stats.id);
1889
+ const mediaPlayout = inboundRtp ? [...this.observedMediaPlayouts.values()].find((mp) => mp.id === inboundRtp.playoutId) : void 0;
1890
+ observedInboundTrack = new ObservedInboundTrack(
1891
+ stats.timestamp,
1892
+ stats.id,
1893
+ stats.kind,
1894
+ this,
1895
+ inboundRtp,
1896
+ mediaPlayout
1897
+ );
1898
+ observedInboundTrack.update(stats);
1899
+ this.observedInboundTracks.set(stats.id, observedInboundTrack);
1900
+ this._notify("inbound-track-added", { ...this.eventScope, observedInboundTrack });
1901
+ } else {
1902
+ observedInboundTrack.update(stats);
1903
+ }
1904
+ this._notify("inbound-track-updated", { ...this.eventScope, observedInboundTrack });
1905
+ return observedInboundTrack;
1906
+ }
1907
+ _updateMediaPlayoutStats(stats) {
1908
+ let observedMediaPlayout = this.observedMediaPlayouts.get(stats.id);
1909
+ if (!observedMediaPlayout) {
1910
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1911
+ return logger.warn(
1912
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1913
+ stats
1914
+ );
1915
+ }
1916
+ observedMediaPlayout = new ObservedMediaPlayout(
1917
+ stats.timestamp,
1918
+ stats.id,
1919
+ stats.kind,
1920
+ this
1921
+ );
1922
+ observedMediaPlayout.update(stats);
1923
+ this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
1924
+ this._notify("media-playout-added", { ...this.eventScope, observedMediaPlayout });
1925
+ } else {
1926
+ observedMediaPlayout.update(stats);
1927
+ }
1928
+ this._notify("media-playout-updated", { ...this.eventScope, observedMediaPlayout });
1929
+ return observedMediaPlayout;
1930
+ }
1931
+ _updateMediaSourceStats(stats) {
1932
+ let observedMediaSource = this.observedMediaSources.get(stats.id);
1933
+ if (!observedMediaSource) {
1934
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1935
+ return logger.warn(
1936
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1937
+ stats
1938
+ );
1939
+ }
1940
+ observedMediaSource = new ObservedMediaSource(
1941
+ stats.timestamp,
1942
+ stats.id,
1943
+ stats.kind,
1944
+ this
1945
+ );
1946
+ observedMediaSource.update(stats);
1947
+ this.observedMediaSources.set(stats.id, observedMediaSource);
1948
+ this._notify("media-source-added", { ...this.eventScope, observedMediaSource });
1949
+ } else {
1950
+ observedMediaSource.update(stats);
1951
+ }
1952
+ this._notify("media-source-updated", { ...this.eventScope, observedMediaSource });
1953
+ return observedMediaSource;
1954
+ }
1955
+ _updateOutboundRtpStats(stats) {
1956
+ let observedOutboundRtp = this.observedOutboundRtps.get(stats.ssrc);
1957
+ if (!observedOutboundRtp) {
1958
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
1959
+ return logger.warn(
1960
+ `ObservedPeerConnection received an invalid OutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1961
+ stats
1962
+ );
1963
+ }
1964
+ observedOutboundRtp = new ObservedOutboundRtp(
1965
+ stats.timestamp,
1966
+ stats.id,
1967
+ stats.ssrc,
1968
+ stats.kind,
1969
+ this
1970
+ );
1971
+ observedOutboundRtp.update(stats);
1972
+ this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
1973
+ this._notify("outbound-rtp-added", { ...this.eventScope, observedOutboundRtp });
1974
+ } else {
1975
+ observedOutboundRtp.update(stats);
1976
+ }
1977
+ this._notify("outbound-rtp-updated", { ...this.eventScope, observedOutboundRtp });
1978
+ return observedOutboundRtp;
1979
+ }
1980
+ _updateOutboundTrackSample(stats) {
1981
+ let observedOutboundTrack = this.observedOutboundTracks.get(stats.id);
1982
+ if (!observedOutboundTrack) {
1983
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1984
+ return logger.warn(
1985
+ `ObservedPeerConnection received an invalid OutboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1986
+ stats
1987
+ );
1988
+ }
1989
+ const observedMediaSource = [...this.observedMediaSources.values()].find((mediaSource) => mediaSource.trackIdentifier === stats.id);
1990
+ const outboundRtps = observedMediaSource ? [...this.observedOutboundRtps.values()].filter((outboundRtp) => outboundRtp.mediaSourceId === observedMediaSource?.id) : void 0;
1991
+ observedOutboundTrack = new ObservedOutboundTrack(
1992
+ stats.timestamp,
1993
+ stats.id,
1994
+ stats.kind,
1995
+ this,
1996
+ outboundRtps,
1997
+ observedMediaSource
1998
+ );
1999
+ observedOutboundTrack.update(stats);
2000
+ this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
2001
+ this._notify("outbound-track-added", { ...this.eventScope, observedOutboundTrack });
2002
+ } else {
2003
+ observedOutboundTrack.update(stats);
2004
+ }
2005
+ this._notify("outbound-track-updated", { ...this.eventScope, observedOutboundTrack });
2006
+ return observedOutboundTrack;
2007
+ }
2008
+ _updatePeerConnectionTransportStats(stats) {
2009
+ let observedPeerConnectionTransport = this.observedPeerConnectionTransports.get(stats.id);
2010
+ if (!observedPeerConnectionTransport) {
2011
+ if (!stats.timestamp || !stats.id) {
2012
+ return logger.warn(
2013
+ `ObservedPeerConnection received an invalid PeerConnectionTransportStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
2014
+ stats
2015
+ );
2016
+ }
2017
+ observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
2018
+ observedPeerConnectionTransport.update(stats);
2019
+ this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
2020
+ this._notify("peer-connection-transport-added", { ...this.eventScope, observedPeerConnectionTransport });
2021
+ } else {
2022
+ observedPeerConnectionTransport.update(stats);
2023
+ }
2024
+ this._notify("peer-connection-transport-updated", { ...this.eventScope, observedPeerConnectionTransport });
2025
+ return observedPeerConnectionTransport;
2026
+ }
2027
+ _updateRemoteInboundRtpStats(stats) {
2028
+ let observedRemoteInboundRtp = this.observedRemoteInboundRtps.get(stats.ssrc);
2029
+ if (!observedRemoteInboundRtp) {
2030
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
2031
+ return logger.warn(
2032
+ `ObservedPeerConnection received an invalid RemoteInboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
2033
+ stats
2034
+ );
2035
+ }
2036
+ observedRemoteInboundRtp = new ObservedRemoteInboundRtp(
2037
+ stats.timestamp,
2038
+ stats.id,
2039
+ stats.ssrc,
2040
+ stats.kind,
2041
+ this
2042
+ );
2043
+ observedRemoteInboundRtp.update(stats);
2044
+ this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
2045
+ this._notify("remote-inbound-rtp-added", { ...this.eventScope, observedRemoteInboundRtp });
2046
+ } else {
2047
+ observedRemoteInboundRtp.update(stats);
2048
+ }
2049
+ this._notify("remote-inbound-rtp-updated", { ...this.eventScope, observedRemoteInboundRtp });
2050
+ return observedRemoteInboundRtp;
2051
+ }
2052
+ _updateRemoteOutboundRtpStats(stats) {
2053
+ let observedRemoteOutboundRtp = this.observedRemoteOutboundRtps.get(stats.ssrc);
2054
+ if (!observedRemoteOutboundRtp) {
2055
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
2056
+ return logger.warn(
2057
+ `ObservedPeerConnection received an invalid RemoteOutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
2058
+ stats
2059
+ );
2060
+ }
2061
+ observedRemoteOutboundRtp = new ObservedRemoteOutboundRtp(
2062
+ stats.timestamp,
2063
+ stats.id,
2064
+ stats.ssrc,
2065
+ stats.kind,
2066
+ this
2067
+ );
2068
+ observedRemoteOutboundRtp.update(stats);
2069
+ this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
2070
+ this._notify("remote-outbound-rtp-added", { ...this.eventScope, observedRemoteOutboundRtp });
2071
+ } else {
2072
+ observedRemoteOutboundRtp.update(stats);
2073
+ }
2074
+ this._notify("remote-outbound-rtp-updated", { ...this.eventScope, observedRemoteOutboundRtp });
2075
+ return observedRemoteOutboundRtp;
2076
+ }
2077
+ /** Emit an Observer-bus event scoped to this peer connection. */
2078
+ _notify(type, ...args) {
2079
+ this.client.call.observer.emit(type, ...args);
2080
+ }
2081
+ };
2082
+
2083
+ // src/schema/ClientEventTypes.ts
2084
+ var ClientEventTypes = /* @__PURE__ */ ((ClientEventTypes2) => {
2085
+ ClientEventTypes2["CLIENT_JOINED"] = "CLIENT_JOINED";
2086
+ ClientEventTypes2["CLIENT_LEFT"] = "CLIENT_LEFT";
2087
+ ClientEventTypes2["PEER_CONNECTION_OPENED"] = "PEER_CONNECTION_OPENED";
2088
+ ClientEventTypes2["PEER_CONNECTION_CLOSED"] = "PEER_CONNECTION_CLOSED";
2089
+ ClientEventTypes2["MEDIA_TRACK_ADDED"] = "MEDIA_TRACK_ADDED";
2090
+ ClientEventTypes2["MEDIA_TRACK_REMOVED"] = "MEDIA_TRACK_REMOVED";
2091
+ ClientEventTypes2["MEDIA_TRACK_RESUMED"] = "MEDIA_TRACK_RESUMED";
2092
+ ClientEventTypes2["MEDIA_TRACK_MUTED"] = "MEDIA_TRACK_MUTED";
2093
+ ClientEventTypes2["MEDIA_TRACK_UNMUTED"] = "MEDIA_TRACK_UNMUTED";
2094
+ ClientEventTypes2["ICE_GATHERING_STATE_CHANGED"] = "ICE_GATHERING_STATE_CHANGED";
2095
+ ClientEventTypes2["PEER_CONNECTION_STATE_CHANGED"] = "PEER_CONNECTION_STATE_CHANGED";
2096
+ ClientEventTypes2["ICE_CONNECTION_STATE_CHANGED"] = "ICE_CONNECTION_STATE_CHANGED";
2097
+ ClientEventTypes2["DATA_CHANNEL_OPEN"] = "DATA_CHANNEL_OPEN";
2098
+ ClientEventTypes2["DATA_CHANNEL_CLOSED"] = "DATA_CHANNEL_CLOSED";
2099
+ ClientEventTypes2["DATA_CHANNEL_ERROR"] = "DATA_CHANNEL_ERROR";
2100
+ ClientEventTypes2["NEGOTIATION_NEEDED"] = "NEGOTIATION_NEEDED";
2101
+ ClientEventTypes2["SIGNALING_STATE_CHANGE"] = "SIGNALING_STATE_CHANGE";
2102
+ ClientEventTypes2["ICE_CANDIDATE"] = "ICE_CANDIDATE";
2103
+ ClientEventTypes2["ICE_CANDIDATE_ERROR"] = "ICE_CANDIDATE_ERROR";
2104
+ ClientEventTypes2["PRODUCER_ADDED"] = "PRODUCER_ADDED";
2105
+ ClientEventTypes2["PRODUCER_REMOVED"] = "PRODUCER_REMOVED";
2106
+ ClientEventTypes2["PRODUCER_PAUSED"] = "PRODUCER_PAUSED";
2107
+ ClientEventTypes2["PRODUCER_RESUMED"] = "PRODUCER_RESUMED";
2108
+ ClientEventTypes2["CONSUMER_ADDED"] = "CONSUMER_ADDED";
2109
+ ClientEventTypes2["CONSUMER_REMOVED"] = "CONSUMER_REMOVED";
2110
+ ClientEventTypes2["CONSUMER_PAUSED"] = "CONSUMER_PAUSED";
2111
+ ClientEventTypes2["CONSUMER_RESUMED"] = "CONSUMER_RESUMED";
2112
+ ClientEventTypes2["DATA_PRODUCER_CREATED"] = "DATA_PRODUCER_CREATED";
2113
+ ClientEventTypes2["DATA_PRODUCER_CLOSED"] = "DATA_PRODUCER_CLOSED";
2114
+ ClientEventTypes2["DATA_CONSUMER_CREATED"] = "DATA_CONSUMER_CREATED";
2115
+ ClientEventTypes2["DATA_CONSUMER_CLOSED"] = "DATA_CONSUMER_CLOSED";
2116
+ return ClientEventTypes2;
2117
+ })(ClientEventTypes || {});
2118
+
2119
+ // src/schema/ClientMetaTypes.ts
2120
+ var ClientMetaTypes = /* @__PURE__ */ ((ClientMetaTypes2) => {
2121
+ ClientMetaTypes2["MEDIA_CONSTRAINT"] = "MEDIA_CONSTRAINT";
2122
+ ClientMetaTypes2["MEDIA_DEVICE"] = "MEDIA_DEVICE";
2123
+ ClientMetaTypes2["MEDIA_DEVICES_SUPPORTED_CONSTRAINTS"] = "MEDIA_DEVICES_SUPPORTED_CONSTRAINTS";
2124
+ ClientMetaTypes2["USER_MEDIA_ERROR"] = "USER_MEDIA_ERROR";
2125
+ ClientMetaTypes2["LOCAL_SDP"] = "LOCAL_SDP";
2126
+ ClientMetaTypes2["OPERATION_SYSTEM"] = "OPERATION_SYSTEM";
2127
+ ClientMetaTypes2["ENGINE"] = "ENGINE";
2128
+ ClientMetaTypes2["PLATFORM"] = "PLATFORM";
2129
+ ClientMetaTypes2["BROWSER"] = "BROWSER";
2130
+ return ClientMetaTypes2;
2131
+ })(ClientMetaTypes || {});
2132
+
2133
+ // src/ObservedClient.ts
2134
+ var logger2 = createLogger("ObservedClient");
2135
+ var ObservedClient = class extends EventEmitter2 {
2136
+ constructor(settings, call) {
2137
+ super();
2138
+ this.call = call;
2139
+ this.setMaxListeners(Infinity);
2140
+ this.eventScope = { observer: call.observer, observedCall: call, observedClient: this };
2141
+ this.sink = call.observer.config.createClientSink?.({ clientId: settings.clientId, observedCall: call });
2142
+ this.clientId = settings.clientId;
2143
+ this.appData = settings.appData ?? {};
2144
+ this.settings = {
2145
+ closeClientIfIdleForMs: settings.closeClientIfIdleForMs
2146
+ };
2147
+ this.report = {
2148
+ callId: this.call.callId,
2149
+ clientId: this.clientId,
2150
+ rttDistribution: {
2151
+ gtOrEq300ms: 0,
2152
+ lt50ms: 0,
2153
+ lt150ms: 0,
2154
+ lt300ms: 0,
2155
+ count: 0,
2156
+ sum: 0
2157
+ },
2158
+ scoreDistribution: {
2159
+ "0": 0,
2160
+ "1": 0,
2161
+ "2": 0,
2162
+ "3": 0,
2163
+ "4": 0,
2164
+ "5": 0,
2165
+ count: 0,
2166
+ sum: 0
2167
+ },
2168
+ issues: {},
2169
+ totalAudioBytesReceived: 0,
2170
+ totalVideoBytesReceived: 0,
2171
+ totalAudioBytesSent: 0,
2172
+ totalVideoBytesSent: 0,
2173
+ totalDataChannelBytesReceived: 0,
2174
+ totalDataChannelBytesSent: 0,
2175
+ totalDataChannelMessagesReceived: 0,
2176
+ totalDataChannelMessagesSent: 0,
2177
+ totalInboundRtpBytesReceived: 0,
2178
+ totalInboundRtpPacketsLost: 0,
2179
+ totalInboundRtpPacketsReceived: 0,
2180
+ totalOutboundRtpBytesSent: 0,
2181
+ totalOutboundRtpPacketsSent: 0,
2182
+ totalNumberOfIssues: 0
2183
+ };
2184
+ if (this.sink) {
2185
+ const onError = (err) => logger2.warn("ClientSampleSink error for client %s: %o", this.clientId, err);
2186
+ this.sink.once("close", () => this.sink?.off("error", onError));
2187
+ this.sink.on("error", onError);
2188
+ }
2189
+ }
2190
+ call;
2191
+ clientId;
2192
+ observedPeerConnections = /* @__PURE__ */ new Map();
2193
+ /** Per-client sink (from `ObserverConfig.createClientSink`), if any. */
2194
+ sink;
2195
+ /** Ancestry base shared by all Observer-bus events originating at this client. */
2196
+ eventScope;
2197
+ calculatedScore = {
2198
+ weight: 1,
2199
+ value: void 0
2200
+ };
2201
+ settings;
2202
+ appData;
2203
+ attachments;
2204
+ updated = Date.now();
2205
+ acceptedSamples = 0;
2206
+ closed = false;
2207
+ joinedAt;
2208
+ leftAt;
2209
+ closedAt;
2210
+ lastSampleTimestamp;
2211
+ // the timestamp of the CLIENT_JOINED event
2212
+ operationSystem;
2213
+ engine;
2214
+ platform;
2215
+ browser;
2216
+ mediaConstraints = [];
2217
+ usingTURN = false;
2218
+ usingTCP = false;
2219
+ availableOutgoingBitrate = 0;
2220
+ availableIncomingBitrate = 0;
2221
+ deltaReceivedAudioBytes = 0;
2222
+ deltaReceivedVideoBytes = 0;
2223
+ deltaSentAudioBytes = 0;
2224
+ deltaSentVideoBytes = 0;
2225
+ deltaDataChannelBytesSent = 0;
2226
+ deltaDataChannelBytesReceived = 0;
2227
+ deltaDataChannelMessagesSent = 0;
2228
+ deltaDataChannelMessagesReceived = 0;
2229
+ deltaInboundPacketsLost = 0;
2230
+ deltaInboundPacketsReceived = 0;
2231
+ deltaOutboundPacketsSent = 0;
2232
+ deltaTransportSentBytes = 0;
2233
+ deltaTransportReceivedBytes = 0;
2234
+ deltaRttLt50Measurements = 0;
2235
+ deltaRttLt150Measurements = 0;
2236
+ deltaRttLt300Measurements = 0;
2237
+ deltaRttGtOrEq300Measurements = 0;
2238
+ currentMaxRttInMs;
2239
+ currentMinRttInMs;
2240
+ currentAvgRttInMs;
2241
+ sendingAudioBitrate = 0;
2242
+ sendingVideoBitrate = 0;
2243
+ receivingAudioBitrate = 0;
2244
+ receivingVideoBitrate = 0;
2245
+ numberOfInboundRtpStreams = 0;
2246
+ numberOfInbundTracks = 0;
2247
+ numberOfOutboundRtpStreams = 0;
2248
+ numberOfOutboundTracks = 0;
2249
+ numberOfDataChannels = 0;
2250
+ deltaNumberOfIssues = 0;
2251
+ totalScoreSum = 0;
2252
+ numberOfScoreMeasurements = 0;
2253
+ // public totalNumberOfIssues = 0;
2254
+ report;
2255
+ mediaDevices = [];
2256
+ issues = [];
2257
+ _injections = {};
2258
+ closeTimer;
2259
+ get numberOfPeerConnections() {
2260
+ return this.observedPeerConnections.size;
2261
+ }
2262
+ get score() {
2263
+ return this.calculatedScore.value;
2264
+ }
2265
+ close() {
2266
+ if (this.closed) return;
2267
+ this.closed = true;
2268
+ this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
2269
+ this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
2270
+ this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
2271
+ this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
2272
+ Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
2273
+ if (!this.leftAt) {
2274
+ this.leftAt = this.lastSampleTimestamp;
2275
+ if (this.leftAt) {
2276
+ this.emit("left");
2277
+ this._notify("client-left", { ...this.eventScope });
2278
+ }
2279
+ }
2280
+ this.closedAt = Date.now();
2281
+ this.emit("close");
2282
+ this._notify("client-closed", { ...this.eventScope });
2283
+ try {
2284
+ this.sink?.end();
2285
+ } catch (err) {
2286
+ logger2.warn("ClientSampleSink.end failed for client %s: %o", this.clientId, err);
2287
+ }
2288
+ }
2289
+ accept(sample, context) {
2290
+ if (this.closed) {
2291
+ logger2.warn("Attempted to accept a sample on a closed client %s", this.clientId);
2292
+ return;
2293
+ }
2294
+ this.sink?.write(sample);
2295
+ if (this.closeTimer) {
2296
+ clearTimeout(this.closeTimer);
2297
+ this.closeTimer = void 0;
2298
+ }
2299
+ const now = Date.now();
2300
+ const elapsedInMs = now - this.updated;
2301
+ const elapsedInSeconds = elapsedInMs / 1e3;
2302
+ let sumOfRtts = 0;
2303
+ let numberOfRttMeasurements = 0;
2304
+ ++this.acceptedSamples;
2305
+ this.availableIncomingBitrate = 0;
2306
+ this.availableOutgoingBitrate = 0;
2307
+ this.deltaDataChannelBytesReceived = 0;
2308
+ this.deltaDataChannelBytesSent = 0;
2309
+ this.deltaDataChannelMessagesReceived = 0;
2310
+ this.deltaDataChannelMessagesSent = 0;
2311
+ this.deltaInboundPacketsLost = 0;
2312
+ this.deltaInboundPacketsReceived = 0;
2313
+ this.deltaOutboundPacketsSent = 0;
2314
+ this.deltaReceivedAudioBytes = 0;
2315
+ this.deltaReceivedVideoBytes = 0;
2316
+ this.deltaSentAudioBytes = 0;
2317
+ this.deltaSentVideoBytes = 0;
2318
+ this.deltaTransportReceivedBytes = 0;
2319
+ this.deltaTransportSentBytes = 0;
2320
+ this.deltaRttLt50Measurements = 0;
2321
+ this.deltaRttLt150Measurements = 0;
2322
+ this.deltaRttLt300Measurements = 0;
2323
+ this.deltaRttGtOrEq300Measurements = 0;
2324
+ this.deltaNumberOfIssues = 0;
2325
+ this.numberOfDataChannels = 0;
2326
+ this.numberOfInboundRtpStreams = 0;
2327
+ this.numberOfInbundTracks = 0;
2328
+ this.numberOfOutboundRtpStreams = 0;
2329
+ this.numberOfOutboundTracks = 0;
2330
+ this.usingTURN = false;
2331
+ this.usingTCP = false;
2332
+ this.currentMinRttInMs = void 0;
2333
+ this.currentMaxRttInMs = void 0;
2334
+ this._mergeInjections(sample);
2335
+ const clientEventsPostBuffer = [];
2336
+ for (const clientEvent of sample.clientEvents ?? []) {
2337
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
2338
+ }
2339
+ for (const metaData of sample.clientMetaItems ?? []) {
2340
+ this.addMetadata(metaData);
2341
+ }
2342
+ for (const issue of sample.clientIssues ?? []) {
2343
+ this.addIssue(issue);
2344
+ ++this.deltaNumberOfIssues;
2345
+ }
2346
+ for (const extensionStat of sample.extensionStats ?? []) {
2347
+ this.addExtensionStats(extensionStat);
2348
+ }
2349
+ for (const pcSample of sample.peerConnections ?? []) {
2350
+ const observedPeerConnection = this._updatePeerConnection(pcSample, context);
2351
+ if (!observedPeerConnection) continue;
2352
+ this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
2353
+ this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
2354
+ this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
2355
+ this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
2356
+ this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
2357
+ this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
2358
+ this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
2359
+ this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
2360
+ this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
2361
+ this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
2362
+ this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
2363
+ this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
2364
+ this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
2365
+ this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
2366
+ this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
2367
+ this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
2368
+ this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
2369
+ this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
2370
+ this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
2371
+ this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
2372
+ if (observedPeerConnection.usingTURN) {
2373
+ this.usingTURN = true;
2374
+ }
2375
+ if (observedPeerConnection.usingTCP) {
2376
+ this.usingTCP = true;
2377
+ }
2378
+ if (observedPeerConnection.currentRttInMs) {
2379
+ if (this.currentMinRttInMs === void 0 || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
2380
+ this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
2381
+ }
2382
+ if (this.currentMaxRttInMs === void 0 || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
2383
+ this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
2384
+ }
2385
+ if (observedPeerConnection.currentRttInMs < 50) {
2386
+ this.deltaRttLt50Measurements += 1;
2387
+ this.report.rttDistribution.lt50ms += 1;
2388
+ } else if (observedPeerConnection.currentRttInMs < 150) {
2389
+ this.deltaRttLt150Measurements += 1;
2390
+ this.report.rttDistribution.lt150ms += 1;
2391
+ } else if (observedPeerConnection.currentRttInMs < 300) {
2392
+ this.deltaRttLt300Measurements += 1;
2393
+ this.report.rttDistribution.lt300ms += 1;
2394
+ } else if (300 <= observedPeerConnection.currentRttInMs) {
2395
+ this.deltaRttGtOrEq300Measurements += 1;
2396
+ this.report.rttDistribution.gtOrEq300ms += 1;
2397
+ }
2398
+ this.report.rttDistribution.count += 1;
2399
+ this.report.rttDistribution.sum += observedPeerConnection.currentRttInMs;
2400
+ sumOfRtts += observedPeerConnection.currentRttInMs;
2401
+ ++numberOfRttMeasurements;
2402
+ }
2403
+ }
2404
+ for (const clientEvent of clientEventsPostBuffer) {
2405
+ this._processClientEvent(clientEvent);
2406
+ }
2407
+ this.attachments = sample.attachments;
2408
+ this.report.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
2409
+ this.report.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
2410
+ this.report.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
2411
+ this.report.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
2412
+ this.report.totalInboundRtpPacketsLost += this.deltaInboundPacketsLost;
2413
+ this.report.totalInboundRtpPacketsReceived += this.deltaInboundPacketsReceived;
2414
+ this.report.totalOutboundRtpPacketsSent += this.deltaOutboundPacketsSent;
2415
+ this.report.totalAudioBytesReceived += this.deltaReceivedAudioBytes;
2416
+ this.report.totalVideoBytesReceived += this.deltaReceivedVideoBytes;
2417
+ this.report.totalAudioBytesSent += this.deltaSentAudioBytes;
2418
+ this.report.totalVideoBytesSent += this.deltaSentVideoBytes;
2419
+ this.report.totalInboundRtpBytesReceived += this.deltaTransportReceivedBytes;
2420
+ this.report.totalOutboundRtpBytesSent += this.deltaTransportSentBytes;
2421
+ this.report.totalNumberOfIssues += this.deltaNumberOfIssues;
2422
+ this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedInSeconds;
2423
+ this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedInSeconds;
2424
+ this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedInSeconds;
2425
+ this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedInSeconds;
2426
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : void 0;
2427
+ this.calculatedScore.value = sample.score;
2428
+ if (sample.score !== void 0) {
2429
+ const bucket = Math.floor(sample.score).toString();
2430
+ this.report.scoreDistribution[bucket] = (this.report.scoreDistribution[bucket] ?? 0) + 1;
2431
+ this.report.scoreDistribution.count += 1;
2432
+ this.report.scoreDistribution.sum += sample.score;
2433
+ }
2434
+ this.lastSampleTimestamp = sample.timestamp;
2435
+ const elapsedTimeInMs = now - this.updated;
2436
+ this.emit("update", sample, elapsedTimeInMs, context);
2437
+ this._notify("client-updated", { ...this.eventScope, sample, elapsedTimeInMs, context });
2438
+ this.updated = now;
2439
+ if (this.calculatedScore.value) {
2440
+ this.totalScoreSum += this.calculatedScore.value;
2441
+ ++this.numberOfScoreMeasurements;
2442
+ }
2443
+ if (this.settings.closeClientIfIdleForMs !== void 0) {
2444
+ this.closeTimer = setTimeout(() => {
2445
+ this.close();
2446
+ }, this.settings.closeClientIfIdleForMs);
2447
+ }
2448
+ }
2449
+ injectMetaData(metaData) {
2450
+ if (this.closed) return;
2451
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
2452
+ this._injections.clientMetaItems.push(metaData);
2453
+ }
2454
+ injectEvent(event) {
2455
+ if (this.closed) return;
2456
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
2457
+ this._injections.clientEvents.push(event);
2458
+ }
2459
+ injectIssue(issue) {
2460
+ if (this.closed) return;
2461
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
2462
+ this._injections.clientIssues.push(issue);
2463
+ }
2464
+ injectExtensionStat(stat) {
2465
+ if (this.closed) return;
2466
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
2467
+ this._injections.extensionStats.push(stat);
2468
+ }
2469
+ injectAttachment(key, value) {
2470
+ if (this.closed) return;
2471
+ if (!this._injections.attachments) this._injections.attachments = {};
2472
+ this._injections.attachments[key] = value;
2473
+ }
2474
+ addMetadata(metadata) {
2475
+ if (this.closed) return;
2476
+ switch (metadata.type) {
2477
+ case "BROWSER" /* BROWSER */: {
2478
+ this.browser = parseJsonAs(metadata.payload);
2479
+ break;
2480
+ }
2481
+ case "ENGINE" /* ENGINE */: {
2482
+ this.engine = parseJsonAs(metadata.payload);
2483
+ break;
2484
+ }
2485
+ case "PLATFORM" /* PLATFORM */: {
2486
+ this.platform = parseJsonAs(metadata.payload);
2487
+ break;
2488
+ }
2489
+ case "OPERATION_SYSTEM" /* OPERATION_SYSTEM */: {
2490
+ this.operationSystem = parseJsonAs(metadata.payload);
2491
+ break;
2492
+ }
2493
+ }
2494
+ this._notify("client-metadata", { ...this.eventScope, metaData: metadata });
2495
+ }
2496
+ addIssue(issue) {
2497
+ if (this.closed) return;
2498
+ this.report.issues[issue.type] = (this.report.issues[issue.type] ?? 0) + 1;
2499
+ this._notify("client-issue", { ...this.eventScope, issue });
2500
+ }
2501
+ addExtensionStats(stats) {
2502
+ this._notify("client-extension-stats", { ...this.eventScope, extensionStats: stats });
2503
+ }
2504
+ _processClientEvent(event, postBuffer) {
2505
+ switch (event.type) {
2506
+ case "CLIENT_JOINED" /* CLIENT_JOINED */: {
2507
+ if (event.timestamp) {
2508
+ if (!this.joinedAt) {
2509
+ this.joinedAt = event.timestamp;
2510
+ this.emit("joined");
2511
+ this._notify("client-joined", { ...this.eventScope });
2512
+ } else if (this.joinedAt < event.timestamp) {
2513
+ this._notify("client-rejoined", { ...this.eventScope, timestamp: event.timestamp });
2514
+ } else {
2515
+ this.joinedAt = event.timestamp;
2516
+ logger2.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
2517
+ }
2518
+ }
2519
+ logger2.debug("Client %s joined at %o", this.clientId, event);
2520
+ break;
2521
+ }
2522
+ case "CLIENT_LEFT" /* CLIENT_LEFT */: {
2523
+ if (event.timestamp) {
2524
+ if (!this.leftAt) {
2525
+ this.leftAt = event.timestamp;
2526
+ this.emit("left");
2527
+ this._notify("client-left", { ...this.eventScope });
2528
+ } else {
2529
+ logger2.warn(`Client ${this.clientId} leftAt timestamp was already set`);
2530
+ }
2531
+ }
2532
+ logger2.debug("Client %s left at %o", this.clientId, event);
2533
+ break;
2534
+ }
2535
+ case "PEER_CONNECTION_OPENED" /* PEER_CONNECTION_OPENED */: {
2536
+ const payload = parseJsonAs(event.payload);
2537
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string") {
2538
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2539
+ if (observedPeerConnection) {
2540
+ observedPeerConnection.openedAt = event.timestamp;
2541
+ } else if (postBuffer) {
2542
+ postBuffer.push(event);
2543
+ } else {
2544
+ logger2.warn("Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o", event);
2545
+ }
2546
+ }
2547
+ break;
2548
+ }
2549
+ case "PEER_CONNECTION_CLOSED" /* PEER_CONNECTION_CLOSED */: {
2550
+ const payload = parseJsonAs(event.payload);
2551
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string") {
2552
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2553
+ if (observedPeerConnection) {
2554
+ observedPeerConnection.closedAt = event.timestamp;
2555
+ } else if (postBuffer) {
2556
+ postBuffer.push(event);
2557
+ } else {
2558
+ logger2.warn("Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o", event);
2559
+ }
2560
+ }
2561
+ break;
2562
+ }
2563
+ case "MEDIA_TRACK_ADDED" /* MEDIA_TRACK_ADDED */: {
2564
+ const payload = parseJsonAs(event.payload);
2565
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2566
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2567
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2568
+ if (observedTrack) {
2569
+ observedTrack.addedAt = event.timestamp;
2570
+ } else if (postBuffer) {
2571
+ postBuffer.push(event);
2572
+ } else {
2573
+ logger2.warn("Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2574
+ }
2575
+ }
2576
+ break;
2577
+ }
2578
+ case "MEDIA_TRACK_REMOVED" /* MEDIA_TRACK_REMOVED */: {
2579
+ const payload = parseJsonAs(event.payload);
2580
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2581
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2582
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2583
+ if (observedTrack) {
2584
+ observedTrack.removedAt = event.timestamp;
2585
+ } else if (postBuffer) {
2586
+ postBuffer.push(event);
2587
+ } else {
2588
+ logger2.warn("Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2589
+ }
2590
+ }
2591
+ break;
2592
+ }
2593
+ case "DATA_CHANNEL_OPEN" /* DATA_CHANNEL_OPEN */: {
2594
+ const payload = parseJsonAs(event.payload);
2595
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.dataChannelId && typeof payload.dataChannelId === "string") {
2596
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2597
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
2598
+ if (observedDataChannel) {
2599
+ observedDataChannel.addedAt = event.timestamp;
2600
+ } else if (postBuffer) {
2601
+ postBuffer.push(event);
2602
+ } else {
2603
+ logger2.warn("Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o", event);
2604
+ }
2605
+ }
2606
+ break;
2607
+ }
2608
+ case "DATA_CHANNEL_CLOSED" /* DATA_CHANNEL_CLOSED */: {
2609
+ const payload = parseJsonAs(event.payload);
2610
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.dataChannelId && typeof payload.dataChannelId === "string") {
2611
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2612
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
2613
+ if (observedDataChannel) {
2614
+ observedDataChannel.removedAt = event.timestamp;
2615
+ } else if (postBuffer) {
2616
+ postBuffer.push(event);
2617
+ } else {
2618
+ logger2.warn("Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o", event);
2619
+ }
2620
+ }
2621
+ break;
2622
+ }
2623
+ case "MEDIA_TRACK_MUTED" /* MEDIA_TRACK_MUTED */: {
2624
+ const payload = parseJsonAs(event.payload);
2625
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2626
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2627
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
2628
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2629
+ if (observedPeerConnection) {
2630
+ if (observedInboundTrack) {
2631
+ observedInboundTrack.muted = true;
2632
+ this._notify("inbound-track-muted", { ...this.eventScope, observedPeerConnection, observedInboundTrack });
2633
+ } else if (observedOutboundTrack) {
2634
+ observedOutboundTrack.muted = true;
2635
+ this._notify("outbound-track-muted", { ...this.eventScope, observedPeerConnection, observedOutboundTrack });
2636
+ }
2637
+ } else if (postBuffer) {
2638
+ postBuffer.push(event);
2639
+ } else {
2640
+ logger2.warn("Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2641
+ }
2642
+ }
2643
+ break;
2644
+ }
2645
+ case "MEDIA_TRACK_UNMUTED" /* MEDIA_TRACK_UNMUTED */: {
2646
+ const payload = parseJsonAs(event.payload);
2647
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2648
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2649
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
2650
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2651
+ if (observedPeerConnection) {
2652
+ if (observedInboundTrack) {
2653
+ observedInboundTrack.muted = false;
2654
+ this._notify("inbound-track-unmuted", { ...this.eventScope, observedPeerConnection, observedInboundTrack });
2655
+ } else if (observedOutboundTrack) {
2656
+ observedOutboundTrack.muted = false;
2657
+ this._notify("outbound-track-unmuted", { ...this.eventScope, observedPeerConnection, observedOutboundTrack });
2658
+ }
2659
+ } else if (postBuffer) {
2660
+ postBuffer.push(event);
2661
+ } else {
2662
+ logger2.warn("Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2663
+ }
2664
+ }
2665
+ break;
2666
+ }
2667
+ case "ICE_CONNECTION_STATE_CHANGED" /* ICE_CONNECTION_STATE_CHANGED */: {
2668
+ const payload = parseJsonAs(event.payload);
2669
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.iceConnectionState && typeof payload.iceConnectionState === "string") {
2670
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2671
+ if (observedPeerConnection) {
2672
+ observedPeerConnection.iceConnectionState = payload.iceConnectionState;
2673
+ this._notify("ice-connection-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.iceConnectionState });
2674
+ } else if (postBuffer) {
2675
+ postBuffer.push(event);
2676
+ } else {
2677
+ logger2.warn("Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2678
+ }
2679
+ }
2680
+ break;
2681
+ }
2682
+ case "ICE_GATHERING_STATE_CHANGED" /* ICE_GATHERING_STATE_CHANGED */: {
2683
+ const payload = parseJsonAs(event.payload);
2684
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.iceGatheringState && typeof payload.iceGatheringState === "string") {
2685
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2686
+ if (observedPeerConnection) {
2687
+ observedPeerConnection.iceGatheringState = payload.iceGatheringState;
2688
+ this._notify("ice-gathering-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.iceGatheringState });
2689
+ } else if (postBuffer) {
2690
+ postBuffer.push(event);
2691
+ } else {
2692
+ logger2.warn("Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2693
+ }
2694
+ }
2695
+ break;
2696
+ }
2697
+ case "PEER_CONNECTION_STATE_CHANGED" /* PEER_CONNECTION_STATE_CHANGED */: {
2698
+ const payload = parseJsonAs(event.payload);
2699
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.peerConnectionState && typeof payload.peerConnectionState === "string") {
2700
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2701
+ if (observedPeerConnection) {
2702
+ observedPeerConnection.connectionState = payload.peerConnectionState;
2703
+ this._notify("connection-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.peerConnectionState });
2704
+ } else if (postBuffer) {
2705
+ postBuffer.push(event);
2706
+ } else {
2707
+ logger2.warn("Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2708
+ }
2709
+ }
2710
+ break;
2711
+ }
2712
+ }
2713
+ this._notify("client-event", { ...this.eventScope, event });
2714
+ }
2715
+ _updatePeerConnection(sample, context) {
2716
+ let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
2717
+ if (!observedPeerConnection) {
2718
+ if (!sample.peerConnectionId) {
2719
+ return logger2.warn(
2720
+ `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
2721
+ sample
2722
+ ), void 0;
2723
+ }
2724
+ const newObservedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
2725
+ const onInboundTrackRemoved = (track) => {
2726
+ this._notify("client-track-report", {
2727
+ ...this.eventScope,
2728
+ report: {
2729
+ direction: "inbound",
2730
+ ...track.report
2731
+ }
2732
+ });
2733
+ };
2734
+ const onOutboundTrackRemoved = (track) => {
2735
+ this._notify("client-track-report", {
2736
+ ...this.eventScope,
2737
+ report: {
2738
+ direction: "outbound",
2739
+ ...track.report
2740
+ }
2741
+ });
2742
+ };
2743
+ newObservedPeerConnection.once("close", () => {
2744
+ newObservedPeerConnection?.off("removed-inbound-track", onInboundTrackRemoved);
2745
+ newObservedPeerConnection?.off("removed-outbound-track", onOutboundTrackRemoved);
2746
+ this.observedPeerConnections.delete(sample.peerConnectionId);
2747
+ });
2748
+ newObservedPeerConnection.on("removed-inbound-track", onInboundTrackRemoved);
2749
+ newObservedPeerConnection.on("removed-outbound-track", onOutboundTrackRemoved);
2750
+ this.observedPeerConnections.set(sample.peerConnectionId, newObservedPeerConnection);
2751
+ this._notify("peer-connection-added", { ...this.eventScope, observedPeerConnection: newObservedPeerConnection });
2752
+ observedPeerConnection = newObservedPeerConnection;
2753
+ }
2754
+ observedPeerConnection.accept(sample, context);
2755
+ return observedPeerConnection;
2756
+ }
2757
+ _mergeInjections(sample) {
2758
+ if (this.closed) return sample;
2759
+ if (this._injections.clientEvents) {
2760
+ if (!sample.clientEvents) sample.clientEvents = [];
2761
+ sample.clientEvents.push(...this._injections.clientEvents);
2762
+ this._injections.clientEvents = void 0;
2763
+ }
2764
+ if (this._injections.clientIssues) {
2765
+ if (!sample.clientIssues) sample.clientIssues = [];
2766
+ sample.clientIssues.push(...this._injections.clientIssues);
2767
+ this._injections.clientIssues = void 0;
2768
+ }
2769
+ if (this._injections.extensionStats) {
2770
+ if (!sample.extensionStats) sample.extensionStats = [];
2771
+ sample.extensionStats.push(...this._injections.extensionStats);
2772
+ this._injections.extensionStats = void 0;
2773
+ }
2774
+ if (this._injections.attachments) {
2775
+ if (!sample.attachments) sample.attachments = {};
2776
+ Object.assign(sample.attachments, this._injections.attachments);
2777
+ this._injections.attachments = void 0;
2778
+ }
2779
+ if (this._injections.clientMetaItems) {
2780
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
2781
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
2782
+ this._injections.clientMetaItems = void 0;
2783
+ }
2784
+ return sample;
2785
+ }
2786
+ /** Emit an Observer-bus event scoped to this client (or a peer connection under it). */
2787
+ _notify(type, ...args) {
2788
+ this.call.observer.emit(type, ...args);
2789
+ }
2790
+ };
2791
+
2792
+ // src/scores/DefaultCallScoreCalculator.ts
2793
+ var DefaultCallScoreCalculator = class {
2794
+ constructor(observedCall) {
2795
+ this.observedCall = observedCall;
2796
+ }
2797
+ observedCall;
2798
+ update() {
2799
+ let totalScore = 0;
2800
+ let totalWeight = 0;
2801
+ for (const client of this.observedCall.observedClients.values()) {
2802
+ if (client.score === void 0) continue;
2803
+ totalScore += client.score * client.calculatedScore.weight;
2804
+ totalWeight += client.calculatedScore.weight;
2805
+ }
2806
+ this.observedCall.calculatedScore.value = totalWeight ? totalScore / totalWeight : void 0;
2807
+ }
2808
+ };
2809
+
2810
+ // src/updaters/OnAllClientCallUpdater.ts
2811
+ var OnAllClientCallUpdater = class {
2812
+ constructor(_observedCall) {
2813
+ this._observedCall = _observedCall;
2814
+ this._onNewObservedClient = this._onNewObservedClient.bind(this);
2815
+ this._observedCall.once("close", () => {
2816
+ this._observedCall.off("newclient", this._onNewObservedClient);
2817
+ });
2818
+ this._observedCall.on("newclient", this._onNewObservedClient);
2819
+ }
2820
+ _observedCall;
2821
+ name = "OnAllClientCallUpdater";
2822
+ description = "Call the update() method of the ObservedCall once all client has been updated";
2823
+ _updatedClients = /* @__PURE__ */ new Set();
2824
+ closed = false;
2825
+ close() {
2826
+ if (this.closed) return;
2827
+ this.closed = true;
2828
+ this._updatedClients.clear();
2829
+ }
2830
+ _onNewObservedClient(observedClient) {
2831
+ if (this.closed) return;
2832
+ const onUpdate = (...args) => this._onClientUpdate(observedClient, args[2]);
2833
+ observedClient.once("close", () => {
2834
+ observedClient.off("update", onUpdate);
2835
+ this._updatedClients.delete(observedClient.clientId);
2836
+ this._updateIfEveryClientUpdated();
2837
+ });
2838
+ observedClient.on("update", onUpdate);
2839
+ }
2840
+ _onClientUpdate(observedClient, context) {
2841
+ if (observedClient.closed) return;
2842
+ this._updatedClients.add(observedClient.clientId);
2843
+ this._updateIfEveryClientUpdated(context);
2844
+ }
2845
+ _updateIfEveryClientUpdated(context) {
2846
+ if (this._updatedClients.size < this._observedCall.observedClients.size) {
2847
+ return;
2848
+ } else if (this.closed) {
2849
+ return;
2850
+ }
2851
+ this._updatedClients.clear();
2852
+ this._observedCall.update(context);
2853
+ }
2854
+ };
2855
+
2856
+ // src/updaters/OnIntervalUpdater.ts
2857
+ var OnIntervalUpdater = class {
2858
+ constructor(intervalInMs, _update) {
2859
+ this._update = _update;
2860
+ this.timer = setInterval(() => this._update(), intervalInMs);
2861
+ }
2862
+ _update;
2863
+ name = "OnIntervalUpdater";
2864
+ description = "Call the update() method given in the constructor once the interval elapsed";
2865
+ timer;
2866
+ close() {
2867
+ clearInterval(this.timer);
2868
+ }
2869
+ };
2870
+
2871
+ // src/updaters/OnAnyClientCallUpdater.ts
2872
+ var OnAnyClientCallUpdater = class {
2873
+ constructor(_observedCall) {
2874
+ this._observedCall = _observedCall;
2875
+ this._onNewObservedClient = this._onNewObservedClient.bind(this);
2876
+ this._observedCall.once("close", () => {
2877
+ this._observedCall.off("newclient", this._onNewObservedClient);
2878
+ });
2879
+ this._observedCall.on("newclient", this._onNewObservedClient);
2880
+ }
2881
+ _observedCall;
2882
+ name = "OnAnyClientCallUpdater";
2883
+ description = "Call the update() method of the ObservedCall when any of the client has been updated";
2884
+ closed = false;
2885
+ close() {
2886
+ if (this.closed) return;
2887
+ this.closed = true;
2888
+ }
2889
+ _onNewObservedClient(observedClient) {
2890
+ if (this.closed) return;
2891
+ const onUpdate = (...args) => {
2892
+ if (this.closed) return;
2893
+ this._observedCall.update(args[2]);
2894
+ };
2895
+ observedClient.once("close", () => {
2896
+ observedClient.off("update", onUpdate);
2897
+ });
2898
+ observedClient.on("update", onUpdate);
2899
+ }
2900
+ };
2901
+
2902
+ // src/detectors/Detectors.ts
2903
+ var logger3 = createLogger("Detectors");
2904
+ var Detectors = class {
2905
+ _detectors;
2906
+ constructor(...detectors) {
2907
+ this._detectors = detectors;
2908
+ }
2909
+ get listOfNames() {
2910
+ return this._detectors.map((d) => d.name);
2911
+ }
2912
+ add(detector) {
2913
+ this._detectors.push(detector);
2914
+ }
2915
+ remove(detector) {
2916
+ this._detectors = this._detectors.filter((d) => d !== detector);
2917
+ }
2918
+ update() {
2919
+ for (const detector of this._detectors) {
2920
+ try {
2921
+ detector.update();
2922
+ } catch (err) {
2923
+ logger3.warn(`Error updating detector ${detector?.name}`, err);
2924
+ }
2925
+ }
2926
+ }
2927
+ clear() {
2928
+ this._detectors = [];
2929
+ }
2930
+ };
2931
+
2932
+ // src/ObservedCall.ts
2933
+ var logger4 = createLogger("ObservedCall");
2934
+ var ObservedCall = class extends EventEmitter3 {
2935
+ constructor(settings, observer) {
2936
+ super();
2937
+ this.observer = observer;
2938
+ this.setMaxListeners(Infinity);
2939
+ this.eventScope = { observer: this.observer, observedCall: this };
2940
+ this.callId = settings.callId;
2941
+ this.appData = settings.appData ?? {};
2942
+ this.scoreCalculator = new DefaultCallScoreCalculator(this);
2943
+ this.detectors = new Detectors();
2944
+ if (settings.updateIntervalInMs && settings.updatePolicy !== "update-on-interval") {
2945
+ logger4.warn('updateIntervalInMs is ignored unless updatePolicy is "update-on-interval" (callId: %s)', settings.callId);
2946
+ }
2947
+ switch (settings.updatePolicy) {
2948
+ case "update-on-any-client-updated":
2949
+ this.updater = new OnAnyClientCallUpdater(this);
2950
+ break;
2951
+ case "update-when-all-client-updated":
2952
+ this.updater = new OnAllClientCallUpdater(this);
2953
+ break;
2954
+ case "update-on-interval":
2955
+ if (!settings.updateIntervalInMs) {
2956
+ logger4.warn('updateIntervalInMs must be set when updatePolicy is "update-on-interval"; falling back to "update-when-all-client-updated" (callId: %s)', settings.callId);
2957
+ this.updater = new OnAllClientCallUpdater(this);
2958
+ } else {
2959
+ this.updater = new OnIntervalUpdater(
2960
+ settings.updateIntervalInMs,
2961
+ this.update.bind(this)
2962
+ );
2963
+ }
2964
+ break;
2965
+ }
2966
+ this.settings = {
2967
+ closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs
2968
+ };
2969
+ }
2970
+ observer;
2971
+ updater;
2972
+ scoreCalculator;
2973
+ detectors;
2974
+ callId;
2975
+ observedClients = /* @__PURE__ */ new Map();
2976
+ clientsUsedTurn = /* @__PURE__ */ new Set();
2977
+ calculatedScore = {
2978
+ weight: 1,
2979
+ value: void 0
2980
+ };
2981
+ remoteTrackResolver;
2982
+ totalAddedClients = 0;
2983
+ totalRemovedClients = 0;
2984
+ numberOfIssues = 0;
2985
+ numberOfPeerConnections = 0;
2986
+ numberOfInboundRtpStreams = 0;
2987
+ numberOfOutboundRtpStreams = 0;
2988
+ numberOfDataChannels = 0;
2989
+ maxNumberOfClients = 0;
2990
+ deltaNumberOfIssues = 0;
2991
+ // public deltaRttLt50Measurements = 0;
2992
+ // public deltaRttLt150Measurements = 0;
2993
+ // public deltaRttLt300Measurements = 0;
2994
+ // public deltaRttGtOrEq300Measurements = 0;
2995
+ appData;
2996
+ closed = false;
2997
+ startedAt;
2998
+ endedAt;
2999
+ closedAt;
3000
+ settings;
3001
+ /** Ancestry base shared by all Observer-bus events originating at this call. */
3002
+ eventScope;
3003
+ closeTimer;
3004
+ get numberOfClients() {
3005
+ return this.observedClients.size;
3006
+ }
3007
+ get score() {
3008
+ return this.calculatedScore.value;
3009
+ }
3010
+ /** Raise a call-level (server-side) issue; surfaced on the Observer bus as `call-issue`. */
3011
+ addIssue(issue) {
3012
+ if (this.closed) return;
3013
+ this._notify("call-issue", { ...this.eventScope, issue });
3014
+ }
3015
+ close() {
3016
+ if (this.closed) return;
3017
+ this.update();
3018
+ this.closed = true;
3019
+ this.updater?.close();
3020
+ let minSampleTimestamps;
3021
+ let maxSampleTimestamps;
3022
+ for (const client of this.observedClients.values()) {
3023
+ client.close();
3024
+ if (client.joinedAt) minSampleTimestamps = Math.min(minSampleTimestamps ?? client.joinedAt, client.joinedAt);
3025
+ if (client.leftAt) maxSampleTimestamps = Math.max(maxSampleTimestamps ?? client.leftAt, client.leftAt);
3026
+ }
3027
+ if (this.startedAt === void 0) this.startedAt = minSampleTimestamps;
3028
+ if (this.endedAt === void 0) this.endedAt = maxSampleTimestamps;
3029
+ this.closedAt = Date.now();
3030
+ this.emit("close");
3031
+ this._notify("call-closed", { ...this.eventScope });
3032
+ }
3033
+ getObservedClient(clientId) {
3034
+ if (this.closed || !this.observedClients.has(clientId)) return;
3035
+ return this.observedClients.get(clientId);
3036
+ }
3037
+ createObservedClient(settings) {
3038
+ if (this.closed) {
3039
+ logger4.warn("Attempted to create a client (clientId: %s) on a closed call %s", settings.clientId, this.callId);
3040
+ return void 0;
3041
+ }
3042
+ if (this.observedClients.has(settings.clientId)) {
3043
+ logger4.warn("Client with id %s already exists in call %s; returning the existing instance", settings.clientId, this.callId);
3044
+ return this.observedClients.get(settings.clientId);
3045
+ }
3046
+ if (!settings.closeClientIfIdleForMs) {
3047
+ settings.closeClientIfIdleForMs = this.observer.config.closeClientIfIdleForMs;
3048
+ }
3049
+ if (settings.appData === void 0) {
3050
+ settings.appData = this.observer.config.createClientAppData?.({ clientId: settings.clientId, observedCall: this });
3051
+ }
3052
+ const result = new ObservedClient(settings, this);
3053
+ const wasEmpty = this.observedClients.size === 0;
3054
+ const onUpdate = () => this._onClientUpdate(result);
3055
+ const joined = () => this._clientJoined(result);
3056
+ const left = () => this._clientLeft(result);
3057
+ result.once("close", () => {
3058
+ result.off("update", onUpdate);
3059
+ result.off("joined", joined);
3060
+ result.off("left", left);
3061
+ this.observedClients.delete(settings.clientId);
3062
+ if (this.observedClients.size === 0) {
3063
+ this.emit("empty");
3064
+ this._notify("call-empty", { ...this.eventScope });
3065
+ if (this.settings.closeCallIfEmptyForMs) {
3066
+ this.closeTimer = setTimeout(() => {
3067
+ this.close();
3068
+ }, this.settings.closeCallIfEmptyForMs);
3069
+ }
3070
+ }
3071
+ ++this.totalRemovedClients;
3072
+ });
3073
+ result.on("update", onUpdate);
3074
+ result.on("joined", joined);
3075
+ result.on("left", left);
3076
+ ++this.totalAddedClients;
3077
+ this.observedClients.set(settings.clientId, result);
3078
+ this.maxNumberOfClients = Math.max(this.maxNumberOfClients, this.observedClients.size);
3079
+ if (this.closeTimer) {
3080
+ clearTimeout(this.closeTimer);
3081
+ this.closeTimer = void 0;
3082
+ }
3083
+ this.emit("newclient", result);
3084
+ this._notify("client-added", { ...this.eventScope, observedClient: result });
3085
+ if (result.sink) {
3086
+ this._notify("client-sink-created", { ...this.eventScope, observedClient: result, sink: result.sink });
3087
+ }
3088
+ if (wasEmpty) {
3089
+ this.emit("not-empty");
3090
+ this._notify("call-not-empty", { ...this.eventScope });
3091
+ }
3092
+ return result;
3093
+ }
3094
+ getOrCreateObservedClient(settings) {
3095
+ return this.getObservedClient(settings.clientId) ?? this.createObservedClient(settings);
3096
+ }
3097
+ update(context) {
3098
+ if (this.closed) return;
3099
+ this.numberOfInboundRtpStreams = 0;
3100
+ this.numberOfOutboundRtpStreams = 0;
3101
+ this.numberOfPeerConnections = 0;
3102
+ this.numberOfDataChannels = 0;
3103
+ for (const client of this.observedClients.values()) {
3104
+ this.numberOfInboundRtpStreams += client.numberOfInboundRtpStreams;
3105
+ this.numberOfOutboundRtpStreams += client.numberOfOutboundRtpStreams;
3106
+ this.numberOfPeerConnections += client.numberOfPeerConnections;
3107
+ this.numberOfDataChannels += client.numberOfDataChannels;
3108
+ }
3109
+ this.scoreCalculator.update();
3110
+ this.detectors.update();
3111
+ this.emit("update");
3112
+ this._notify("call-updated", { ...this.eventScope, context });
3113
+ this.deltaNumberOfIssues = 0;
3114
+ }
3115
+ _onClientUpdate(client) {
3116
+ this.deltaNumberOfIssues += client.deltaNumberOfIssues;
3117
+ this.numberOfIssues += client.deltaNumberOfIssues;
3118
+ if (client.usingTURN) {
3119
+ this.clientsUsedTurn.add(client.clientId);
3120
+ }
3121
+ }
3122
+ _clientJoined(client) {
3123
+ if (!client.joinedAt) return;
3124
+ this.startedAt = Math.min(this.startedAt ?? client.joinedAt, client.joinedAt);
3125
+ }
3126
+ _clientLeft(client) {
3127
+ if (!client.leftAt) return;
3128
+ this.endedAt = Math.max(this.endedAt ?? client.leftAt, client.leftAt);
3129
+ }
3130
+ /** Emit an Observer-bus event scoped to this call. */
3131
+ _notify(type, ...args) {
3132
+ this.observer.emit(type, ...args);
3133
+ }
3134
+ };
3135
+
3136
+ // src/Observer.ts
3137
+ import { EventEmitter as EventEmitter5 } from "events";
3138
+
3139
+ // src/ObservedTURN.ts
3140
+ import { EventEmitter as EventEmitter4 } from "events";
3141
+
3142
+ // src/ObservedTurnServer.ts
3143
+ var ObservedTurnServer = class {
3144
+ constructor(url, observedTURN) {
3145
+ this.url = url;
3146
+ this.observedTURN = observedTURN;
3147
+ }
3148
+ url;
3149
+ observedTURN;
3150
+ totalBytesSent = 0;
3151
+ totalBytesReceived = 0;
3152
+ totalPacketsSent = 0;
3153
+ totalPacketsReceived = 0;
3154
+ packetsSentPerSecond = 0;
3155
+ packetsReceivedPerSecond = 0;
3156
+ outboundBitrate = 0;
3157
+ inboundBitrate = 0;
3158
+ deltaBytesSent = 0;
3159
+ deltaBytesReceived = 0;
3160
+ deltaPacketsSent = 0;
3161
+ deltaPacketsReceived = 0;
3162
+ observedPeerConnections = /* @__PURE__ */ new Map();
3163
+ updated = Date.now();
3164
+ updateTurnUsage(...selectedcandidatepairs) {
3165
+ for (const selectedcandidatepair of selectedcandidatepairs) {
3166
+ this.deltaBytesReceived += selectedcandidatepair.deltaBytesReceived;
3167
+ this.deltaBytesSent += selectedcandidatepair.deltaBytesSent;
3168
+ this.deltaPacketsReceived += selectedcandidatepair.deltaPacketsReceived;
3169
+ this.deltaPacketsSent += selectedcandidatepair.deltaPacketsSent;
3170
+ }
3171
+ }
3172
+ update() {
3173
+ const now = Date.now();
3174
+ const deltaInS = (now - this.updated) / 1e3;
3175
+ this.packetsSentPerSecond = this.deltaPacketsSent / deltaInS;
3176
+ this.packetsReceivedPerSecond = this.deltaPacketsReceived / deltaInS;
3177
+ this.outboundBitrate = this.deltaBytesReceived * 8 / deltaInS;
3178
+ this.inboundBitrate = this.deltaBytesSent * 8 / deltaInS;
3179
+ this.totalBytesSent += this.deltaBytesSent;
3180
+ this.totalBytesReceived += this.deltaBytesReceived;
3181
+ this.totalPacketsSent += this.deltaPacketsSent;
3182
+ this.totalPacketsReceived += this.deltaPacketsReceived;
3183
+ this.observedTURN.totalBytesSent += this.deltaBytesSent;
3184
+ this.observedTURN.totalBytesReceived += this.deltaBytesReceived;
3185
+ this.observedTURN.totalPacketsSent += this.deltaPacketsSent;
3186
+ this.observedTURN.totalPacketsReceived += this.deltaPacketsReceived;
3187
+ this.observedTURN.inboundBitrate += this.inboundBitrate;
3188
+ this.observedTURN.outboundBitrate += this.outboundBitrate;
3189
+ this.observedTURN.packetsReceivedPerSecond += this.packetsReceivedPerSecond;
3190
+ this.observedTURN.packetsSentPerSecond += this.packetsSentPerSecond;
3191
+ this.deltaBytesSent = 0;
3192
+ this.deltaBytesReceived = 0;
3193
+ this.deltaPacketsSent = 0;
3194
+ this.deltaPacketsReceived = 0;
3195
+ this.updated = now;
3196
+ }
3197
+ };
3198
+
3199
+ // src/ObservedTURN.ts
3200
+ var logger5 = createLogger("ObservedTURN");
3201
+ var ObservedTURN = class extends EventEmitter4 {
3202
+ totalBytesSent = 0;
3203
+ totalBytesReceived = 0;
3204
+ totalPacketsSent = 0;
3205
+ totalPacketsReceived = 0;
3206
+ packetsSentPerSecond = 0;
3207
+ packetsReceivedPerSecond = 0;
3208
+ outboundBitrate = 0;
3209
+ inboundBitrate = 0;
3210
+ numberOfClients = 0;
3211
+ servers = /* @__PURE__ */ new Map();
3212
+ constructor() {
3213
+ super();
3214
+ }
3215
+ update() {
3216
+ const clientIds = /* @__PURE__ */ new Set();
3217
+ this.packetsReceivedPerSecond = 0;
3218
+ this.packetsSentPerSecond = 0;
3219
+ this.outboundBitrate = 0;
3220
+ this.inboundBitrate = 0;
3221
+ for (const server of this.servers.values()) {
3222
+ server.update();
3223
+ server.observedPeerConnections.forEach((pc) => clientIds.add(pc.client.clientId));
3224
+ }
3225
+ this.numberOfClients = clientIds.size;
3226
+ }
3227
+ addPeerConnection(peerConnection) {
3228
+ const turnPairs = peerConnection.selectedIceCandidatePairs.filter((pair) => pair.getLocalCandidate()?.candidateType === "relay" && pair.getRemoteCandidate()?.url?.startsWith("turn:"));
3229
+ if (turnPairs.length !== 1) {
3230
+ return logger5.warn(`Expected exactly one TURN pair, but found for peerconnection ${peerConnection.peerConnectionId}`, turnPairs.length), void 0;
3231
+ }
3232
+ const candidatePair = turnPairs[0];
3233
+ const rawUrl = candidatePair.getRemoteCandidate()?.url;
3234
+ if (!rawUrl) {
3235
+ return logger5.warn(`No remote candidate URL found for peerconnection ${peerConnection.peerConnectionId}`), void 0;
3236
+ }
3237
+ const turnUrl = new URL(rawUrl);
3238
+ const turnServerUrl = `${turnUrl.protocol}//${turnUrl.hostname}:${turnUrl.port}`;
3239
+ let turnServer = this.servers.get(turnServerUrl);
3240
+ if (!turnServer) {
3241
+ turnServer = new ObservedTurnServer(turnServerUrl, this);
3242
+ }
3243
+ return turnServer;
3244
+ }
3245
+ removePeerConnection(peerConnection) {
3246
+ for (const turnServer of this.servers.values()) {
3247
+ turnServer.observedPeerConnections.delete(peerConnection.peerConnectionId);
3248
+ }
3249
+ }
3250
+ };
3251
+
3252
+ // src/updaters/OnAllCallObserverUpdater.ts
3253
+ var OnAllCallObserverUpdater = class {
3254
+ constructor(observer) {
3255
+ this.observer = observer;
3256
+ this._onNewObservedCall = this._onNewObservedCall.bind(this);
3257
+ this.observer.once("observer-closed", () => {
3258
+ this.observer.off("call-added", this._onNewObservedCall);
3259
+ });
3260
+ this.observer.on("call-added", this._onNewObservedCall);
3261
+ }
3262
+ observer;
3263
+ name = "OnAllCallObserverUpdater";
3264
+ description = "Call Observer's update() method when all of the ObservedCalls are updated";
3265
+ _updatedCalls = /* @__PURE__ */ new Set();
3266
+ closed = false;
3267
+ close() {
3268
+ if (this.closed) return;
3269
+ this.closed = true;
3270
+ this._updatedCalls.clear();
3271
+ }
3272
+ _onNewObservedCall({ observedCall }) {
3273
+ if (this.closed) return;
3274
+ const onUpdate = () => this._onObservedCallUpdated(observedCall);
3275
+ observedCall.once("close", () => {
3276
+ observedCall.off("update", onUpdate);
3277
+ this._updatedCalls.delete(observedCall.callId);
3278
+ this._updateIfEveryCallUpdated();
3279
+ });
3280
+ observedCall.on("update", onUpdate);
3281
+ }
3282
+ _onObservedCallUpdated(observedCall) {
3283
+ if (observedCall.closed) return;
3284
+ this._updatedCalls.add(observedCall.callId);
3285
+ this._updateIfEveryCallUpdated();
3286
+ }
3287
+ _updateIfEveryCallUpdated() {
3288
+ if (this._updatedCalls.size < this.observer.observedCalls.size) return;
3289
+ else if (this.closed) return;
3290
+ this._updatedCalls.clear();
3291
+ this.observer.update();
3292
+ }
3293
+ };
3294
+
3295
+ // src/updaters/OnAnyCallObserverUpdater.ts
3296
+ var OnAnyCallObserverUpdater = class {
3297
+ constructor(observver) {
3298
+ this.observver = observver;
3299
+ this._onNewObservedCall = this._onNewObservedCall.bind(this);
3300
+ this.observver.once("observer-closed", () => {
3301
+ this.observver.off("call-added", this._onNewObservedCall);
3302
+ });
3303
+ this.observver.on("call-added", this._onNewObservedCall);
3304
+ }
3305
+ observver;
3306
+ name = "OnAnyCallObserverUpdater";
3307
+ description = "Call Observer's update() method on any of the ObservedCall is updated";
3308
+ closed = false;
3309
+ close() {
3310
+ if (this.closed) return;
3311
+ this.closed = true;
3312
+ }
3313
+ _onNewObservedCall({ observedCall }) {
3314
+ if (this.closed) return;
3315
+ const onUpdate = () => {
3316
+ if (this.closed) return;
3317
+ this.observver.update();
3318
+ };
3319
+ observedCall.once("close", () => {
3320
+ observedCall.off("update", onUpdate);
3321
+ });
3322
+ observedCall.on("update", onUpdate);
3323
+ }
3324
+ };
3325
+
3326
+ // src/utils/MediasoupRemoteTrackResolver.ts
3327
+ var MediasoupRemoteTrackResolver = class {
3328
+ constructor(observedCall) {
3329
+ this.observedCall = observedCall;
3330
+ const observer = observedCall.observer;
3331
+ const onInboundAdded = (p) => {
3332
+ if (p.observedCall === this.observedCall) this._addInboundTrack(p.observedInboundTrack);
3333
+ };
3334
+ const onInboundRemoved = (p) => {
3335
+ if (p.observedCall === this.observedCall) this._removeInboundTrack(p.observedInboundTrack);
3336
+ };
3337
+ const onOutboundAdded = (p) => {
3338
+ if (p.observedCall === this.observedCall) this._addOutboundTrack(p.observedOutboundTrack);
3339
+ };
3340
+ const onOutboundRemoved = (p) => {
3341
+ if (p.observedCall === this.observedCall) this._removeOutboundTrack(p.observedOutboundTrack);
3342
+ };
3343
+ observer.on("inbound-track-added", onInboundAdded);
3344
+ observer.on("inbound-track-removed", onInboundRemoved);
3345
+ observer.on("outbound-track-added", onOutboundAdded);
3346
+ observer.on("outbound-track-removed", onOutboundRemoved);
3347
+ observedCall.once("close", () => {
3348
+ observer.off("inbound-track-added", onInboundAdded);
3349
+ observer.off("inbound-track-removed", onInboundRemoved);
3350
+ observer.off("outbound-track-added", onOutboundAdded);
3351
+ observer.off("outbound-track-removed", onOutboundRemoved);
3352
+ });
3353
+ }
3354
+ observedCall;
3355
+ _consumerIdToProducerId = /* @__PURE__ */ new Map();
3356
+ _producerIdToOutboundTrack = /* @__PURE__ */ new Map();
3357
+ _consumerIdToInboundTrack = /* @__PURE__ */ new Map();
3358
+ // private _producerIdToOutboundTrack = new Map<string, ObservedOutboundTrack>();
3359
+ _inboundTrackToConsumerId = /* @__PURE__ */ new Map();
3360
+ _producerIdToConsumerIds = /* @__PURE__ */ new Map();
3361
+ _outboundTrackToProducerId = /* @__PURE__ */ new Map();
3362
+ resolveRemoteOutboundTrack(inboundTrack) {
3363
+ const consumerId = this._inboundTrackToConsumerId.get(inboundTrack.id);
3364
+ if (!consumerId) return;
3365
+ const producerId = this._consumerIdToProducerId.get(consumerId);
3366
+ if (!producerId) return;
3367
+ return this._producerIdToOutboundTrack.get(producerId);
3368
+ }
3369
+ resolveRemoteInboundTracks(outboundTrack) {
3370
+ const producerId = this._outboundTrackToProducerId.get(outboundTrack.id);
3371
+ if (!producerId) return;
3372
+ const consumerIds = this._producerIdToConsumerIds.get(producerId);
3373
+ if (!consumerIds) return;
3374
+ return consumerIds.map((consumerId) => this._consumerIdToInboundTrack.get(consumerId)).filter((inboundTrack) => Boolean(inboundTrack));
3375
+ }
3376
+ _addInboundTrack(inboundTrack) {
3377
+ const attachments = this._getInboundTrackAttachments(inboundTrack);
3378
+ if (!attachments) return;
3379
+ const {
3380
+ producerId,
3381
+ consumerId
3382
+ } = attachments;
3383
+ this._inboundTrackToConsumerId.set(inboundTrack.id, consumerId);
3384
+ this._consumerIdToProducerId.set(consumerId, producerId);
3385
+ this._consumerIdToInboundTrack.set(consumerId, inboundTrack);
3386
+ let producerConsumers = this._producerIdToConsumerIds.get(producerId);
3387
+ if (!producerConsumers) {
3388
+ producerConsumers = [];
3389
+ this._producerIdToConsumerIds.set(producerId, producerConsumers);
3390
+ }
3391
+ producerConsumers.push(consumerId);
3392
+ }
3393
+ _removeInboundTrack(inboundTrack) {
3394
+ const consumerId = this._inboundTrackToConsumerId.get(inboundTrack.id);
3395
+ if (!consumerId) return;
3396
+ this._inboundTrackToConsumerId.delete(inboundTrack.id);
3397
+ this._consumerIdToProducerId.delete(consumerId);
3398
+ this._consumerIdToInboundTrack.delete(consumerId);
3399
+ const producerConsumers = this._producerIdToConsumerIds.get(consumerId);
3400
+ if (!producerConsumers) return;
3401
+ const filteredProducerConsumers = producerConsumers.filter((id) => id !== consumerId);
3402
+ if (filteredProducerConsumers.length === 0) {
3403
+ this._producerIdToConsumerIds.delete(consumerId);
3404
+ } else {
3405
+ this._producerIdToConsumerIds.set(consumerId, filteredProducerConsumers);
3406
+ }
3407
+ }
3408
+ _addOutboundTrack(outboundTrack) {
3409
+ const attachments = this._getOutboundTrackAttachments(outboundTrack);
3410
+ if (!attachments) return;
3411
+ const {
3412
+ producerId
3413
+ } = attachments;
3414
+ this._producerIdToOutboundTrack.set(producerId, outboundTrack);
3415
+ this._outboundTrackToProducerId.set(outboundTrack.id, producerId);
3416
+ }
3417
+ _removeOutboundTrack(outboundTrack) {
3418
+ const producerId = this._outboundTrackToProducerId.get(outboundTrack.id);
3419
+ if (!producerId) return;
3420
+ this._producerIdToOutboundTrack.delete(producerId);
3421
+ this._outboundTrackToProducerId.delete(outboundTrack.id);
3422
+ }
3423
+ _getInboundTrackAttachments(inboundTrack) {
3424
+ const {
3425
+ producerId,
3426
+ consumerId
3427
+ } = inboundTrack.attachments ?? {};
3428
+ if (!producerId || !consumerId || typeof producerId !== "string" || typeof consumerId !== "string") return;
3429
+ return {
3430
+ producerId,
3431
+ consumerId
3432
+ };
3433
+ }
3434
+ _getOutboundTrackAttachments(outboundTrack) {
3435
+ const {
3436
+ producerId
3437
+ } = outboundTrack.attachments ?? {};
3438
+ if (!producerId || typeof producerId !== "string") return;
3439
+ return {
3440
+ producerId
3441
+ };
3442
+ }
3443
+ };
3444
+
3445
+ // src/Observer.ts
3446
+ var logger6 = createLogger("Observer");
3447
+ var Observer = class extends EventEmitter5 {
3448
+ constructor(config = {
3449
+ updatePolicy: "update-when-all-call-updated",
3450
+ appData: {}
3451
+ }) {
3452
+ super();
3453
+ this.config = config;
3454
+ this.setMaxListeners(Infinity);
3455
+ this.update = this.update.bind(this);
3456
+ const currentUpdatePolicy = config?.updatePolicy ?? "update-when-all-call-updated";
3457
+ switch (currentUpdatePolicy) {
3458
+ case "update-on-any-call-updated":
3459
+ this.updater = new OnAnyCallObserverUpdater(this);
3460
+ break;
3461
+ case "update-when-all-call-updated":
3462
+ this.updater = new OnAllCallObserverUpdater(this);
3463
+ break;
3464
+ case "update-on-interval": {
3465
+ const interval = config?.updateIntervalInMs;
3466
+ if (!interval) {
3467
+ logger6.warn('updateIntervalInMs must be set when updatePolicy is "update-on-interval"; falling back to "update-when-all-call-updated"');
3468
+ this.updater = new OnAllCallObserverUpdater(this);
3469
+ } else {
3470
+ this.updater = new OnIntervalUpdater(
3471
+ interval,
3472
+ this.update.bind(this)
3473
+ );
3474
+ }
3475
+ break;
3476
+ }
3477
+ }
3478
+ }
3479
+ config;
3480
+ observedTURN = new ObservedTURN();
3481
+ observedCalls = /* @__PURE__ */ new Map();
3482
+ updater;
3483
+ /** Ancestry base shared by all Observer-bus events originating at the observer. */
3484
+ eventScope = { observer: this };
3485
+ closed = false;
3486
+ totalAddedCall = 0;
3487
+ totalRemovedCall = 0;
3488
+ numberOfClientsUsingTurn = 0;
3489
+ numberOfClients = 0;
3490
+ numberOfInboundRtpStreams = 0;
3491
+ numberOfOutboundRtpStreams = 0;
3492
+ numberOfDataChannels = 0;
3493
+ numberOfPeerConnections = 0;
3494
+ _timer;
3495
+ get numberOfCalls() {
3496
+ return this.observedCalls.size;
3497
+ }
3498
+ get appData() {
3499
+ return this.config.appData;
3500
+ }
3501
+ getObservedCall(callId) {
3502
+ if (this.closed || !this.observedCalls.has(callId)) return;
3503
+ return this.observedCalls.get(callId);
3504
+ }
3505
+ createObservedCall(settings) {
3506
+ if (this.closed) {
3507
+ logger6.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3508
+ return void 0;
3509
+ }
3510
+ if (this.observedCalls.has(settings.callId)) {
3511
+ logger6.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3512
+ return this.observedCalls.get(settings.callId);
3513
+ }
3514
+ const callSettings = {
3515
+ ...settings,
3516
+ updatePolicy: settings.updatePolicy ?? this.config.defaultCallUpdatePolicy,
3517
+ updateIntervalInMs: settings.updateIntervalInMs ?? this.config.defaultCallUpdateIntervalInMs,
3518
+ closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs ?? this.config.closeCallIfEmptyForMs,
3519
+ appData: settings.appData ?? this.config.createCallAppData?.({ callId: settings.callId, observer: this })
3520
+ };
3521
+ const observedCall = new ObservedCall(callSettings, this);
3522
+ if (callSettings.remoteTrackResolvePolicy === "mediasoup-sfu") {
3523
+ observedCall.remoteTrackResolver = new MediasoupRemoteTrackResolver(observedCall);
3524
+ }
3525
+ observedCall.once("close", () => {
3526
+ this.observedCalls.delete(observedCall.callId);
3527
+ ++this.totalRemovedCall;
3528
+ });
3529
+ this.observedCalls.set(observedCall.callId, observedCall);
3530
+ ++this.totalAddedCall;
3531
+ this._notify("call-added", { ...this.eventScope, observedCall });
3532
+ return observedCall;
3533
+ }
3534
+ getOrCreateObservedCall(settings) {
3535
+ return this.getObservedCall(settings.callId) ?? this.createObservedCall(settings);
3536
+ }
3537
+ close() {
3538
+ if (this.closed) {
3539
+ return logger6.debug("Attempted to close twice");
3540
+ }
3541
+ this.closed = true;
3542
+ clearInterval(this._timer);
3543
+ this._timer = void 0;
3544
+ this.observedCalls.forEach((call) => call.close());
3545
+ this._notify("observer-closed", { ...this.eventScope });
3546
+ }
3547
+ accept(sample, context) {
3548
+ if (this.closed) {
3549
+ this._notify("sample-rejected", { ...this.eventScope, reason: "observer-closed", sample });
3550
+ return;
3551
+ }
3552
+ if (!sample.callId) {
3553
+ this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
3554
+ return;
3555
+ }
3556
+ if (!sample.clientId) {
3557
+ this._notify("sample-rejected", { ...this.eventScope, reason: "missing-clientId", sample });
3558
+ return;
3559
+ }
3560
+ let call = this.getObservedCall(sample.callId);
3561
+ if (!call) {
3562
+ call = this.createObservedCall({
3563
+ callId: sample.callId,
3564
+ appData: this.config.createCallAppData?.({ callId: sample.callId, observer: this })
3565
+ });
3566
+ }
3567
+ if (!call) return;
3568
+ let client = call.getObservedClient(sample.clientId);
3569
+ if (!client) {
3570
+ client = call.createObservedClient({
3571
+ clientId: sample.clientId,
3572
+ appData: this.config.createClientAppData?.({ clientId: sample.clientId, observedCall: call })
3573
+ });
3574
+ }
3575
+ if (!client) return;
3576
+ client.accept(sample, context);
3577
+ }
3578
+ update() {
3579
+ if (this.closed) {
3580
+ return;
3581
+ }
3582
+ this.numberOfInboundRtpStreams = 0;
3583
+ this.numberOfOutboundRtpStreams = 0;
3584
+ this.numberOfPeerConnections = 0;
3585
+ this.numberOfDataChannels = 0;
3586
+ this.numberOfClients = 0;
3587
+ this.numberOfClientsUsingTurn = 0;
3588
+ for (const call of this.observedCalls.values()) {
3589
+ this.numberOfInboundRtpStreams += call.numberOfInboundRtpStreams;
3590
+ this.numberOfOutboundRtpStreams += call.numberOfOutboundRtpStreams;
3591
+ this.numberOfPeerConnections += call.numberOfPeerConnections;
3592
+ this.numberOfDataChannels += call.numberOfDataChannels;
3593
+ this.numberOfClients += call.numberOfClients;
3594
+ this.numberOfClientsUsingTurn += call.clientsUsedTurn.size;
3595
+ }
3596
+ this.observedTURN.update();
3597
+ this._notify("observer-updated", { ...this.eventScope });
3598
+ }
3599
+ /** Emit an Observer-bus event. */
3600
+ _notify(type, ...args) {
3601
+ this.emit(type, ...args);
3602
+ }
3603
+ };
3604
+
3605
+ // src/sinks/ClientSampleSink.ts
3606
+ import { EventEmitter as EventEmitter6 } from "events";
3607
+ var ClientSampleSink = class extends EventEmitter6 {
3608
+ };
3609
+
3610
+ // src/sinks/JsonlFileSink.ts
3611
+ import * as fs from "fs";
3612
+ import * as path from "path";
3613
+ var JsonlFileSink = class extends ClientSampleSink {
3614
+ /** The file this sink writes to. Read it (e.g. in a `close` handler) to upload/move the file. */
3615
+ path;
3616
+ _stream;
3617
+ _serializeSample;
3618
+ constructor(options) {
3619
+ super();
3620
+ this.path = options.path;
3621
+ this._stream = fs.createWriteStream(this.path, { flags: "a" });
3622
+ this._stream.on("close", () => this.emit("close"));
3623
+ this._stream.on("finish", () => this.emit("finish"));
3624
+ this._stream.on("drain", () => this.emit("drain"));
3625
+ this._stream.on("error", (err) => this.emit("error", err));
3626
+ this._serializeSample = options.serializeSample ?? ((sample) => JSON.stringify(sample));
3627
+ }
3628
+ write(sample) {
3629
+ return this._stream.write(`${this._serializeSample(sample)}
3630
+ `);
3631
+ }
3632
+ end() {
3633
+ this._stream.end();
3634
+ }
3635
+ };
3636
+ function createJsonlFileSink(options) {
3637
+ return new JsonlFileSink(options);
3638
+ }
3639
+ function createJsonlFileSinkFactory(options) {
3640
+ return ({ clientId, observedCall }) => {
3641
+ const filename = options.getFileName?.({ callId: observedCall.callId, clientId }) ?? `${observedCall.callId}__${clientId}.jsonl`;
3642
+ return createJsonlFileSink({
3643
+ path: path.join(options.directory, filename),
3644
+ serializeSample: options.serializeSample
3645
+ });
3646
+ };
3647
+ }
3648
+
3649
+ // src/sinks/InMemorySink.ts
3650
+ var InMemorySink = class extends ClientSampleSink {
3651
+ constructor(samples = []) {
3652
+ super();
3653
+ this.samples = samples;
3654
+ }
3655
+ samples;
3656
+ write(sample) {
3657
+ this.samples.push(sample);
3658
+ return true;
3659
+ }
3660
+ end() {
3661
+ this.emit("close");
3662
+ }
3663
+ };
3664
+ function createInMemorySink(samples) {
3665
+ return new InMemorySink(samples);
3666
+ }
3667
+ export {
3668
+ ClientEventTypes,
3669
+ ClientMetaTypes,
3670
+ ClientSampleSink,
3671
+ Detectors,
3672
+ InMemorySink,
3673
+ JsonlFileSink,
3674
+ ObservedCall,
3675
+ ObservedCertificate,
3676
+ ObservedClient,
3677
+ ObservedCodec,
3678
+ ObservedDataChannel,
3679
+ ObservedIceCandidate,
3680
+ ObservedIceCandidatePair,
3681
+ ObservedIceTransport,
3682
+ ObservedInboundRtp,
3683
+ ObservedInboundTrack,
3684
+ ObservedMediaPlayout,
3685
+ ObservedMediaSource,
3686
+ ObservedOutboundRtp,
3687
+ ObservedOutboundTrack,
3688
+ ObservedPeerConnection,
3689
+ ObservedPeerConnectionTransport,
3690
+ ObservedRemoteInboundRtp,
3691
+ ObservedRemoteOutboundRtp,
3692
+ Observer,
3693
+ createInMemorySink,
3694
+ createJsonlFileSink,
3695
+ createJsonlFileSinkFactory,
3696
+ createLogger,
3697
+ setObserverLogger
3698
+ };
3699
+ //# sourceMappingURL=index.mjs.map