@observertc/observer-js 1.0.0-b276103.0 → 1.0.0-beta.10

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 (231) hide show
  1. package/README.md +972 -383
  2. package/dist/index.d.mts +2952 -0
  3. package/dist/index.d.ts +2952 -0
  4. package/dist/index.js +4040 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +3970 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/llms-full.txt +1536 -0
  9. package/package.json +32 -16
  10. package/.eslintrc.json +0 -143
  11. package/.prettierignore +0 -5
  12. package/.prettierrc +0 -7
  13. package/jest.config.js +0 -9
  14. package/lib/ObservedCall.d.ts +0 -70
  15. package/lib/ObservedCall.d.ts.map +0 -1
  16. package/lib/ObservedCall.js +0 -196
  17. package/lib/ObservedCallEventMonitor.d.ts +0 -107
  18. package/lib/ObservedCallEventMonitor.d.ts.map +0 -1
  19. package/lib/ObservedCallEventMonitor.js +0 -321
  20. package/lib/ObservedCallSummary.d.ts +0 -18
  21. package/lib/ObservedCallSummary.d.ts.map +0 -1
  22. package/lib/ObservedCallSummary.js +0 -2
  23. package/lib/ObservedCertificate.d.ts +0 -19
  24. package/lib/ObservedCertificate.d.ts.map +0 -1
  25. package/lib/ObservedCertificate.js +0 -38
  26. package/lib/ObservedClient.d.ts +0 -115
  27. package/lib/ObservedClient.d.ts.map +0 -1
  28. package/lib/ObservedClient.js +0 -714
  29. package/lib/ObservedClientEventMonitor.d.ts +0 -91
  30. package/lib/ObservedClientEventMonitor.d.ts.map +0 -1
  31. package/lib/ObservedClientEventMonitor.js +0 -254
  32. package/lib/ObservedClientSummary.d.ts +0 -23
  33. package/lib/ObservedClientSummary.d.ts.map +0 -1
  34. package/lib/ObservedClientSummary.js +0 -2
  35. package/lib/ObservedCodec.d.ts +0 -22
  36. package/lib/ObservedCodec.d.ts.map +0 -1
  37. package/lib/ObservedCodec.js +0 -45
  38. package/lib/ObservedDataChannel.d.ts +0 -30
  39. package/lib/ObservedDataChannel.d.ts.map +0 -1
  40. package/lib/ObservedDataChannel.js +0 -75
  41. package/lib/ObservedIceCandidate.d.ts +0 -29
  42. package/lib/ObservedIceCandidate.d.ts.map +0 -1
  43. package/lib/ObservedIceCandidate.js +0 -59
  44. package/lib/ObservedIceCandidatePair.d.ts +0 -45
  45. package/lib/ObservedIceCandidatePair.d.ts.map +0 -1
  46. package/lib/ObservedIceCandidatePair.js +0 -127
  47. package/lib/ObservedIceTransport.d.ts +0 -36
  48. package/lib/ObservedIceTransport.d.ts.map +0 -1
  49. package/lib/ObservedIceTransport.js +0 -93
  50. package/lib/ObservedInboundRtp.d.ts +0 -92
  51. package/lib/ObservedInboundRtp.d.ts.map +0 -1
  52. package/lib/ObservedInboundRtp.js +0 -193
  53. package/lib/ObservedInboundTrack.d.ts +0 -34
  54. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  55. package/lib/ObservedInboundTrack.js +0 -92
  56. package/lib/ObservedMediaPlayout.d.ts +0 -22
  57. package/lib/ObservedMediaPlayout.d.ts.map +0 -1
  58. package/lib/ObservedMediaPlayout.js +0 -42
  59. package/lib/ObservedMediaSource.d.ts +0 -28
  60. package/lib/ObservedMediaSource.d.ts.map +0 -1
  61. package/lib/ObservedMediaSource.js +0 -55
  62. package/lib/ObservedOutboundRtp.d.ts +0 -62
  63. package/lib/ObservedOutboundRtp.d.ts.map +0 -1
  64. package/lib/ObservedOutboundRtp.js +0 -144
  65. package/lib/ObservedOutboundTrack.d.ts +0 -34
  66. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
  67. package/lib/ObservedOutboundTrack.js +0 -65
  68. package/lib/ObservedPeerConnection.d.ts +0 -207
  69. package/lib/ObservedPeerConnection.d.ts.map +0 -1
  70. package/lib/ObservedPeerConnection.js +0 -788
  71. package/lib/ObservedPeerConnectionTransport.d.ts +0 -17
  72. package/lib/ObservedPeerConnectionTransport.d.ts.map +0 -1
  73. package/lib/ObservedPeerConnectionTransport.js +0 -34
  74. package/lib/ObservedRemoteInboundRtp.d.ts +0 -30
  75. package/lib/ObservedRemoteInboundRtp.d.ts.map +0 -1
  76. package/lib/ObservedRemoteInboundRtp.js +0 -60
  77. package/lib/ObservedRemoteOutboundRtp.d.ts +0 -30
  78. package/lib/ObservedRemoteOutboundRtp.d.ts.map +0 -1
  79. package/lib/ObservedRemoteOutboundRtp.js +0 -60
  80. package/lib/ObservedTURN.d.ts +0 -31
  81. package/lib/ObservedTURN.d.ts.map +0 -1
  82. package/lib/ObservedTURN.js +0 -58
  83. package/lib/ObservedTurnServer.d.ts +0 -25
  84. package/lib/ObservedTurnServer.d.ts.map +0 -1
  85. package/lib/ObservedTurnServer.js +0 -59
  86. package/lib/Observer.d.ts +0 -62
  87. package/lib/Observer.d.ts.map +0 -1
  88. package/lib/Observer.js +0 -160
  89. package/lib/ObserverEventMonitor.d.ts +0 -141
  90. package/lib/ObserverEventMonitor.d.ts.map +0 -1
  91. package/lib/ObserverEventMonitor.js +0 -440
  92. package/lib/ObserverSummary.d.ts +0 -13
  93. package/lib/ObserverSummary.d.ts.map +0 -1
  94. package/lib/ObserverSummary.js +0 -2
  95. package/lib/Reports.d.ts +0 -85
  96. package/lib/Reports.d.ts.map +0 -1
  97. package/lib/Reports.js +0 -2
  98. package/lib/common/Middleware.d.ts +0 -17
  99. package/lib/common/Middleware.d.ts.map +0 -1
  100. package/lib/common/Middleware.js +0 -60
  101. package/lib/common/SingleExecutor.d.ts +0 -3
  102. package/lib/common/SingleExecutor.d.ts.map +0 -1
  103. package/lib/common/SingleExecutor.js +0 -31
  104. package/lib/common/logger.d.ts +0 -17
  105. package/lib/common/logger.d.ts.map +0 -1
  106. package/lib/common/logger.js +0 -50
  107. package/lib/common/types.d.ts +0 -3
  108. package/lib/common/types.d.ts.map +0 -1
  109. package/lib/common/types.js +0 -3
  110. package/lib/common/utils.d.ts +0 -11
  111. package/lib/common/utils.d.ts.map +0 -1
  112. package/lib/common/utils.js +0 -63
  113. package/lib/detectors/Detector.d.ts +0 -5
  114. package/lib/detectors/Detector.d.ts.map +0 -1
  115. package/lib/detectors/Detector.js +0 -3
  116. package/lib/detectors/Detectors.d.ts +0 -11
  117. package/lib/detectors/Detectors.d.ts.map +0 -1
  118. package/lib/detectors/Detectors.js +0 -34
  119. package/lib/index.d.ts +0 -29
  120. package/lib/index.d.ts.map +0 -1
  121. package/lib/index.js +0 -49
  122. package/lib/mediasoup/ObservedMediaRouter.d.ts +0 -10
  123. package/lib/mediasoup/ObservedMediaRouter.d.ts.map +0 -1
  124. package/lib/mediasoup/ObservedMediaRouter.js +0 -6
  125. package/lib/monitors/TurnUsageMonitor.d.ts +0 -1
  126. package/lib/monitors/TurnUsageMonitor.d.ts.map +0 -1
  127. package/lib/monitors/TurnUsageMonitor.js +0 -146
  128. package/lib/schema/ClientEventTypes.d.ts +0 -228
  129. package/lib/schema/ClientEventTypes.d.ts.map +0 -1
  130. package/lib/schema/ClientEventTypes.js +0 -41
  131. package/lib/schema/ClientMetaTypes.d.ts +0 -34
  132. package/lib/schema/ClientMetaTypes.d.ts.map +0 -1
  133. package/lib/schema/ClientMetaTypes.js +0 -16
  134. package/lib/schema/ClientSample.d.ts +0 -1333
  135. package/lib/schema/ClientSample.d.ts.map +0 -1
  136. package/lib/schema/ClientSample.js +0 -4
  137. package/lib/scores/CalculatedScore.d.ts +0 -6
  138. package/lib/scores/CalculatedScore.d.ts.map +0 -1
  139. package/lib/scores/CalculatedScore.js +0 -2
  140. package/lib/scores/DefaultCallScoreCalculator.d.ts +0 -7
  141. package/lib/scores/DefaultCallScoreCalculator.d.ts.map +0 -1
  142. package/lib/scores/DefaultCallScoreCalculator.js +0 -21
  143. package/lib/scores/ScoreCalculator.d.ts +0 -4
  144. package/lib/scores/ScoreCalculator.d.ts.map +0 -1
  145. package/lib/scores/ScoreCalculator.js +0 -2
  146. package/lib/updaters/CallUpdater.d.ts +0 -5
  147. package/lib/updaters/CallUpdater.d.ts.map +0 -1
  148. package/lib/updaters/CallUpdater.js +0 -2
  149. package/lib/updaters/ObserverUpdater.d.ts +0 -5
  150. package/lib/updaters/ObserverUpdater.d.ts.map +0 -1
  151. package/lib/updaters/ObserverUpdater.js +0 -2
  152. package/lib/updaters/OnAllCallObserverUpdater.d.ts +0 -15
  153. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +0 -1
  154. package/lib/updaters/OnAllCallObserverUpdater.js +0 -50
  155. package/lib/updaters/OnAllClientCallUpdater.d.ts +0 -15
  156. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +0 -1
  157. package/lib/updaters/OnAllClientCallUpdater.js +0 -53
  158. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +0 -12
  159. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +0 -1
  160. package/lib/updaters/OnAnyCallObserverUpdater.js +0 -37
  161. package/lib/updaters/OnAnyClientCallUpdater.d.ts +0 -12
  162. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +0 -1
  163. package/lib/updaters/OnAnyClientCallUpdater.js +0 -36
  164. package/lib/updaters/OnIntervalUpdater.d.ts +0 -10
  165. package/lib/updaters/OnIntervalUpdater.d.ts.map +0 -1
  166. package/lib/updaters/OnIntervalUpdater.js +0 -17
  167. package/lib/updaters/Updater.d.ts +0 -6
  168. package/lib/updaters/Updater.d.ts.map +0 -1
  169. package/lib/updaters/Updater.js +0 -2
  170. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +0 -25
  171. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +0 -1
  172. package/lib/utils/MediasoupRemoteTrackResolver.js +0 -110
  173. package/lib/utils/RemoteTrackResolver.d.ts +0 -7
  174. package/lib/utils/RemoteTrackResolver.d.ts.map +0 -1
  175. package/lib/utils/RemoteTrackResolver.js +0 -2
  176. package/src/ObservedCall.ts +0 -308
  177. package/src/ObservedCallEventMonitor.ts +0 -402
  178. package/src/ObservedCallSummary.ts +0 -22
  179. package/src/ObservedCertificate.ts +0 -43
  180. package/src/ObservedClient.ts +0 -883
  181. package/src/ObservedClientEventMonitor.ts +0 -309
  182. package/src/ObservedClientSummary.ts +0 -24
  183. package/src/ObservedCodec.ts +0 -49
  184. package/src/ObservedDataChannel.ts +0 -85
  185. package/src/ObservedIceCandidate.ts +0 -64
  186. package/src/ObservedIceCandidatePair.ts +0 -134
  187. package/src/ObservedIceTransport.ts +0 -99
  188. package/src/ObservedInboundRtp.ts +0 -205
  189. package/src/ObservedInboundTrack.ts +0 -105
  190. package/src/ObservedMediaPlayout.ts +0 -49
  191. package/src/ObservedMediaSource.ts +0 -60
  192. package/src/ObservedOutboundRtp.ts +0 -156
  193. package/src/ObservedOutboundTrack.ts +0 -82
  194. package/src/ObservedPeerConnection.ts +0 -1104
  195. package/src/ObservedPeerConnectionTransport.ts +0 -38
  196. package/src/ObservedRemoteInboundRtp.ts +0 -64
  197. package/src/ObservedRemoteOutboundRtp.ts +0 -65
  198. package/src/ObservedTURN.ts +0 -86
  199. package/src/ObservedTurnServer.ts +0 -72
  200. package/src/Observer.ts +0 -248
  201. package/src/ObserverEventMonitor.ts +0 -561
  202. package/src/ObserverSummary.ts +0 -15
  203. package/src/Reports.ts +0 -87
  204. package/src/common/Middleware.ts +0 -84
  205. package/src/common/SingleExecutor.ts +0 -36
  206. package/src/common/logger.ts +0 -82
  207. package/src/common/types.ts +0 -4
  208. package/src/common/utils.ts +0 -69
  209. package/src/detectors/Detector.ts +0 -6
  210. package/src/detectors/Detectors.ts +0 -38
  211. package/src/index.ts +0 -32
  212. package/src/mediasoup/ObservedMediaRouter.ts +0 -10
  213. package/src/monitors/TurnUsageMonitor.ts +0 -187
  214. package/src/schema/ClientEventTypes.ts +0 -280
  215. package/src/schema/ClientMetaTypes.ts +0 -41
  216. package/src/schema/ClientSample.ts +0 -1682
  217. package/src/scores/CalculatedScore.ts +0 -6
  218. package/src/scores/DefaultCallScoreCalculator.ts +0 -23
  219. package/src/scores/ScoreCalculator.ts +0 -3
  220. package/src/updaters/CallUpdater.ts +0 -5
  221. package/src/updaters/ObserverUpdater.ts +0 -5
  222. package/src/updaters/OnAllCallObserverUpdater.ts +0 -59
  223. package/src/updaters/OnAllClientCallUpdater.ts +0 -61
  224. package/src/updaters/OnAnyCallObserverUpdater.ts +0 -41
  225. package/src/updaters/OnAnyClientCallUpdater.ts +0 -39
  226. package/src/updaters/OnIntervalUpdater.ts +0 -19
  227. package/src/updaters/Updater.ts +0 -5
  228. package/src/utils/MediasoupRemoteTrackResolver.ts +0 -155
  229. package/src/utils/RemoteTrackResolver.ts +0 -12
  230. package/tsconfig.json +0 -19
  231. package/tslint.json +0 -6
package/dist/index.mjs ADDED
@@ -0,0 +1,3970 @@
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(logger8) {
41
+ mainLogger = logger8;
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
+ }
1067
+ timestamp;
1068
+ id;
1069
+ kind;
1070
+ _peerConnection;
1071
+ _inboundRtp;
1072
+ _mediaPlayout;
1073
+ calculatedScore = {
1074
+ weight: 1,
1075
+ value: void 0
1076
+ };
1077
+ appData;
1078
+ remoteOutboundTrack;
1079
+ _visited = false;
1080
+ addedAt;
1081
+ removedAt;
1082
+ muted;
1083
+ attachments;
1084
+ get score() {
1085
+ return this.calculatedScore.value;
1086
+ }
1087
+ get visited() {
1088
+ const visited = this._visited;
1089
+ this._visited = false;
1090
+ return visited;
1091
+ }
1092
+ getPeerConnection() {
1093
+ return this._peerConnection;
1094
+ }
1095
+ getInboundRtp() {
1096
+ return this._inboundRtp;
1097
+ }
1098
+ getMediaPlayout() {
1099
+ return this._mediaPlayout;
1100
+ }
1101
+ update(stats) {
1102
+ this._visited = true;
1103
+ this.timestamp = stats.timestamp;
1104
+ this.calculatedScore.value = stats.score;
1105
+ this.attachments = stats.attachments;
1106
+ }
1107
+ };
1108
+
1109
+ // src/ObservedOutboundTrack.ts
1110
+ var ObservedOutboundTrack = class {
1111
+ constructor(timestamp, id, kind, _peerConnection, _outboundRtps, _mediaSource) {
1112
+ this.timestamp = timestamp;
1113
+ this.id = id;
1114
+ this.kind = kind;
1115
+ this._peerConnection = _peerConnection;
1116
+ this._outboundRtps = _outboundRtps;
1117
+ this._mediaSource = _mediaSource;
1118
+ }
1119
+ timestamp;
1120
+ id;
1121
+ kind;
1122
+ _peerConnection;
1123
+ _outboundRtps;
1124
+ _mediaSource;
1125
+ _visited = false;
1126
+ appData;
1127
+ remoteInboundTracks = /* @__PURE__ */ new Set();
1128
+ calculatedScore = {
1129
+ weight: 1,
1130
+ value: void 0
1131
+ };
1132
+ addedAt;
1133
+ removedAt;
1134
+ muted;
1135
+ attachments;
1136
+ get score() {
1137
+ return this.calculatedScore.value;
1138
+ }
1139
+ get visited() {
1140
+ const visited = this._visited;
1141
+ this._visited = false;
1142
+ return visited;
1143
+ }
1144
+ getPeerConnection() {
1145
+ return this._peerConnection;
1146
+ }
1147
+ getOutboundRtps() {
1148
+ return this._outboundRtps;
1149
+ }
1150
+ getMediaSource() {
1151
+ return this._mediaSource;
1152
+ }
1153
+ update(stats) {
1154
+ this._visited = true;
1155
+ this.timestamp = stats.timestamp;
1156
+ this.calculatedScore.value = stats.score;
1157
+ this.attachments = stats.attachments;
1158
+ }
1159
+ };
1160
+
1161
+ // src/common/utils.ts
1162
+ function getMedian(arr, copyArrayForSorting = true) {
1163
+ const sortedArr = copyArrayForSorting ? arr.slice().sort((a, b) => a - b) : arr.sort((a, b) => a - b);
1164
+ const mid = Math.floor(sortedArr.length / 2);
1165
+ if (sortedArr.length % 2 !== 0) {
1166
+ return sortedArr[mid];
1167
+ }
1168
+ return (sortedArr[mid - 1] + sortedArr[mid]) / 2;
1169
+ }
1170
+ function parseJsonAs(json) {
1171
+ if (!json) {
1172
+ return void 0;
1173
+ }
1174
+ try {
1175
+ return JSON.parse(json);
1176
+ } catch (error) {
1177
+ return void 0;
1178
+ }
1179
+ }
1180
+
1181
+ // src/ObservedPeerConnection.ts
1182
+ var logger = createLogger("ObservedPeerConnection");
1183
+ var ObservedPeerConnection = class extends EventEmitter {
1184
+ constructor(peerConnectionId, client) {
1185
+ super();
1186
+ this.peerConnectionId = peerConnectionId;
1187
+ this.client = client;
1188
+ this.setMaxListeners(Infinity);
1189
+ this.eventScope = {
1190
+ observer: client.call.observer,
1191
+ observedCall: client.call,
1192
+ observedClient: client,
1193
+ observedPeerConnection: this
1194
+ };
1195
+ }
1196
+ peerConnectionId;
1197
+ client;
1198
+ _visited = true;
1199
+ appData;
1200
+ calculatedScore = {
1201
+ weight: 1,
1202
+ value: void 0
1203
+ };
1204
+ closed = false;
1205
+ // timestamp of the PEER_CONNECTION_OPENED event
1206
+ openedAt;
1207
+ // timestamp of the PEER_CONNECTION_CLOSED event
1208
+ closedAt;
1209
+ updated = Date.now();
1210
+ connectionState;
1211
+ iceConnectionState;
1212
+ iceGatheringState;
1213
+ availableIncomingBitrate = 0;
1214
+ availableOutgoingBitrate = 0;
1215
+ totalInboundPacketsLost = 0;
1216
+ totalInboundPacketsReceived = 0;
1217
+ totalOutboundPacketsSent = 0;
1218
+ totalDataChannelBytesSent = 0;
1219
+ totalDataChannelBytesReceived = 0;
1220
+ totalDataChannelMessagesSent = 0;
1221
+ totalDataChannelMessagesReceived = 0;
1222
+ totalSentAudioBytes = 0;
1223
+ totalSentVideoBytes = 0;
1224
+ totalSentAudioPackets = 0;
1225
+ totalSentVideoPackets = 0;
1226
+ totalReceivedAudioPacktes = 0;
1227
+ totalReceivedVideoPackets = 0;
1228
+ totalReceivedAudioBytes = 0;
1229
+ totalReceivedVideoBytes = 0;
1230
+ deltaInboundPacketsLost = 0;
1231
+ deltaInboundPacketsReceived = 0;
1232
+ deltaOutboundPacketsSent = 0;
1233
+ deltaDataChannelBytesSent = 0;
1234
+ deltaDataChannelBytesReceived = 0;
1235
+ deltaDataChannelMessagesSent = 0;
1236
+ deltaDataChannelMessagesReceived = 0;
1237
+ deltaInboundReceivedBytes = 0;
1238
+ deltaOutboundSentBytes = 0;
1239
+ deltaReceivedAudioBytes = 0;
1240
+ deltaReceivedVideoBytes = 0;
1241
+ deltaReceivedAudioPackets = 0;
1242
+ deltaReceivedVideoPackets = 0;
1243
+ deltaSentAudioBytes = 0;
1244
+ deltaSentVideoBytes = 0;
1245
+ deltaSentAudioPackets = 0;
1246
+ deltaSentVideoPackets = 0;
1247
+ deltaTransportSentBytes = 0;
1248
+ deltaTransportReceivedBytes = 0;
1249
+ receivingPacketsPerSecond = 0;
1250
+ sendingPacketsPerSecond = 0;
1251
+ sendingAudioBitrate = 0;
1252
+ sendingVideoBitrate = 0;
1253
+ receivingAudioBitrate = 0;
1254
+ receivingVideoBitrate = 0;
1255
+ currentRttInMs;
1256
+ currentJitter;
1257
+ usingTCP = false;
1258
+ usingTURN = false;
1259
+ observedTurnServer;
1260
+ observedCertificates = /* @__PURE__ */ new Map();
1261
+ observedCodecs = /* @__PURE__ */ new Map();
1262
+ observedDataChannels = /* @__PURE__ */ new Map();
1263
+ observedIceCandidates = /* @__PURE__ */ new Map();
1264
+ observedIceCandidatesPair = /* @__PURE__ */ new Map();
1265
+ observedIceTransports = /* @__PURE__ */ new Map();
1266
+ observedInboundRtps = /* @__PURE__ */ new Map();
1267
+ observedInboundTracks = /* @__PURE__ */ new Map();
1268
+ observedMediaPlayouts = /* @__PURE__ */ new Map();
1269
+ observedMediaSources = /* @__PURE__ */ new Map();
1270
+ observedOutboundRtps = /* @__PURE__ */ new Map();
1271
+ observedOutboundTracks = /* @__PURE__ */ new Map();
1272
+ observedPeerConnectionTransports = /* @__PURE__ */ new Map();
1273
+ observedRemoteInboundRtps = /* @__PURE__ */ new Map();
1274
+ observedRemoteOutboundRtps = /* @__PURE__ */ new Map();
1275
+ eventScope;
1276
+ get score() {
1277
+ return this.calculatedScore.value;
1278
+ }
1279
+ get visited() {
1280
+ const visited = this._visited;
1281
+ this._visited = false;
1282
+ return visited;
1283
+ }
1284
+ get codecs() {
1285
+ return [...this.observedCodecs.values()];
1286
+ }
1287
+ get inboundRtps() {
1288
+ return [...this.observedInboundRtps.values()];
1289
+ }
1290
+ get remoteOutboundRtps() {
1291
+ return [...this.observedRemoteOutboundRtps.values()];
1292
+ }
1293
+ get outboundRtps() {
1294
+ return [...this.observedOutboundRtps.values()];
1295
+ }
1296
+ get remoteInboundRtps() {
1297
+ return [...this.observedRemoteInboundRtps.values()];
1298
+ }
1299
+ get mediaSources() {
1300
+ return [...this.observedMediaSources.values()];
1301
+ }
1302
+ get mediaPlayouts() {
1303
+ return [...this.observedMediaPlayouts.values()];
1304
+ }
1305
+ get dataChannels() {
1306
+ return [...this.observedDataChannels.values()];
1307
+ }
1308
+ get peerConnectionTransports() {
1309
+ return [...this.observedPeerConnectionTransports.values()];
1310
+ }
1311
+ get iceTransports() {
1312
+ return [...this.observedIceTransports.values()];
1313
+ }
1314
+ get iceCandidates() {
1315
+ return [...this.observedIceCandidates.values()];
1316
+ }
1317
+ get iceCandidatePairs() {
1318
+ return [...this.observedIceCandidatesPair.values()];
1319
+ }
1320
+ get certificates() {
1321
+ return [...this.observedCertificates.values()];
1322
+ }
1323
+ get selectedIceCandidatePairs() {
1324
+ return this.iceTransports.map((iceTransport) => iceTransport.getSelectedCandidatePair()).filter((pair) => pair !== void 0);
1325
+ }
1326
+ get selectedIceCandiadtePairForTurn() {
1327
+ return this.selectedIceCandidatePairs.filter(
1328
+ (pair) => pair.getLocalCandidate()?.candidateType === "relay" && pair.getRemoteCandidate()?.url?.startsWith("turn:")
1329
+ );
1330
+ }
1331
+ close() {
1332
+ if (this.closed) return;
1333
+ this.closed = true;
1334
+ this.observedCertificates.forEach((observedCertificate) => this._notify("certificate-removed", { ...this.eventScope, observedCertificate }));
1335
+ this.observedCodecs.forEach((observedCodec) => this._notify("codec-removed", { ...this.eventScope, observedCodec }));
1336
+ this.observedDataChannels.forEach((observedDataChannel) => this._notify("data-channel-removed", { ...this.eventScope, observedDataChannel }));
1337
+ this.observedIceCandidates.forEach((observedIceCandidate) => this._notify("ice-candidate-removed", { ...this.eventScope, observedIceCandidate }));
1338
+ this.observedIceCandidatesPair.forEach((pair) => this._notify("ice-candidate-pair-removed", { ...this.eventScope, observedIceCandidatePair: pair }));
1339
+ this.observedIceTransports.forEach((transport) => this._notify("ice-transport-removed", { ...this.eventScope, observedIceTransport: transport }));
1340
+ this.observedInboundRtps.forEach((rtp) => this._notify("inbound-rtp-removed", { ...this.eventScope, observedInboundRtp: rtp }));
1341
+ this.observedInboundTracks.forEach((track) => {
1342
+ this.emit("removed-inbound-track", track);
1343
+ this._notify("inbound-track-removed", { ...this.eventScope, observedInboundTrack: track });
1344
+ });
1345
+ this.observedMediaPlayouts.forEach((playout) => this._notify("media-playout-removed", { ...this.eventScope, observedMediaPlayout: playout }));
1346
+ this.observedMediaSources.forEach((source) => this._notify("media-source-removed", { ...this.eventScope, observedMediaSource: source }));
1347
+ this.observedOutboundRtps.forEach((rtp) => this._notify("outbound-rtp-removed", { ...this.eventScope, observedOutboundRtp: rtp }));
1348
+ this.observedOutboundTracks.forEach((track) => {
1349
+ this.emit("removed-outbound-track", track);
1350
+ this._notify("outbound-track-removed", { ...this.eventScope, observedOutboundTrack: track });
1351
+ });
1352
+ this.observedPeerConnectionTransports.forEach((transport) => this._notify("peer-connection-transport-removed", { ...this.eventScope, observedPeerConnectionTransport: transport }));
1353
+ this.observedRemoteInboundRtps.forEach((rtp) => this._notify("remote-inbound-rtp-removed", { ...this.eventScope, observedRemoteInboundRtp: rtp }));
1354
+ this.observedRemoteOutboundRtps.forEach((rtp) => this._notify("remote-outbound-rtp-removed", { ...this.eventScope, observedRemoteOutboundRtp: rtp }));
1355
+ this.observedCertificates.clear();
1356
+ this.observedCodecs.clear();
1357
+ this.observedDataChannels.clear();
1358
+ this.observedIceCandidates.clear();
1359
+ this.observedIceCandidatesPair.clear();
1360
+ this.observedIceTransports.clear();
1361
+ this.observedInboundRtps.clear();
1362
+ this.observedInboundTracks.clear();
1363
+ this.observedMediaPlayouts.clear();
1364
+ this.observedMediaSources.clear();
1365
+ this.observedOutboundRtps.clear();
1366
+ this.observedOutboundTracks.clear();
1367
+ this.observedPeerConnectionTransports.clear();
1368
+ this.observedRemoteInboundRtps.clear();
1369
+ this.observedRemoteOutboundRtps.clear();
1370
+ this.client.call.observer.observedTURN.removePeerConnection(this);
1371
+ if (!this.closedAt) this.closedAt = Date.now();
1372
+ this.emit("close");
1373
+ this._notify("peer-connection-closed", { ...this.eventScope });
1374
+ }
1375
+ accept(sample, context) {
1376
+ if (this.closed) return;
1377
+ this._visited = true;
1378
+ if (context) this.appData = { ...this.appData, ...context };
1379
+ this.availableIncomingBitrate = 0;
1380
+ this.availableOutgoingBitrate = 0;
1381
+ this.deltaInboundPacketsLost = 0;
1382
+ this.deltaInboundPacketsReceived = 0;
1383
+ this.deltaOutboundPacketsSent = 0;
1384
+ this.deltaDataChannelBytesSent = 0;
1385
+ this.deltaDataChannelBytesReceived = 0;
1386
+ this.deltaInboundReceivedBytes = 0;
1387
+ this.deltaOutboundSentBytes = 0;
1388
+ this.deltaReceivedAudioBytes = 0;
1389
+ this.deltaReceivedVideoBytes = 0;
1390
+ this.deltaReceivedAudioPackets = 0;
1391
+ this.deltaReceivedVideoPackets = 0;
1392
+ this.deltaSentAudioBytes = 0;
1393
+ this.deltaSentVideoBytes = 0;
1394
+ this.deltaSentAudioPackets = 0;
1395
+ this.deltaSentVideoPackets = 0;
1396
+ this.deltaTransportReceivedBytes = 0;
1397
+ this.deltaTransportSentBytes = 0;
1398
+ this.sendingAudioBitrate = 0;
1399
+ this.sendingVideoBitrate = 0;
1400
+ this.receivingAudioBitrate = 0;
1401
+ this.receivingVideoBitrate = 0;
1402
+ const now = Date.now();
1403
+ const elapsedTimeInMs = now - this.updated;
1404
+ const elapsedTimeInSec = elapsedTimeInMs / 1e3;
1405
+ const rttMeasurementsInSec = [];
1406
+ const jitterMeasurements = [];
1407
+ if (sample.certificates) {
1408
+ for (const certificate of sample.certificates) {
1409
+ this._updateCertificateStats(certificate);
1410
+ }
1411
+ }
1412
+ if (sample.codecs) {
1413
+ for (const codec of sample.codecs) {
1414
+ this._updateCodecStats(codec);
1415
+ }
1416
+ }
1417
+ if (sample.dataChannels) {
1418
+ for (const dataChannel of sample.dataChannels) {
1419
+ const observedDataChannel = this._updateDataChannelStats(dataChannel);
1420
+ if (!observedDataChannel) continue;
1421
+ this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent;
1422
+ this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived;
1423
+ this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent;
1424
+ this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived;
1425
+ }
1426
+ }
1427
+ if (sample.iceCandidates) {
1428
+ for (const iceCandidate of sample.iceCandidates) {
1429
+ this._updateIceCandidateStats(iceCandidate);
1430
+ }
1431
+ }
1432
+ if (sample.iceCandidatePairs) {
1433
+ for (const iceCandidatePair of sample.iceCandidatePairs) {
1434
+ const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair);
1435
+ if (!observedCandidatePair) continue;
1436
+ if (observedCandidatePair.currentRoundTripTime) {
1437
+ rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime);
1438
+ }
1439
+ if (observedCandidatePair.availableIncomingBitrate) {
1440
+ this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate;
1441
+ }
1442
+ if (observedCandidatePair.availableOutgoingBitrate) {
1443
+ this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate;
1444
+ }
1445
+ }
1446
+ }
1447
+ if (sample.iceTransports) {
1448
+ for (const iceTransport of sample.iceTransports) {
1449
+ const observedIceTransport = this._updateIceTransportStats(iceTransport);
1450
+ if (!observedIceTransport) continue;
1451
+ }
1452
+ }
1453
+ if (sample.inboundRtps) {
1454
+ for (const inboundRtp of sample.inboundRtps) {
1455
+ const observedInboundRtp = this._updateInboundRtpStats(inboundRtp);
1456
+ if (!observedInboundRtp) continue;
1457
+ this.deltaInboundPacketsLost += observedInboundRtp.deltaLostPackets;
1458
+ this.deltaInboundPacketsReceived += observedInboundRtp.deltaReceivedPackets;
1459
+ this.deltaInboundReceivedBytes += observedInboundRtp.deltaBytesReceived;
1460
+ switch (inboundRtp.kind) {
1461
+ case "audio":
1462
+ this.deltaReceivedAudioBytes += observedInboundRtp.deltaBytesReceived;
1463
+ this.deltaReceivedAudioPackets += observedInboundRtp.deltaReceivedPackets;
1464
+ break;
1465
+ case "video":
1466
+ this.deltaReceivedVideoBytes += observedInboundRtp.deltaBytesReceived;
1467
+ this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
1468
+ break;
1469
+ }
1470
+ if (observedInboundRtp.jitter) {
1471
+ jitterMeasurements.push(observedInboundRtp.jitter);
1472
+ }
1473
+ }
1474
+ }
1475
+ if (sample.mediaPlayouts) {
1476
+ for (const mediaPlayout of sample.mediaPlayouts) {
1477
+ this._updateMediaPlayoutStats(mediaPlayout);
1478
+ }
1479
+ }
1480
+ if (sample.mediaSources) {
1481
+ for (const mediaSource of sample.mediaSources) {
1482
+ this._updateMediaSourceStats(mediaSource);
1483
+ }
1484
+ }
1485
+ if (sample.outboundRtps) {
1486
+ for (const outboundRtp of sample.outboundRtps) {
1487
+ const observedOutboundRtp = this._updateOutboundRtpStats(outboundRtp);
1488
+ if (!observedOutboundRtp) continue;
1489
+ this.deltaOutboundPacketsSent += observedOutboundRtp.deltaPacketsSent ?? 0;
1490
+ this.deltaOutboundSentBytes += observedOutboundRtp.deltaBytesSent ?? 0;
1491
+ switch (outboundRtp.kind) {
1492
+ case "audio":
1493
+ this.deltaSentAudioBytes += observedOutboundRtp.deltaBytesSent;
1494
+ this.deltaSentAudioPackets += observedOutboundRtp.deltaPacketsSent;
1495
+ break;
1496
+ case "video":
1497
+ this.deltaSentVideoBytes += observedOutboundRtp.deltaBytesSent;
1498
+ this.deltaSentVideoPackets += observedOutboundRtp.deltaPacketsSent;
1499
+ break;
1500
+ }
1501
+ }
1502
+ }
1503
+ if (sample.peerConnectionTransports) {
1504
+ for (const peerConnectionTransport of sample.peerConnectionTransports) {
1505
+ const observedTransport = this._updatePeerConnectionTransportStats(peerConnectionTransport);
1506
+ if (!observedTransport) continue;
1507
+ }
1508
+ }
1509
+ if (sample.remoteInboundRtps) {
1510
+ for (const remoteInboundRtp of sample.remoteInboundRtps) {
1511
+ const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp);
1512
+ if (!observedRemoteInboundRtp) continue;
1513
+ if (observedRemoteInboundRtp.roundTripTime) {
1514
+ rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime);
1515
+ }
1516
+ const observedOutboundRtp = observedRemoteInboundRtp.getOutboundRtp();
1517
+ if (observedOutboundRtp) {
1518
+ if (observedRemoteInboundRtp.roundTripTime !== void 0) {
1519
+ observedOutboundRtp.remoteRttInMs = observedRemoteInboundRtp.roundTripTime * 1e3;
1520
+ }
1521
+ if (observedRemoteInboundRtp.fractionLost !== void 0) {
1522
+ observedOutboundRtp.remoteFractionLost = observedRemoteInboundRtp.fractionLost;
1523
+ }
1524
+ if (observedRemoteInboundRtp.jitter !== void 0) {
1525
+ observedOutboundRtp.remoteJitter = observedRemoteInboundRtp.jitter;
1526
+ }
1527
+ if (observedRemoteInboundRtp.packetsLost !== void 0) {
1528
+ observedOutboundRtp.remotePacketsLost = observedRemoteInboundRtp.packetsLost;
1529
+ }
1530
+ }
1531
+ }
1532
+ }
1533
+ if (sample.remoteOutboundRtps) {
1534
+ for (const remoteOutboundRtp of sample.remoteOutboundRtps) {
1535
+ const observedRemoteOutboundRtp = this._updateRemoteOutboundRtpStats(remoteOutboundRtp);
1536
+ if (!observedRemoteOutboundRtp) continue;
1537
+ const observedInboundRtp = observedRemoteOutboundRtp.getInboundRtp();
1538
+ if (observedInboundRtp) {
1539
+ if (observedRemoteOutboundRtp.roundTripTime !== void 0) {
1540
+ observedInboundRtp.remoteRttInMs = observedRemoteOutboundRtp.roundTripTime * 1e3;
1541
+ }
1542
+ if (observedRemoteOutboundRtp.bytesSent !== void 0) {
1543
+ observedInboundRtp.remoteBytesSent = observedRemoteOutboundRtp.bytesSent;
1544
+ }
1545
+ if (observedRemoteOutboundRtp.packetsSent !== void 0) {
1546
+ observedInboundRtp.remotePacketsSent = observedRemoteOutboundRtp.packetsSent;
1547
+ }
1548
+ if (observedRemoteOutboundRtp.remoteTimestamp !== void 0) {
1549
+ observedInboundRtp.remoteTimestamp = observedRemoteOutboundRtp.remoteTimestamp;
1550
+ }
1551
+ }
1552
+ }
1553
+ }
1554
+ if (sample.inboundTracks) {
1555
+ for (const inboundTrack of sample.inboundTracks) {
1556
+ this._updateInboundTrackSample(inboundTrack);
1557
+ }
1558
+ }
1559
+ if (sample.outboundTracks) {
1560
+ for (const outboundTrack of sample.outboundTracks) {
1561
+ this._updateOutboundTrackSample(outboundTrack);
1562
+ }
1563
+ }
1564
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
1565
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
1566
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
1567
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
1568
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
1569
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
1570
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
1571
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
1572
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
1573
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
1574
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
1575
+ this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets;
1576
+ this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets;
1577
+ this.totalSentAudioPackets += this.deltaSentAudioPackets;
1578
+ this.totalSentVideoPackets += this.deltaSentVideoPackets;
1579
+ this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
1580
+ this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec;
1581
+ this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedTimeInSec;
1582
+ this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedTimeInSec;
1583
+ this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedTimeInSec;
1584
+ this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedTimeInSec;
1585
+ if (rttMeasurementsInSec.length > 0) {
1586
+ this.currentRttInMs = getMedian(rttMeasurementsInSec, false) * 1e3;
1587
+ } else {
1588
+ this.currentRttInMs = void 0;
1589
+ }
1590
+ if (jitterMeasurements.length > 0) {
1591
+ this.currentJitter = getMedian(jitterMeasurements, false);
1592
+ } else {
1593
+ this.currentJitter = void 0;
1594
+ }
1595
+ const wasUsingTURN = this.usingTURN;
1596
+ const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
1597
+ const selectedCandidatePairForTurn = [];
1598
+ this.usingTCP = false;
1599
+ this.usingTURN = false;
1600
+ for (const selectedCandidatePair of selectedIceCandidatePairs) {
1601
+ if (selectedCandidatePair.getLocalCandidate()?.protocol === "tcp") {
1602
+ this.usingTCP = true;
1603
+ }
1604
+ if (selectedCandidatePair.getLocalCandidate()?.candidateType === "relay" && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith("turn:")) {
1605
+ selectedCandidatePairForTurn.push(selectedCandidatePair);
1606
+ this.usingTURN = true;
1607
+ }
1608
+ this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived;
1609
+ this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent;
1610
+ }
1611
+ if (this.usingTURN) {
1612
+ if (!this.observedTurnServer) {
1613
+ this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this);
1614
+ }
1615
+ this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn);
1616
+ } else if (wasUsingTURN) {
1617
+ if (!this.usingTURN) {
1618
+ this.client.call.observer.observedTURN.removePeerConnection(this);
1619
+ }
1620
+ }
1621
+ this.calculatedScore.value = sample.score;
1622
+ this.updated = now;
1623
+ this._checkVisited();
1624
+ this._notify("peer-connection-updated", { ...this.eventScope, context });
1625
+ }
1626
+ _checkVisited() {
1627
+ for (const certificate of [...this.observedCertificates.values()]) {
1628
+ if (certificate.visited) continue;
1629
+ this.observedCertificates.delete(certificate.id);
1630
+ }
1631
+ for (const observedCodec of [...this.observedCodecs.values()]) {
1632
+ if (observedCodec.visited) continue;
1633
+ this.observedCodecs.delete(observedCodec.id);
1634
+ this._notify("codec-removed", { ...this.eventScope, observedCodec });
1635
+ }
1636
+ for (const observedDataChannel of [...this.observedDataChannels.values()]) {
1637
+ if (observedDataChannel.visited) continue;
1638
+ this.observedDataChannels.delete(observedDataChannel.id);
1639
+ this._notify("data-channel-removed", { ...this.eventScope, observedDataChannel });
1640
+ }
1641
+ for (const observedIceCandidate of [...this.observedIceCandidates.values()]) {
1642
+ if (observedIceCandidate.visited) continue;
1643
+ this.observedIceCandidates.delete(observedIceCandidate.id);
1644
+ this._notify("ice-candidate-removed", { ...this.eventScope, observedIceCandidate });
1645
+ }
1646
+ for (const observedIceCandidatePair of [...this.observedIceCandidatesPair.values()]) {
1647
+ if (observedIceCandidatePair.visited) continue;
1648
+ this.observedIceCandidatesPair.delete(observedIceCandidatePair.id);
1649
+ this._notify("ice-candidate-pair-removed", { ...this.eventScope, observedIceCandidatePair });
1650
+ }
1651
+ for (const observedIceTransport of [...this.observedIceTransports.values()]) {
1652
+ if (observedIceTransport.visited) continue;
1653
+ this.observedIceTransports.delete(observedIceTransport.id);
1654
+ this._notify("ice-transport-removed", { ...this.eventScope, observedIceTransport });
1655
+ }
1656
+ for (const observedInboundRtp of [...this.observedInboundRtps.values()]) {
1657
+ if (observedInboundRtp.visited) continue;
1658
+ this.observedInboundRtps.delete(observedInboundRtp.ssrc);
1659
+ this._notify("inbound-rtp-removed", { ...this.eventScope, observedInboundRtp });
1660
+ }
1661
+ for (const observedInboundTrack of [...this.observedInboundTracks.values()]) {
1662
+ if (observedInboundTrack.visited) continue;
1663
+ this.observedInboundTracks.delete(observedInboundTrack.id);
1664
+ this.emit("removed-inbound-track", observedInboundTrack);
1665
+ this._notify("inbound-track-removed", { ...this.eventScope, observedInboundTrack });
1666
+ }
1667
+ for (const observedMediaPlayout of [...this.observedMediaPlayouts.values()]) {
1668
+ if (observedMediaPlayout.visited) continue;
1669
+ this.observedMediaPlayouts.delete(observedMediaPlayout.id);
1670
+ this._notify("media-playout-removed", { ...this.eventScope, observedMediaPlayout });
1671
+ }
1672
+ for (const observedMediaSource of [...this.observedMediaSources.values()]) {
1673
+ if (observedMediaSource.visited) continue;
1674
+ this.observedMediaSources.delete(observedMediaSource.id);
1675
+ this._notify("media-source-removed", { ...this.eventScope, observedMediaSource });
1676
+ }
1677
+ for (const observedOutboundRtp of [...this.observedOutboundRtps.values()]) {
1678
+ if (observedOutboundRtp.visited) continue;
1679
+ this.observedOutboundRtps.delete(observedOutboundRtp.ssrc);
1680
+ this._notify("outbound-rtp-removed", { ...this.eventScope, observedOutboundRtp });
1681
+ }
1682
+ for (const observedOutboundTrack of [...this.observedOutboundTracks.values()]) {
1683
+ if (observedOutboundTrack.visited) continue;
1684
+ this.observedOutboundTracks.delete(observedOutboundTrack.id);
1685
+ this.emit("removed-outbound-track", observedOutboundTrack);
1686
+ this._notify("outbound-track-removed", { ...this.eventScope, observedOutboundTrack });
1687
+ }
1688
+ for (const observedPeerConnectionTransport of [...this.observedPeerConnectionTransports.values()]) {
1689
+ if (observedPeerConnectionTransport.visited) continue;
1690
+ this.observedPeerConnectionTransports.delete(observedPeerConnectionTransport.id);
1691
+ this._notify("peer-connection-transport-removed", { ...this.eventScope, observedPeerConnectionTransport });
1692
+ }
1693
+ for (const observedRemoteInboundRtp of [...this.observedRemoteInboundRtps.values()]) {
1694
+ if (observedRemoteInboundRtp.visited) continue;
1695
+ this.observedRemoteInboundRtps.delete(observedRemoteInboundRtp.ssrc);
1696
+ this._notify("remote-inbound-rtp-removed", { ...this.eventScope, observedRemoteInboundRtp });
1697
+ }
1698
+ for (const observedRemoteOutboundRtp of [...this.observedRemoteOutboundRtps.values()]) {
1699
+ if (observedRemoteOutboundRtp.visited) continue;
1700
+ this.observedRemoteOutboundRtps.delete(observedRemoteOutboundRtp.ssrc);
1701
+ this._notify("remote-outbound-rtp-removed", { ...this.eventScope, observedRemoteOutboundRtp });
1702
+ }
1703
+ }
1704
+ _updateCertificateStats(stats) {
1705
+ let observedCertificate = this.observedCertificates.get(stats.id);
1706
+ if (!observedCertificate) {
1707
+ if (!stats.timestamp || !stats.id || !stats.fingerprint) {
1708
+ return logger.warn(
1709
+ `ObservedPeerConnection received an invalid CertificateStats (missing timestamp OR id OR fingerprint field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1710
+ stats
1711
+ );
1712
+ }
1713
+ observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
1714
+ observedCertificate.update(stats);
1715
+ this.observedCertificates.set(stats.id, observedCertificate);
1716
+ this._notify("certificate-added", { ...this.eventScope, observedCertificate });
1717
+ } else {
1718
+ observedCertificate.update(stats);
1719
+ this._notify("certificate-updated", { ...this.eventScope, observedCertificate });
1720
+ }
1721
+ return observedCertificate;
1722
+ }
1723
+ _updateCodecStats(stats) {
1724
+ let observedCodec = this.observedCodecs.get(stats.id);
1725
+ if (!observedCodec) {
1726
+ if (!stats.timestamp || !stats.id || !stats.mimeType) {
1727
+ return logger.warn(
1728
+ `ObservedPeerConnection received an invalid CodecStats (missing timestamp OR id OR mimeType field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1729
+ stats
1730
+ );
1731
+ }
1732
+ observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
1733
+ observedCodec.update(stats);
1734
+ this.observedCodecs.set(stats.id, observedCodec);
1735
+ this._notify("codec-added", { ...this.eventScope, observedCodec });
1736
+ } else {
1737
+ observedCodec.update(stats);
1738
+ }
1739
+ this._notify("codec-updated", { ...this.eventScope, observedCodec });
1740
+ return observedCodec;
1741
+ }
1742
+ _updateDataChannelStats(stats) {
1743
+ let observedDataChannel = this.observedDataChannels.get(stats.id);
1744
+ if (!observedDataChannel) {
1745
+ if (!stats.timestamp || !stats.id) {
1746
+ return logger.warn(
1747
+ `ObservedPeerConnection received an invalid DataChannelStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1748
+ stats
1749
+ );
1750
+ }
1751
+ observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
1752
+ observedDataChannel.update(stats);
1753
+ this.observedDataChannels.set(stats.id, observedDataChannel);
1754
+ this._notify("data-channel-added", { ...this.eventScope, observedDataChannel });
1755
+ } else {
1756
+ observedDataChannel.update(stats);
1757
+ }
1758
+ this._notify("data-channel-updated", { ...this.eventScope, observedDataChannel });
1759
+ return observedDataChannel;
1760
+ }
1761
+ _updateIceCandidateStats(stats) {
1762
+ let observedIceCandidate = this.observedIceCandidates.get(stats.id);
1763
+ if (!observedIceCandidate) {
1764
+ if (!stats.timestamp || !stats.id) {
1765
+ return logger.warn(
1766
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1767
+ stats
1768
+ );
1769
+ }
1770
+ observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
1771
+ observedIceCandidate.update(stats);
1772
+ this.observedIceCandidates.set(stats.id, observedIceCandidate);
1773
+ this._notify("ice-candidate-added", { ...this.eventScope, observedIceCandidate });
1774
+ } else {
1775
+ observedIceCandidate.update(stats);
1776
+ }
1777
+ this._notify("ice-candidate-updated", { ...this.eventScope, observedIceCandidate });
1778
+ return observedIceCandidate;
1779
+ }
1780
+ _updateIceCandidatePairStats(stats) {
1781
+ let observedIceCandidatePair = this.observedIceCandidatesPair.get(stats.id);
1782
+ if (!observedIceCandidatePair) {
1783
+ if (!stats.timestamp || !stats.id) {
1784
+ return logger.warn(
1785
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1786
+ stats
1787
+ );
1788
+ }
1789
+ observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
1790
+ observedIceCandidatePair.update(stats);
1791
+ this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
1792
+ this._notify("ice-candidate-pair-added", { ...this.eventScope, observedIceCandidatePair });
1793
+ } else {
1794
+ observedIceCandidatePair.update(stats);
1795
+ }
1796
+ this._notify("ice-candidate-pair-updated", { ...this.eventScope, observedIceCandidatePair });
1797
+ return observedIceCandidatePair;
1798
+ }
1799
+ _updateIceTransportStats(stats) {
1800
+ let observedIceTransport = this.observedIceTransports.get(stats.id);
1801
+ if (!observedIceTransport) {
1802
+ if (!stats.timestamp || !stats.id) {
1803
+ return logger.warn(
1804
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1805
+ stats
1806
+ );
1807
+ }
1808
+ observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
1809
+ observedIceTransport.update(stats);
1810
+ this.observedIceTransports.set(stats.id, observedIceTransport);
1811
+ this._notify("ice-transport-added", { ...this.eventScope, observedIceTransport });
1812
+ } else {
1813
+ observedIceTransport.update(stats);
1814
+ }
1815
+ this._notify("ice-transport-updated", { ...this.eventScope, observedIceTransport });
1816
+ return observedIceTransport;
1817
+ }
1818
+ _updateInboundRtpStats(stats) {
1819
+ let observedInboundRtp = this.observedInboundRtps.get(stats.ssrc);
1820
+ if (!observedInboundRtp) {
1821
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind || !stats.trackIdentifier) {
1822
+ return logger.warn(
1823
+ `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}`,
1824
+ stats
1825
+ );
1826
+ }
1827
+ observedInboundRtp = new ObservedInboundRtp(
1828
+ stats.timestamp,
1829
+ stats.id,
1830
+ stats.ssrc,
1831
+ stats.kind,
1832
+ stats.trackIdentifier,
1833
+ this
1834
+ );
1835
+ observedInboundRtp.update(stats);
1836
+ this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
1837
+ this._notify("inbound-rtp-added", { ...this.eventScope, observedInboundRtp });
1838
+ } else {
1839
+ observedInboundRtp.update(stats);
1840
+ }
1841
+ this._notify("inbound-rtp-updated", { ...this.eventScope, observedInboundRtp });
1842
+ return observedInboundRtp;
1843
+ }
1844
+ _updateInboundTrackSample(stats) {
1845
+ let observedInboundTrack = this.observedInboundTracks.get(stats.id);
1846
+ if (!observedInboundTrack) {
1847
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1848
+ return logger.warn(
1849
+ `ObservedPeerConnection received an invalid InboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1850
+ stats
1851
+ );
1852
+ }
1853
+ const inboundRtp = [...this.observedInboundRtps.values()].find((inbRtp) => inbRtp.trackIdentifier === stats.id);
1854
+ const mediaPlayout = inboundRtp ? [...this.observedMediaPlayouts.values()].find((mp) => mp.id === inboundRtp.playoutId) : void 0;
1855
+ observedInboundTrack = new ObservedInboundTrack(
1856
+ stats.timestamp,
1857
+ stats.id,
1858
+ stats.kind,
1859
+ this,
1860
+ inboundRtp,
1861
+ mediaPlayout
1862
+ );
1863
+ observedInboundTrack.update(stats);
1864
+ this.observedInboundTracks.set(stats.id, observedInboundTrack);
1865
+ this._notify("inbound-track-added", { ...this.eventScope, observedInboundTrack });
1866
+ } else {
1867
+ observedInboundTrack.update(stats);
1868
+ }
1869
+ this._notify("inbound-track-updated", { ...this.eventScope, observedInboundTrack });
1870
+ return observedInboundTrack;
1871
+ }
1872
+ _updateMediaPlayoutStats(stats) {
1873
+ let observedMediaPlayout = this.observedMediaPlayouts.get(stats.id);
1874
+ if (!observedMediaPlayout) {
1875
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1876
+ return logger.warn(
1877
+ `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}`,
1878
+ stats
1879
+ );
1880
+ }
1881
+ observedMediaPlayout = new ObservedMediaPlayout(
1882
+ stats.timestamp,
1883
+ stats.id,
1884
+ stats.kind,
1885
+ this
1886
+ );
1887
+ observedMediaPlayout.update(stats);
1888
+ this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
1889
+ this._notify("media-playout-added", { ...this.eventScope, observedMediaPlayout });
1890
+ } else {
1891
+ observedMediaPlayout.update(stats);
1892
+ }
1893
+ this._notify("media-playout-updated", { ...this.eventScope, observedMediaPlayout });
1894
+ return observedMediaPlayout;
1895
+ }
1896
+ _updateMediaSourceStats(stats) {
1897
+ let observedMediaSource = this.observedMediaSources.get(stats.id);
1898
+ if (!observedMediaSource) {
1899
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1900
+ return logger.warn(
1901
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1902
+ stats
1903
+ );
1904
+ }
1905
+ observedMediaSource = new ObservedMediaSource(
1906
+ stats.timestamp,
1907
+ stats.id,
1908
+ stats.kind,
1909
+ this
1910
+ );
1911
+ observedMediaSource.update(stats);
1912
+ this.observedMediaSources.set(stats.id, observedMediaSource);
1913
+ this._notify("media-source-added", { ...this.eventScope, observedMediaSource });
1914
+ } else {
1915
+ observedMediaSource.update(stats);
1916
+ }
1917
+ this._notify("media-source-updated", { ...this.eventScope, observedMediaSource });
1918
+ return observedMediaSource;
1919
+ }
1920
+ _updateOutboundRtpStats(stats) {
1921
+ let observedOutboundRtp = this.observedOutboundRtps.get(stats.ssrc);
1922
+ if (!observedOutboundRtp) {
1923
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
1924
+ return logger.warn(
1925
+ `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}`,
1926
+ stats
1927
+ );
1928
+ }
1929
+ observedOutboundRtp = new ObservedOutboundRtp(
1930
+ stats.timestamp,
1931
+ stats.id,
1932
+ stats.ssrc,
1933
+ stats.kind,
1934
+ this
1935
+ );
1936
+ observedOutboundRtp.update(stats);
1937
+ this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
1938
+ this._notify("outbound-rtp-added", { ...this.eventScope, observedOutboundRtp });
1939
+ } else {
1940
+ observedOutboundRtp.update(stats);
1941
+ }
1942
+ this._notify("outbound-rtp-updated", { ...this.eventScope, observedOutboundRtp });
1943
+ return observedOutboundRtp;
1944
+ }
1945
+ _updateOutboundTrackSample(stats) {
1946
+ let observedOutboundTrack = this.observedOutboundTracks.get(stats.id);
1947
+ if (!observedOutboundTrack) {
1948
+ if (!stats.timestamp || !stats.id || !stats.kind) {
1949
+ return logger.warn(
1950
+ `ObservedPeerConnection received an invalid OutboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1951
+ stats
1952
+ );
1953
+ }
1954
+ const observedMediaSource = [...this.observedMediaSources.values()].find((mediaSource) => mediaSource.trackIdentifier === stats.id);
1955
+ const outboundRtps = observedMediaSource ? [...this.observedOutboundRtps.values()].filter((outboundRtp) => outboundRtp.mediaSourceId === observedMediaSource?.id) : void 0;
1956
+ observedOutboundTrack = new ObservedOutboundTrack(
1957
+ stats.timestamp,
1958
+ stats.id,
1959
+ stats.kind,
1960
+ this,
1961
+ outboundRtps,
1962
+ observedMediaSource
1963
+ );
1964
+ observedOutboundTrack.update(stats);
1965
+ this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
1966
+ this._notify("outbound-track-added", { ...this.eventScope, observedOutboundTrack });
1967
+ } else {
1968
+ observedOutboundTrack.update(stats);
1969
+ }
1970
+ this._notify("outbound-track-updated", { ...this.eventScope, observedOutboundTrack });
1971
+ return observedOutboundTrack;
1972
+ }
1973
+ _updatePeerConnectionTransportStats(stats) {
1974
+ let observedPeerConnectionTransport = this.observedPeerConnectionTransports.get(stats.id);
1975
+ if (!observedPeerConnectionTransport) {
1976
+ if (!stats.timestamp || !stats.id) {
1977
+ return logger.warn(
1978
+ `ObservedPeerConnection received an invalid PeerConnectionTransportStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
1979
+ stats
1980
+ );
1981
+ }
1982
+ observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
1983
+ observedPeerConnectionTransport.update(stats);
1984
+ this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
1985
+ this._notify("peer-connection-transport-added", { ...this.eventScope, observedPeerConnectionTransport });
1986
+ } else {
1987
+ observedPeerConnectionTransport.update(stats);
1988
+ }
1989
+ this._notify("peer-connection-transport-updated", { ...this.eventScope, observedPeerConnectionTransport });
1990
+ return observedPeerConnectionTransport;
1991
+ }
1992
+ _updateRemoteInboundRtpStats(stats) {
1993
+ let observedRemoteInboundRtp = this.observedRemoteInboundRtps.get(stats.ssrc);
1994
+ if (!observedRemoteInboundRtp) {
1995
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
1996
+ return logger.warn(
1997
+ `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}`,
1998
+ stats
1999
+ );
2000
+ }
2001
+ observedRemoteInboundRtp = new ObservedRemoteInboundRtp(
2002
+ stats.timestamp,
2003
+ stats.id,
2004
+ stats.ssrc,
2005
+ stats.kind,
2006
+ this
2007
+ );
2008
+ observedRemoteInboundRtp.update(stats);
2009
+ this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
2010
+ this._notify("remote-inbound-rtp-added", { ...this.eventScope, observedRemoteInboundRtp });
2011
+ } else {
2012
+ observedRemoteInboundRtp.update(stats);
2013
+ }
2014
+ this._notify("remote-inbound-rtp-updated", { ...this.eventScope, observedRemoteInboundRtp });
2015
+ return observedRemoteInboundRtp;
2016
+ }
2017
+ _updateRemoteOutboundRtpStats(stats) {
2018
+ let observedRemoteOutboundRtp = this.observedRemoteOutboundRtps.get(stats.ssrc);
2019
+ if (!observedRemoteOutboundRtp) {
2020
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
2021
+ return logger.warn(
2022
+ `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}`,
2023
+ stats
2024
+ );
2025
+ }
2026
+ observedRemoteOutboundRtp = new ObservedRemoteOutboundRtp(
2027
+ stats.timestamp,
2028
+ stats.id,
2029
+ stats.ssrc,
2030
+ stats.kind,
2031
+ this
2032
+ );
2033
+ observedRemoteOutboundRtp.update(stats);
2034
+ this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
2035
+ this._notify("remote-outbound-rtp-added", { ...this.eventScope, observedRemoteOutboundRtp });
2036
+ } else {
2037
+ observedRemoteOutboundRtp.update(stats);
2038
+ }
2039
+ this._notify("remote-outbound-rtp-updated", { ...this.eventScope, observedRemoteOutboundRtp });
2040
+ return observedRemoteOutboundRtp;
2041
+ }
2042
+ /** Emit an Observer-bus event scoped to this peer connection. */
2043
+ _notify(type, ...args) {
2044
+ this.client.call.observer.emit(type, ...args);
2045
+ }
2046
+ };
2047
+
2048
+ // src/schema/ClientEventTypes.ts
2049
+ var ClientEventTypes = /* @__PURE__ */ ((ClientEventTypes2) => {
2050
+ ClientEventTypes2["CLIENT_JOINED"] = "CLIENT_JOINED";
2051
+ ClientEventTypes2["CLIENT_LEFT"] = "CLIENT_LEFT";
2052
+ ClientEventTypes2["PEER_CONNECTION_OPENED"] = "PEER_CONNECTION_OPENED";
2053
+ ClientEventTypes2["PEER_CONNECTION_CLOSED"] = "PEER_CONNECTION_CLOSED";
2054
+ ClientEventTypes2["MEDIA_TRACK_ADDED"] = "MEDIA_TRACK_ADDED";
2055
+ ClientEventTypes2["MEDIA_TRACK_REMOVED"] = "MEDIA_TRACK_REMOVED";
2056
+ ClientEventTypes2["MEDIA_TRACK_RESUMED"] = "MEDIA_TRACK_RESUMED";
2057
+ ClientEventTypes2["MEDIA_TRACK_MUTED"] = "MEDIA_TRACK_MUTED";
2058
+ ClientEventTypes2["MEDIA_TRACK_UNMUTED"] = "MEDIA_TRACK_UNMUTED";
2059
+ ClientEventTypes2["ICE_GATHERING_STATE_CHANGED"] = "ICE_GATHERING_STATE_CHANGED";
2060
+ ClientEventTypes2["PEER_CONNECTION_STATE_CHANGED"] = "PEER_CONNECTION_STATE_CHANGED";
2061
+ ClientEventTypes2["ICE_CONNECTION_STATE_CHANGED"] = "ICE_CONNECTION_STATE_CHANGED";
2062
+ ClientEventTypes2["DATA_CHANNEL_OPEN"] = "DATA_CHANNEL_OPEN";
2063
+ ClientEventTypes2["DATA_CHANNEL_CLOSED"] = "DATA_CHANNEL_CLOSED";
2064
+ ClientEventTypes2["DATA_CHANNEL_ERROR"] = "DATA_CHANNEL_ERROR";
2065
+ ClientEventTypes2["NEGOTIATION_NEEDED"] = "NEGOTIATION_NEEDED";
2066
+ ClientEventTypes2["SIGNALING_STATE_CHANGE"] = "SIGNALING_STATE_CHANGE";
2067
+ ClientEventTypes2["ICE_CANDIDATE"] = "ICE_CANDIDATE";
2068
+ ClientEventTypes2["ICE_CANDIDATE_ERROR"] = "ICE_CANDIDATE_ERROR";
2069
+ ClientEventTypes2["PRODUCER_ADDED"] = "PRODUCER_ADDED";
2070
+ ClientEventTypes2["PRODUCER_REMOVED"] = "PRODUCER_REMOVED";
2071
+ ClientEventTypes2["PRODUCER_PAUSED"] = "PRODUCER_PAUSED";
2072
+ ClientEventTypes2["PRODUCER_RESUMED"] = "PRODUCER_RESUMED";
2073
+ ClientEventTypes2["CONSUMER_ADDED"] = "CONSUMER_ADDED";
2074
+ ClientEventTypes2["CONSUMER_REMOVED"] = "CONSUMER_REMOVED";
2075
+ ClientEventTypes2["CONSUMER_PAUSED"] = "CONSUMER_PAUSED";
2076
+ ClientEventTypes2["CONSUMER_RESUMED"] = "CONSUMER_RESUMED";
2077
+ ClientEventTypes2["DATA_PRODUCER_CREATED"] = "DATA_PRODUCER_CREATED";
2078
+ ClientEventTypes2["DATA_PRODUCER_CLOSED"] = "DATA_PRODUCER_CLOSED";
2079
+ ClientEventTypes2["DATA_CONSUMER_CREATED"] = "DATA_CONSUMER_CREATED";
2080
+ ClientEventTypes2["DATA_CONSUMER_CLOSED"] = "DATA_CONSUMER_CLOSED";
2081
+ return ClientEventTypes2;
2082
+ })(ClientEventTypes || {});
2083
+
2084
+ // src/schema/ClientMetaTypes.ts
2085
+ var ClientMetaTypes = /* @__PURE__ */ ((ClientMetaTypes2) => {
2086
+ ClientMetaTypes2["MEDIA_CONSTRAINT"] = "MEDIA_CONSTRAINT";
2087
+ ClientMetaTypes2["MEDIA_DEVICE"] = "MEDIA_DEVICE";
2088
+ ClientMetaTypes2["MEDIA_DEVICES_SUPPORTED_CONSTRAINTS"] = "MEDIA_DEVICES_SUPPORTED_CONSTRAINTS";
2089
+ ClientMetaTypes2["USER_MEDIA_ERROR"] = "USER_MEDIA_ERROR";
2090
+ ClientMetaTypes2["LOCAL_SDP"] = "LOCAL_SDP";
2091
+ ClientMetaTypes2["OPERATION_SYSTEM"] = "OPERATION_SYSTEM";
2092
+ ClientMetaTypes2["ENGINE"] = "ENGINE";
2093
+ ClientMetaTypes2["PLATFORM"] = "PLATFORM";
2094
+ ClientMetaTypes2["BROWSER"] = "BROWSER";
2095
+ return ClientMetaTypes2;
2096
+ })(ClientMetaTypes || {});
2097
+
2098
+ // src/ObservedClient.ts
2099
+ var logger2 = createLogger("ObservedClient");
2100
+ var ObservedClient = class extends EventEmitter2 {
2101
+ constructor(settings, call) {
2102
+ super();
2103
+ this.call = call;
2104
+ this.setMaxListeners(Infinity);
2105
+ this.eventScope = { observer: call.observer, observedCall: call, observedClient: this };
2106
+ this.sink = call.observer.config.createClientSink?.({ clientId: settings.clientId, observedCall: call });
2107
+ this.clientId = settings.clientId;
2108
+ this.appData = settings.appData ?? {};
2109
+ this.settings = {
2110
+ closeClientIfIdleForMs: settings.closeClientIfIdleForMs
2111
+ };
2112
+ if (this.sink) {
2113
+ const onError = (err) => logger2.warn("ClientSampleSink error for client %s: %o", this.clientId, err);
2114
+ this.sink.once("close", () => this.sink?.off("error", onError));
2115
+ this.sink.on("error", onError);
2116
+ }
2117
+ }
2118
+ call;
2119
+ clientId;
2120
+ observedPeerConnections = /* @__PURE__ */ new Map();
2121
+ /** Per-client sink (from `ObserverConfig.createClientSink`), if any. */
2122
+ sink;
2123
+ /** Ancestry base shared by all Observer-bus events originating at this client. */
2124
+ eventScope;
2125
+ calculatedScore = {
2126
+ weight: 1,
2127
+ value: void 0
2128
+ };
2129
+ settings;
2130
+ appData;
2131
+ attachments;
2132
+ updated = Date.now();
2133
+ acceptedSamples = 0;
2134
+ closed = false;
2135
+ joinedAt;
2136
+ leftAt;
2137
+ closedAt;
2138
+ lastSampleTimestamp;
2139
+ // the timestamp of the CLIENT_JOINED event
2140
+ operationSystem;
2141
+ engine;
2142
+ platform;
2143
+ browser;
2144
+ mediaConstraints = [];
2145
+ usingTURN = false;
2146
+ usingTCP = false;
2147
+ availableOutgoingBitrate = 0;
2148
+ availableIncomingBitrate = 0;
2149
+ deltaReceivedAudioBytes = 0;
2150
+ deltaReceivedVideoBytes = 0;
2151
+ deltaSentAudioBytes = 0;
2152
+ deltaSentVideoBytes = 0;
2153
+ deltaDataChannelBytesSent = 0;
2154
+ deltaDataChannelBytesReceived = 0;
2155
+ deltaDataChannelMessagesSent = 0;
2156
+ deltaDataChannelMessagesReceived = 0;
2157
+ deltaInboundPacketsLost = 0;
2158
+ deltaInboundPacketsReceived = 0;
2159
+ deltaOutboundPacketsSent = 0;
2160
+ deltaTransportSentBytes = 0;
2161
+ deltaTransportReceivedBytes = 0;
2162
+ deltaRttLt50Measurements = 0;
2163
+ deltaRttLt150Measurements = 0;
2164
+ deltaRttLt300Measurements = 0;
2165
+ deltaRttGtOrEq300Measurements = 0;
2166
+ currentMaxRttInMs;
2167
+ currentMinRttInMs;
2168
+ currentAvgRttInMs;
2169
+ sendingAudioBitrate = 0;
2170
+ sendingVideoBitrate = 0;
2171
+ receivingAudioBitrate = 0;
2172
+ receivingVideoBitrate = 0;
2173
+ numberOfInboundRtpStreams = 0;
2174
+ numberOfInbundTracks = 0;
2175
+ numberOfOutboundRtpStreams = 0;
2176
+ numberOfOutboundTracks = 0;
2177
+ numberOfDataChannels = 0;
2178
+ deltaNumberOfIssues = 0;
2179
+ totalScoreSum = 0;
2180
+ numberOfScoreMeasurements = 0;
2181
+ // public totalNumberOfIssues = 0;
2182
+ mediaDevices = [];
2183
+ issues = [];
2184
+ _injections = {};
2185
+ closeTimer;
2186
+ get numberOfPeerConnections() {
2187
+ return this.observedPeerConnections.size;
2188
+ }
2189
+ get score() {
2190
+ return this.calculatedScore.value;
2191
+ }
2192
+ close() {
2193
+ if (this.closed) return;
2194
+ this.closed = true;
2195
+ this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
2196
+ this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
2197
+ this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
2198
+ this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
2199
+ Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
2200
+ if (!this.leftAt) {
2201
+ this.leftAt = this.lastSampleTimestamp;
2202
+ if (this.leftAt) {
2203
+ this.emit("left");
2204
+ this._notify("client-left", { ...this.eventScope });
2205
+ }
2206
+ }
2207
+ this.closedAt = Date.now();
2208
+ this.emit("close");
2209
+ this._notify("client-closed", { ...this.eventScope });
2210
+ try {
2211
+ this.sink?.end();
2212
+ } catch (err) {
2213
+ logger2.warn("ClientSampleSink.end failed for client %s: %o", this.clientId, err);
2214
+ }
2215
+ }
2216
+ accept(sample, context) {
2217
+ if (this.closed) {
2218
+ logger2.warn("Attempted to accept a sample on a closed client %s", this.clientId);
2219
+ return;
2220
+ }
2221
+ this.sink?.write(sample);
2222
+ if (this.closeTimer) {
2223
+ clearTimeout(this.closeTimer);
2224
+ this.closeTimer = void 0;
2225
+ }
2226
+ const now = Date.now();
2227
+ const elapsedInMs = now - this.updated;
2228
+ const elapsedInSeconds = elapsedInMs / 1e3;
2229
+ let sumOfRtts = 0;
2230
+ let numberOfRttMeasurements = 0;
2231
+ ++this.acceptedSamples;
2232
+ this.availableIncomingBitrate = 0;
2233
+ this.availableOutgoingBitrate = 0;
2234
+ this.deltaDataChannelBytesReceived = 0;
2235
+ this.deltaDataChannelBytesSent = 0;
2236
+ this.deltaDataChannelMessagesReceived = 0;
2237
+ this.deltaDataChannelMessagesSent = 0;
2238
+ this.deltaInboundPacketsLost = 0;
2239
+ this.deltaInboundPacketsReceived = 0;
2240
+ this.deltaOutboundPacketsSent = 0;
2241
+ this.deltaReceivedAudioBytes = 0;
2242
+ this.deltaReceivedVideoBytes = 0;
2243
+ this.deltaSentAudioBytes = 0;
2244
+ this.deltaSentVideoBytes = 0;
2245
+ this.deltaTransportReceivedBytes = 0;
2246
+ this.deltaTransportSentBytes = 0;
2247
+ this.deltaRttLt50Measurements = 0;
2248
+ this.deltaRttLt150Measurements = 0;
2249
+ this.deltaRttLt300Measurements = 0;
2250
+ this.deltaRttGtOrEq300Measurements = 0;
2251
+ this.deltaNumberOfIssues = 0;
2252
+ this.numberOfDataChannels = 0;
2253
+ this.numberOfInboundRtpStreams = 0;
2254
+ this.numberOfInbundTracks = 0;
2255
+ this.numberOfOutboundRtpStreams = 0;
2256
+ this.numberOfOutboundTracks = 0;
2257
+ this.usingTURN = false;
2258
+ this.usingTCP = false;
2259
+ this.currentMinRttInMs = void 0;
2260
+ this.currentMaxRttInMs = void 0;
2261
+ this._mergeInjections(sample);
2262
+ const clientEventsPostBuffer = [];
2263
+ for (const clientEvent of sample.clientEvents ?? []) {
2264
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
2265
+ }
2266
+ for (const metaData of sample.clientMetaItems ?? []) {
2267
+ this.addMetadata(metaData);
2268
+ }
2269
+ for (const issue of sample.clientIssues ?? []) {
2270
+ this.addIssue(issue);
2271
+ ++this.deltaNumberOfIssues;
2272
+ }
2273
+ for (const extensionStat of sample.extensionStats ?? []) {
2274
+ this.addExtensionStats(extensionStat);
2275
+ }
2276
+ for (const pcSample of sample.peerConnections ?? []) {
2277
+ const observedPeerConnection = this._updatePeerConnection(pcSample, context);
2278
+ if (!observedPeerConnection) continue;
2279
+ this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
2280
+ this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
2281
+ this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
2282
+ this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
2283
+ this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
2284
+ this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
2285
+ this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
2286
+ this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
2287
+ this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
2288
+ this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
2289
+ this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
2290
+ this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
2291
+ this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
2292
+ this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
2293
+ this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
2294
+ this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
2295
+ this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
2296
+ this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
2297
+ this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
2298
+ this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
2299
+ if (observedPeerConnection.usingTURN) {
2300
+ this.usingTURN = true;
2301
+ }
2302
+ if (observedPeerConnection.usingTCP) {
2303
+ this.usingTCP = true;
2304
+ }
2305
+ if (observedPeerConnection.currentRttInMs) {
2306
+ if (this.currentMinRttInMs === void 0 || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
2307
+ this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
2308
+ }
2309
+ if (this.currentMaxRttInMs === void 0 || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
2310
+ this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
2311
+ }
2312
+ if (observedPeerConnection.currentRttInMs < 50) {
2313
+ this.deltaRttLt50Measurements += 1;
2314
+ } else if (observedPeerConnection.currentRttInMs < 150) {
2315
+ this.deltaRttLt150Measurements += 1;
2316
+ } else if (observedPeerConnection.currentRttInMs < 300) {
2317
+ this.deltaRttLt300Measurements += 1;
2318
+ } else if (300 <= observedPeerConnection.currentRttInMs) {
2319
+ this.deltaRttGtOrEq300Measurements += 1;
2320
+ }
2321
+ sumOfRtts += observedPeerConnection.currentRttInMs;
2322
+ ++numberOfRttMeasurements;
2323
+ }
2324
+ }
2325
+ for (const clientEvent of clientEventsPostBuffer) {
2326
+ this._processClientEvent(clientEvent);
2327
+ }
2328
+ this.attachments = sample.attachments;
2329
+ this.receivingAudioBitrate = this.deltaReceivedAudioBytes * 8 / elapsedInSeconds;
2330
+ this.receivingVideoBitrate = this.deltaReceivedVideoBytes * 8 / elapsedInSeconds;
2331
+ this.sendingAudioBitrate = this.deltaSentAudioBytes * 8 / elapsedInSeconds;
2332
+ this.sendingVideoBitrate = this.deltaSentVideoBytes * 8 / elapsedInSeconds;
2333
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : void 0;
2334
+ this.calculatedScore.value = sample.score;
2335
+ this.lastSampleTimestamp = sample.timestamp;
2336
+ const elapsedTimeInMs = now - this.updated;
2337
+ this.emit("update", sample, elapsedTimeInMs, context);
2338
+ this._notify("client-updated", { ...this.eventScope, sample, elapsedTimeInMs, context });
2339
+ this.updated = now;
2340
+ if (this.calculatedScore.value) {
2341
+ this.totalScoreSum += this.calculatedScore.value;
2342
+ ++this.numberOfScoreMeasurements;
2343
+ }
2344
+ if (this.settings.closeClientIfIdleForMs !== void 0) {
2345
+ this.closeTimer = setTimeout(() => {
2346
+ this.close();
2347
+ }, this.settings.closeClientIfIdleForMs);
2348
+ }
2349
+ }
2350
+ injectMetaData(metaData) {
2351
+ if (this.closed) return;
2352
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
2353
+ this._injections.clientMetaItems.push(metaData);
2354
+ }
2355
+ injectEvent(event) {
2356
+ if (this.closed) return;
2357
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
2358
+ this._injections.clientEvents.push(event);
2359
+ }
2360
+ injectIssue(issue) {
2361
+ if (this.closed) return;
2362
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
2363
+ this._injections.clientIssues.push(issue);
2364
+ }
2365
+ injectExtensionStat(stat) {
2366
+ if (this.closed) return;
2367
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
2368
+ this._injections.extensionStats.push(stat);
2369
+ }
2370
+ injectAttachment(key, value) {
2371
+ if (this.closed) return;
2372
+ if (!this._injections.attachments) this._injections.attachments = {};
2373
+ this._injections.attachments[key] = value;
2374
+ }
2375
+ addMetadata(metadata) {
2376
+ if (this.closed) return;
2377
+ switch (metadata.type) {
2378
+ case "BROWSER" /* BROWSER */: {
2379
+ this.browser = parseJsonAs(metadata.payload);
2380
+ break;
2381
+ }
2382
+ case "ENGINE" /* ENGINE */: {
2383
+ this.engine = parseJsonAs(metadata.payload);
2384
+ break;
2385
+ }
2386
+ case "PLATFORM" /* PLATFORM */: {
2387
+ this.platform = parseJsonAs(metadata.payload);
2388
+ break;
2389
+ }
2390
+ case "OPERATION_SYSTEM" /* OPERATION_SYSTEM */: {
2391
+ this.operationSystem = parseJsonAs(metadata.payload);
2392
+ break;
2393
+ }
2394
+ }
2395
+ this._notify("client-metadata", { ...this.eventScope, metaData: metadata });
2396
+ }
2397
+ addIssue(issue) {
2398
+ if (this.closed) return;
2399
+ this._notify("client-issue", { ...this.eventScope, issue });
2400
+ }
2401
+ addExtensionStats(stats) {
2402
+ this._notify("client-extension-stats", { ...this.eventScope, extensionStats: stats });
2403
+ }
2404
+ _processClientEvent(event, postBuffer) {
2405
+ switch (event.type) {
2406
+ case "CLIENT_JOINED" /* CLIENT_JOINED */: {
2407
+ if (event.timestamp) {
2408
+ if (!this.joinedAt) {
2409
+ this.joinedAt = event.timestamp;
2410
+ this.emit("joined");
2411
+ this._notify("client-joined", { ...this.eventScope });
2412
+ } else if (this.joinedAt < event.timestamp) {
2413
+ this._notify("client-rejoined", { ...this.eventScope, timestamp: event.timestamp });
2414
+ } else {
2415
+ this.joinedAt = event.timestamp;
2416
+ logger2.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
2417
+ }
2418
+ }
2419
+ logger2.debug("Client %s joined at %o", this.clientId, event);
2420
+ break;
2421
+ }
2422
+ case "CLIENT_LEFT" /* CLIENT_LEFT */: {
2423
+ if (event.timestamp) {
2424
+ if (!this.leftAt) {
2425
+ this.leftAt = event.timestamp;
2426
+ this.emit("left");
2427
+ this._notify("client-left", { ...this.eventScope });
2428
+ } else {
2429
+ logger2.warn(`Client ${this.clientId} leftAt timestamp was already set`);
2430
+ }
2431
+ }
2432
+ logger2.debug("Client %s left at %o", this.clientId, event);
2433
+ break;
2434
+ }
2435
+ case "PEER_CONNECTION_OPENED" /* PEER_CONNECTION_OPENED */: {
2436
+ const payload = parseJsonAs(event.payload);
2437
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string") {
2438
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2439
+ if (observedPeerConnection) {
2440
+ observedPeerConnection.openedAt = event.timestamp;
2441
+ } else if (postBuffer) {
2442
+ postBuffer.push(event);
2443
+ } else {
2444
+ logger2.warn("Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o", event);
2445
+ }
2446
+ }
2447
+ break;
2448
+ }
2449
+ case "PEER_CONNECTION_CLOSED" /* PEER_CONNECTION_CLOSED */: {
2450
+ const payload = parseJsonAs(event.payload);
2451
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string") {
2452
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2453
+ if (observedPeerConnection) {
2454
+ observedPeerConnection.closedAt = event.timestamp;
2455
+ } else if (postBuffer) {
2456
+ postBuffer.push(event);
2457
+ } else {
2458
+ logger2.warn("Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o", event);
2459
+ }
2460
+ }
2461
+ break;
2462
+ }
2463
+ case "MEDIA_TRACK_ADDED" /* MEDIA_TRACK_ADDED */: {
2464
+ const payload = parseJsonAs(event.payload);
2465
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2466
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2467
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2468
+ if (observedTrack) {
2469
+ observedTrack.addedAt = event.timestamp;
2470
+ } else if (postBuffer) {
2471
+ postBuffer.push(event);
2472
+ } else {
2473
+ logger2.warn("Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2474
+ }
2475
+ }
2476
+ break;
2477
+ }
2478
+ case "MEDIA_TRACK_REMOVED" /* MEDIA_TRACK_REMOVED */: {
2479
+ const payload = parseJsonAs(event.payload);
2480
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2481
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2482
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2483
+ if (observedTrack) {
2484
+ observedTrack.removedAt = event.timestamp;
2485
+ } else if (postBuffer) {
2486
+ postBuffer.push(event);
2487
+ } else {
2488
+ logger2.warn("Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2489
+ }
2490
+ }
2491
+ break;
2492
+ }
2493
+ case "DATA_CHANNEL_OPEN" /* DATA_CHANNEL_OPEN */: {
2494
+ const payload = parseJsonAs(event.payload);
2495
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.dataChannelId && typeof payload.dataChannelId === "string") {
2496
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2497
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
2498
+ if (observedDataChannel) {
2499
+ observedDataChannel.addedAt = event.timestamp;
2500
+ } else if (postBuffer) {
2501
+ postBuffer.push(event);
2502
+ } else {
2503
+ logger2.warn("Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o", event);
2504
+ }
2505
+ }
2506
+ break;
2507
+ }
2508
+ case "DATA_CHANNEL_CLOSED" /* DATA_CHANNEL_CLOSED */: {
2509
+ const payload = parseJsonAs(event.payload);
2510
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.dataChannelId && typeof payload.dataChannelId === "string") {
2511
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2512
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
2513
+ if (observedDataChannel) {
2514
+ observedDataChannel.removedAt = event.timestamp;
2515
+ } else if (postBuffer) {
2516
+ postBuffer.push(event);
2517
+ } else {
2518
+ logger2.warn("Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o", event);
2519
+ }
2520
+ }
2521
+ break;
2522
+ }
2523
+ case "MEDIA_TRACK_MUTED" /* MEDIA_TRACK_MUTED */: {
2524
+ const payload = parseJsonAs(event.payload);
2525
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2526
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2527
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
2528
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2529
+ if (observedPeerConnection) {
2530
+ if (observedInboundTrack) {
2531
+ observedInboundTrack.muted = true;
2532
+ this._notify("inbound-track-muted", { ...this.eventScope, observedPeerConnection, observedInboundTrack });
2533
+ } else if (observedOutboundTrack) {
2534
+ observedOutboundTrack.muted = true;
2535
+ this._notify("outbound-track-muted", { ...this.eventScope, observedPeerConnection, observedOutboundTrack });
2536
+ }
2537
+ } else if (postBuffer) {
2538
+ postBuffer.push(event);
2539
+ } else {
2540
+ logger2.warn("Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2541
+ }
2542
+ }
2543
+ break;
2544
+ }
2545
+ case "MEDIA_TRACK_UNMUTED" /* MEDIA_TRACK_UNMUTED */: {
2546
+ const payload = parseJsonAs(event.payload);
2547
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.trackId && typeof payload.trackId === "string") {
2548
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2549
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
2550
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
2551
+ if (observedPeerConnection) {
2552
+ if (observedInboundTrack) {
2553
+ observedInboundTrack.muted = false;
2554
+ this._notify("inbound-track-unmuted", { ...this.eventScope, observedPeerConnection, observedInboundTrack });
2555
+ } else if (observedOutboundTrack) {
2556
+ observedOutboundTrack.muted = false;
2557
+ this._notify("outbound-track-unmuted", { ...this.eventScope, observedPeerConnection, observedOutboundTrack });
2558
+ }
2559
+ } else if (postBuffer) {
2560
+ postBuffer.push(event);
2561
+ } else {
2562
+ logger2.warn("Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o", event);
2563
+ }
2564
+ }
2565
+ break;
2566
+ }
2567
+ case "ICE_CONNECTION_STATE_CHANGED" /* ICE_CONNECTION_STATE_CHANGED */: {
2568
+ const payload = parseJsonAs(event.payload);
2569
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.iceConnectionState && typeof payload.iceConnectionState === "string") {
2570
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2571
+ if (observedPeerConnection) {
2572
+ observedPeerConnection.iceConnectionState = payload.iceConnectionState;
2573
+ this._notify("ice-connection-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.iceConnectionState });
2574
+ } else if (postBuffer) {
2575
+ postBuffer.push(event);
2576
+ } else {
2577
+ logger2.warn("Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2578
+ }
2579
+ }
2580
+ break;
2581
+ }
2582
+ case "ICE_GATHERING_STATE_CHANGED" /* ICE_GATHERING_STATE_CHANGED */: {
2583
+ const payload = parseJsonAs(event.payload);
2584
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.iceGatheringState && typeof payload.iceGatheringState === "string") {
2585
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2586
+ if (observedPeerConnection) {
2587
+ observedPeerConnection.iceGatheringState = payload.iceGatheringState;
2588
+ this._notify("ice-gathering-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.iceGatheringState });
2589
+ } else if (postBuffer) {
2590
+ postBuffer.push(event);
2591
+ } else {
2592
+ logger2.warn("Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2593
+ }
2594
+ }
2595
+ break;
2596
+ }
2597
+ case "PEER_CONNECTION_STATE_CHANGED" /* PEER_CONNECTION_STATE_CHANGED */: {
2598
+ const payload = parseJsonAs(event.payload);
2599
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === "string" && payload?.peerConnectionState && typeof payload.peerConnectionState === "string") {
2600
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
2601
+ if (observedPeerConnection) {
2602
+ observedPeerConnection.connectionState = payload.peerConnectionState;
2603
+ this._notify("connection-state-changed", { ...this.eventScope, observedPeerConnection, state: payload.peerConnectionState });
2604
+ } else if (postBuffer) {
2605
+ postBuffer.push(event);
2606
+ } else {
2607
+ logger2.warn("Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o", event);
2608
+ }
2609
+ }
2610
+ break;
2611
+ }
2612
+ }
2613
+ this._notify("client-event", { ...this.eventScope, event });
2614
+ }
2615
+ _updatePeerConnection(sample, context) {
2616
+ let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
2617
+ if (!observedPeerConnection) {
2618
+ if (!sample.peerConnectionId) {
2619
+ return logger2.warn(
2620
+ `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
2621
+ sample
2622
+ ), void 0;
2623
+ }
2624
+ const newObservedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
2625
+ newObservedPeerConnection.once("close", () => {
2626
+ this.observedPeerConnections.delete(sample.peerConnectionId);
2627
+ });
2628
+ this.observedPeerConnections.set(sample.peerConnectionId, newObservedPeerConnection);
2629
+ this._notify("peer-connection-added", { ...this.eventScope, observedPeerConnection: newObservedPeerConnection });
2630
+ observedPeerConnection = newObservedPeerConnection;
2631
+ }
2632
+ observedPeerConnection.accept(sample, context);
2633
+ return observedPeerConnection;
2634
+ }
2635
+ _mergeInjections(sample) {
2636
+ if (this.closed) return sample;
2637
+ if (this._injections.clientEvents) {
2638
+ if (!sample.clientEvents) sample.clientEvents = [];
2639
+ sample.clientEvents.push(...this._injections.clientEvents);
2640
+ this._injections.clientEvents = void 0;
2641
+ }
2642
+ if (this._injections.clientIssues) {
2643
+ if (!sample.clientIssues) sample.clientIssues = [];
2644
+ sample.clientIssues.push(...this._injections.clientIssues);
2645
+ this._injections.clientIssues = void 0;
2646
+ }
2647
+ if (this._injections.extensionStats) {
2648
+ if (!sample.extensionStats) sample.extensionStats = [];
2649
+ sample.extensionStats.push(...this._injections.extensionStats);
2650
+ this._injections.extensionStats = void 0;
2651
+ }
2652
+ if (this._injections.attachments) {
2653
+ if (!sample.attachments) sample.attachments = {};
2654
+ Object.assign(sample.attachments, this._injections.attachments);
2655
+ this._injections.attachments = void 0;
2656
+ }
2657
+ if (this._injections.clientMetaItems) {
2658
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
2659
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
2660
+ this._injections.clientMetaItems = void 0;
2661
+ }
2662
+ return sample;
2663
+ }
2664
+ /** Emit an Observer-bus event scoped to this client (or a peer connection under it). */
2665
+ _notify(type, ...args) {
2666
+ this.call.observer.emit(type, ...args);
2667
+ }
2668
+ };
2669
+
2670
+ // src/scores/DefaultCallScoreCalculator.ts
2671
+ var DefaultCallScoreCalculator = class {
2672
+ constructor(observedCall) {
2673
+ this.observedCall = observedCall;
2674
+ }
2675
+ observedCall;
2676
+ update() {
2677
+ let totalScore = 0;
2678
+ let totalWeight = 0;
2679
+ for (const client of this.observedCall.observedClients.values()) {
2680
+ if (client.score === void 0) continue;
2681
+ totalScore += client.score * client.calculatedScore.weight;
2682
+ totalWeight += client.calculatedScore.weight;
2683
+ }
2684
+ this.observedCall.calculatedScore.value = totalWeight ? totalScore / totalWeight : void 0;
2685
+ }
2686
+ };
2687
+
2688
+ // src/updaters/OnAllClientCallUpdater.ts
2689
+ var OnAllClientCallUpdater = class {
2690
+ constructor(_observedCall) {
2691
+ this._observedCall = _observedCall;
2692
+ this._onNewObservedClient = this._onNewObservedClient.bind(this);
2693
+ this._observedCall.once("close", () => {
2694
+ this._observedCall.off("newclient", this._onNewObservedClient);
2695
+ });
2696
+ this._observedCall.on("newclient", this._onNewObservedClient);
2697
+ }
2698
+ _observedCall;
2699
+ name = "OnAllClientCallUpdater";
2700
+ description = "Call the update() method of the ObservedCall once all client has been updated";
2701
+ _updatedClients = /* @__PURE__ */ new Set();
2702
+ closed = false;
2703
+ close() {
2704
+ if (this.closed) return;
2705
+ this.closed = true;
2706
+ this._updatedClients.clear();
2707
+ }
2708
+ _onNewObservedClient(observedClient) {
2709
+ if (this.closed) return;
2710
+ const onUpdate = (...args) => this._onClientUpdate(observedClient, args[2]);
2711
+ observedClient.once("close", () => {
2712
+ observedClient.off("update", onUpdate);
2713
+ this._updatedClients.delete(observedClient.clientId);
2714
+ this._updateIfEveryClientUpdated();
2715
+ });
2716
+ observedClient.on("update", onUpdate);
2717
+ }
2718
+ _onClientUpdate(observedClient, context) {
2719
+ if (observedClient.closed) return;
2720
+ this._updatedClients.add(observedClient.clientId);
2721
+ this._updateIfEveryClientUpdated(context);
2722
+ }
2723
+ _updateIfEveryClientUpdated(context) {
2724
+ if (this._updatedClients.size < this._observedCall.observedClients.size) {
2725
+ return;
2726
+ } else if (this.closed) {
2727
+ return;
2728
+ }
2729
+ this._updatedClients.clear();
2730
+ this._observedCall.update(context);
2731
+ }
2732
+ };
2733
+
2734
+ // src/updaters/OnAnyClientCallUpdater.ts
2735
+ var OnAnyClientCallUpdater = class {
2736
+ constructor(_observedCall) {
2737
+ this._observedCall = _observedCall;
2738
+ this._onNewObservedClient = this._onNewObservedClient.bind(this);
2739
+ this._observedCall.once("close", () => {
2740
+ this._observedCall.off("newclient", this._onNewObservedClient);
2741
+ });
2742
+ this._observedCall.on("newclient", this._onNewObservedClient);
2743
+ }
2744
+ _observedCall;
2745
+ name = "OnAnyClientCallUpdater";
2746
+ description = "Call the update() method of the ObservedCall when any of the client has been updated";
2747
+ closed = false;
2748
+ close() {
2749
+ if (this.closed) return;
2750
+ this.closed = true;
2751
+ }
2752
+ _onNewObservedClient(observedClient) {
2753
+ if (this.closed) return;
2754
+ const onUpdate = (...args) => {
2755
+ if (this.closed) return;
2756
+ this._observedCall.update(args[2]);
2757
+ };
2758
+ observedClient.once("close", () => {
2759
+ observedClient.off("update", onUpdate);
2760
+ });
2761
+ observedClient.on("update", onUpdate);
2762
+ }
2763
+ };
2764
+
2765
+ // src/detectors/Detectors.ts
2766
+ var logger3 = createLogger("Detectors");
2767
+ var Detectors = class {
2768
+ _detectors;
2769
+ constructor(...detectors) {
2770
+ this._detectors = detectors;
2771
+ }
2772
+ get listOfNames() {
2773
+ return this._detectors.map((d) => d.name);
2774
+ }
2775
+ add(detector) {
2776
+ this._detectors.push(detector);
2777
+ }
2778
+ remove(detector) {
2779
+ this._detectors = this._detectors.filter((d) => d !== detector);
2780
+ }
2781
+ update() {
2782
+ for (const detector of this._detectors) {
2783
+ try {
2784
+ detector.update();
2785
+ } catch (err) {
2786
+ logger3.warn(`Error updating detector ${detector?.name}`, err);
2787
+ }
2788
+ }
2789
+ }
2790
+ clear() {
2791
+ this._detectors = [];
2792
+ }
2793
+ };
2794
+
2795
+ // src/ObservedCall.ts
2796
+ var logger4 = createLogger("ObservedCall");
2797
+ var ObservedCall = class extends EventEmitter3 {
2798
+ constructor(settings, observer) {
2799
+ super();
2800
+ this.observer = observer;
2801
+ this.setMaxListeners(Infinity);
2802
+ this.eventScope = { observer: this.observer, observedCall: this };
2803
+ this.callId = settings.callId;
2804
+ this.appData = settings.appData ?? {};
2805
+ this.scoreCalculator = new DefaultCallScoreCalculator(this);
2806
+ this.detectors = new Detectors();
2807
+ switch (settings.updatePolicy) {
2808
+ case "update-on-any-client-updated":
2809
+ this.updater = new OnAnyClientCallUpdater(this);
2810
+ break;
2811
+ case "update-when-all-client-updated":
2812
+ this.updater = new OnAllClientCallUpdater(this);
2813
+ break;
2814
+ case "none":
2815
+ break;
2816
+ }
2817
+ this.settings = {
2818
+ closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs
2819
+ };
2820
+ }
2821
+ observer;
2822
+ updater;
2823
+ scoreCalculator;
2824
+ detectors;
2825
+ callId;
2826
+ observedClients = /* @__PURE__ */ new Map();
2827
+ clientsUsedTurn = /* @__PURE__ */ new Set();
2828
+ calculatedScore = {
2829
+ weight: 1,
2830
+ value: void 0
2831
+ };
2832
+ remoteTrackResolver;
2833
+ totalAddedClients = 0;
2834
+ totalRemovedClients = 0;
2835
+ numberOfIssues = 0;
2836
+ numberOfPeerConnections = 0;
2837
+ numberOfInboundRtpStreams = 0;
2838
+ numberOfOutboundRtpStreams = 0;
2839
+ numberOfDataChannels = 0;
2840
+ maxNumberOfClients = 0;
2841
+ deltaNumberOfIssues = 0;
2842
+ // public deltaRttLt50Measurements = 0;
2843
+ // public deltaRttLt150Measurements = 0;
2844
+ // public deltaRttLt300Measurements = 0;
2845
+ // public deltaRttGtOrEq300Measurements = 0;
2846
+ appData;
2847
+ closed = false;
2848
+ startedAt;
2849
+ endedAt;
2850
+ closedAt;
2851
+ settings;
2852
+ /** Ancestry base shared by all Observer-bus events originating at this call. */
2853
+ eventScope;
2854
+ closeTimer;
2855
+ get numberOfClients() {
2856
+ return this.observedClients.size;
2857
+ }
2858
+ get score() {
2859
+ return this.calculatedScore.value;
2860
+ }
2861
+ /** Raise a call-level (server-side) issue; surfaced on the Observer bus as `call-issue`. */
2862
+ addIssue(issue) {
2863
+ if (this.closed) return;
2864
+ this._notify("call-issue", { ...this.eventScope, issue });
2865
+ }
2866
+ close() {
2867
+ if (this.closed) return;
2868
+ this.update();
2869
+ this.closed = true;
2870
+ this.updater?.close();
2871
+ let minSampleTimestamps;
2872
+ let maxSampleTimestamps;
2873
+ for (const client of this.observedClients.values()) {
2874
+ client.close();
2875
+ if (client.joinedAt) minSampleTimestamps = Math.min(minSampleTimestamps ?? client.joinedAt, client.joinedAt);
2876
+ if (client.leftAt) maxSampleTimestamps = Math.max(maxSampleTimestamps ?? client.leftAt, client.leftAt);
2877
+ }
2878
+ if (this.startedAt === void 0) this.startedAt = minSampleTimestamps;
2879
+ if (this.endedAt === void 0) this.endedAt = maxSampleTimestamps;
2880
+ this.closedAt = Date.now();
2881
+ this.emit("close");
2882
+ this._notify("call-closed", { ...this.eventScope });
2883
+ }
2884
+ getObservedClient(clientId) {
2885
+ if (this.closed || !this.observedClients.has(clientId)) return;
2886
+ return this.observedClients.get(clientId);
2887
+ }
2888
+ createObservedClient(settings) {
2889
+ if (this.closed) {
2890
+ logger4.warn("Attempted to create a client (clientId: %s) on a closed call %s", settings.clientId, this.callId);
2891
+ return void 0;
2892
+ }
2893
+ if (this.observedClients.has(settings.clientId)) {
2894
+ logger4.warn("Client with id %s already exists in call %s; returning the existing instance", settings.clientId, this.callId);
2895
+ return this.observedClients.get(settings.clientId);
2896
+ }
2897
+ if (!settings.closeClientIfIdleForMs) {
2898
+ settings.closeClientIfIdleForMs = this.observer.config.closeClientIfIdleForMs;
2899
+ }
2900
+ if (settings.appData === void 0) {
2901
+ settings.appData = this.observer.config.createClientAppData?.({ clientId: settings.clientId, observedCall: this });
2902
+ }
2903
+ const result = new ObservedClient(settings, this);
2904
+ const wasEmpty = this.observedClients.size === 0;
2905
+ const onUpdate = () => this._onClientUpdate(result);
2906
+ const joined = () => this._clientJoined(result);
2907
+ const left = () => this._clientLeft(result);
2908
+ result.once("close", () => {
2909
+ result.off("update", onUpdate);
2910
+ result.off("joined", joined);
2911
+ result.off("left", left);
2912
+ this.observedClients.delete(settings.clientId);
2913
+ if (this.observedClients.size === 0) {
2914
+ this.emit("empty");
2915
+ this._notify("call-empty", { ...this.eventScope });
2916
+ if (this.settings.closeCallIfEmptyForMs) {
2917
+ this.closeTimer = setTimeout(() => {
2918
+ this.close();
2919
+ }, this.settings.closeCallIfEmptyForMs);
2920
+ }
2921
+ }
2922
+ ++this.totalRemovedClients;
2923
+ });
2924
+ result.on("update", onUpdate);
2925
+ result.on("joined", joined);
2926
+ result.on("left", left);
2927
+ ++this.totalAddedClients;
2928
+ this.observedClients.set(settings.clientId, result);
2929
+ this.maxNumberOfClients = Math.max(this.maxNumberOfClients, this.observedClients.size);
2930
+ if (this.closeTimer) {
2931
+ clearTimeout(this.closeTimer);
2932
+ this.closeTimer = void 0;
2933
+ }
2934
+ this.emit("newclient", result);
2935
+ this._notify("client-added", { ...this.eventScope, observedClient: result });
2936
+ if (result.sink) {
2937
+ this._notify("client-sink-created", { ...this.eventScope, observedClient: result, sink: result.sink });
2938
+ }
2939
+ if (wasEmpty) {
2940
+ this.emit("not-empty");
2941
+ this._notify("call-not-empty", { ...this.eventScope });
2942
+ }
2943
+ return result;
2944
+ }
2945
+ getOrCreateObservedClient(settings) {
2946
+ return this.getObservedClient(settings.clientId) ?? this.createObservedClient(settings);
2947
+ }
2948
+ update(context) {
2949
+ if (this.closed) return;
2950
+ this.numberOfInboundRtpStreams = 0;
2951
+ this.numberOfOutboundRtpStreams = 0;
2952
+ this.numberOfPeerConnections = 0;
2953
+ this.numberOfDataChannels = 0;
2954
+ for (const client of this.observedClients.values()) {
2955
+ this.numberOfInboundRtpStreams += client.numberOfInboundRtpStreams;
2956
+ this.numberOfOutboundRtpStreams += client.numberOfOutboundRtpStreams;
2957
+ this.numberOfPeerConnections += client.numberOfPeerConnections;
2958
+ this.numberOfDataChannels += client.numberOfDataChannels;
2959
+ }
2960
+ this.scoreCalculator.update();
2961
+ this.detectors.update();
2962
+ this.emit("update");
2963
+ this._notify("call-updated", { ...this.eventScope, context });
2964
+ this.deltaNumberOfIssues = 0;
2965
+ }
2966
+ _onClientUpdate(client) {
2967
+ this.deltaNumberOfIssues += client.deltaNumberOfIssues;
2968
+ this.numberOfIssues += client.deltaNumberOfIssues;
2969
+ if (client.usingTURN) {
2970
+ this.clientsUsedTurn.add(client.clientId);
2971
+ }
2972
+ }
2973
+ _clientJoined(client) {
2974
+ if (!client.joinedAt) return;
2975
+ this.startedAt = Math.min(this.startedAt ?? client.joinedAt, client.joinedAt);
2976
+ }
2977
+ _clientLeft(client) {
2978
+ if (!client.leftAt) return;
2979
+ this.endedAt = Math.max(this.endedAt ?? client.leftAt, client.leftAt);
2980
+ }
2981
+ /** Emit an Observer-bus event scoped to this call. */
2982
+ _notify(type, ...args) {
2983
+ this.observer.emit(type, ...args);
2984
+ }
2985
+ };
2986
+
2987
+ // src/Observer.ts
2988
+ import { EventEmitter as EventEmitter6 } from "events";
2989
+
2990
+ // src/ObservedTURN.ts
2991
+ import { EventEmitter as EventEmitter4 } from "events";
2992
+
2993
+ // src/ObservedTurnServer.ts
2994
+ var ObservedTurnServer = class {
2995
+ constructor(url, observedTURN) {
2996
+ this.url = url;
2997
+ this.observedTURN = observedTURN;
2998
+ }
2999
+ url;
3000
+ observedTURN;
3001
+ totalBytesSent = 0;
3002
+ totalBytesReceived = 0;
3003
+ totalPacketsSent = 0;
3004
+ totalPacketsReceived = 0;
3005
+ packetsSentPerSecond = 0;
3006
+ packetsReceivedPerSecond = 0;
3007
+ outboundBitrate = 0;
3008
+ inboundBitrate = 0;
3009
+ deltaBytesSent = 0;
3010
+ deltaBytesReceived = 0;
3011
+ deltaPacketsSent = 0;
3012
+ deltaPacketsReceived = 0;
3013
+ observedPeerConnections = /* @__PURE__ */ new Map();
3014
+ updated = Date.now();
3015
+ updateTurnUsage(...selectedcandidatepairs) {
3016
+ for (const selectedcandidatepair of selectedcandidatepairs) {
3017
+ this.deltaBytesReceived += selectedcandidatepair.deltaBytesReceived;
3018
+ this.deltaBytesSent += selectedcandidatepair.deltaBytesSent;
3019
+ this.deltaPacketsReceived += selectedcandidatepair.deltaPacketsReceived;
3020
+ this.deltaPacketsSent += selectedcandidatepair.deltaPacketsSent;
3021
+ }
3022
+ }
3023
+ update() {
3024
+ const now = Date.now();
3025
+ const deltaInS = (now - this.updated) / 1e3;
3026
+ this.packetsSentPerSecond = this.deltaPacketsSent / deltaInS;
3027
+ this.packetsReceivedPerSecond = this.deltaPacketsReceived / deltaInS;
3028
+ this.outboundBitrate = this.deltaBytesReceived * 8 / deltaInS;
3029
+ this.inboundBitrate = this.deltaBytesSent * 8 / deltaInS;
3030
+ this.totalBytesSent += this.deltaBytesSent;
3031
+ this.totalBytesReceived += this.deltaBytesReceived;
3032
+ this.totalPacketsSent += this.deltaPacketsSent;
3033
+ this.totalPacketsReceived += this.deltaPacketsReceived;
3034
+ this.observedTURN.totalBytesSent += this.deltaBytesSent;
3035
+ this.observedTURN.totalBytesReceived += this.deltaBytesReceived;
3036
+ this.observedTURN.totalPacketsSent += this.deltaPacketsSent;
3037
+ this.observedTURN.totalPacketsReceived += this.deltaPacketsReceived;
3038
+ this.observedTURN.inboundBitrate += this.inboundBitrate;
3039
+ this.observedTURN.outboundBitrate += this.outboundBitrate;
3040
+ this.observedTURN.packetsReceivedPerSecond += this.packetsReceivedPerSecond;
3041
+ this.observedTURN.packetsSentPerSecond += this.packetsSentPerSecond;
3042
+ this.deltaBytesSent = 0;
3043
+ this.deltaBytesReceived = 0;
3044
+ this.deltaPacketsSent = 0;
3045
+ this.deltaPacketsReceived = 0;
3046
+ this.updated = now;
3047
+ }
3048
+ };
3049
+
3050
+ // src/ObservedTURN.ts
3051
+ var logger5 = createLogger("ObservedTURN");
3052
+ var ObservedTURN = class extends EventEmitter4 {
3053
+ totalBytesSent = 0;
3054
+ totalBytesReceived = 0;
3055
+ totalPacketsSent = 0;
3056
+ totalPacketsReceived = 0;
3057
+ packetsSentPerSecond = 0;
3058
+ packetsReceivedPerSecond = 0;
3059
+ outboundBitrate = 0;
3060
+ inboundBitrate = 0;
3061
+ numberOfClients = 0;
3062
+ servers = /* @__PURE__ */ new Map();
3063
+ constructor() {
3064
+ super();
3065
+ }
3066
+ update() {
3067
+ const clientIds = /* @__PURE__ */ new Set();
3068
+ this.packetsReceivedPerSecond = 0;
3069
+ this.packetsSentPerSecond = 0;
3070
+ this.outboundBitrate = 0;
3071
+ this.inboundBitrate = 0;
3072
+ for (const server of this.servers.values()) {
3073
+ server.update();
3074
+ server.observedPeerConnections.forEach((pc) => clientIds.add(pc.client.clientId));
3075
+ }
3076
+ this.numberOfClients = clientIds.size;
3077
+ }
3078
+ addPeerConnection(peerConnection) {
3079
+ const turnPairs = peerConnection.selectedIceCandidatePairs.filter((pair) => pair.getLocalCandidate()?.candidateType === "relay" && pair.getRemoteCandidate()?.url?.startsWith("turn:"));
3080
+ if (turnPairs.length !== 1) {
3081
+ return logger5.warn(`Expected exactly one TURN pair, but found for peerconnection ${peerConnection.peerConnectionId}`, turnPairs.length), void 0;
3082
+ }
3083
+ const candidatePair = turnPairs[0];
3084
+ const rawUrl = candidatePair.getRemoteCandidate()?.url;
3085
+ if (!rawUrl) {
3086
+ return logger5.warn(`No remote candidate URL found for peerconnection ${peerConnection.peerConnectionId}`), void 0;
3087
+ }
3088
+ const turnUrl = new URL(rawUrl);
3089
+ const turnServerUrl = `${turnUrl.protocol}//${turnUrl.hostname}:${turnUrl.port}`;
3090
+ let turnServer = this.servers.get(turnServerUrl);
3091
+ if (!turnServer) {
3092
+ turnServer = new ObservedTurnServer(turnServerUrl, this);
3093
+ }
3094
+ return turnServer;
3095
+ }
3096
+ removePeerConnection(peerConnection) {
3097
+ for (const turnServer of this.servers.values()) {
3098
+ turnServer.observedPeerConnections.delete(peerConnection.peerConnectionId);
3099
+ }
3100
+ }
3101
+ };
3102
+
3103
+ // src/updaters/OnAllCallObserverUpdater.ts
3104
+ var OnAllCallObserverUpdater = class {
3105
+ constructor(observer) {
3106
+ this.observer = observer;
3107
+ this._onNewObservedCall = this._onNewObservedCall.bind(this);
3108
+ this.observer.once("observer-closed", () => {
3109
+ this.observer.off("call-added", this._onNewObservedCall);
3110
+ });
3111
+ this.observer.on("call-added", this._onNewObservedCall);
3112
+ }
3113
+ observer;
3114
+ name = "OnAllCallObserverUpdater";
3115
+ description = "Call Observer's update() method when all of the ObservedCalls are updated";
3116
+ _updatedCalls = /* @__PURE__ */ new Set();
3117
+ closed = false;
3118
+ close() {
3119
+ if (this.closed) return;
3120
+ this.closed = true;
3121
+ this._updatedCalls.clear();
3122
+ }
3123
+ _onNewObservedCall({ observedCall }) {
3124
+ if (this.closed) return;
3125
+ const onUpdate = () => this._onObservedCallUpdated(observedCall);
3126
+ observedCall.once("close", () => {
3127
+ observedCall.off("update", onUpdate);
3128
+ this._updatedCalls.delete(observedCall.callId);
3129
+ this._updateIfEveryCallUpdated();
3130
+ });
3131
+ observedCall.on("update", onUpdate);
3132
+ }
3133
+ _onObservedCallUpdated(observedCall) {
3134
+ if (observedCall.closed) return;
3135
+ this._updatedCalls.add(observedCall.callId);
3136
+ this._updateIfEveryCallUpdated();
3137
+ }
3138
+ _updateIfEveryCallUpdated() {
3139
+ if (this._updatedCalls.size < this.observer.observedCalls.size) return;
3140
+ else if (this.closed) return;
3141
+ this._updatedCalls.clear();
3142
+ this.observer.update();
3143
+ }
3144
+ };
3145
+
3146
+ // src/updaters/OnAnyCallObserverUpdater.ts
3147
+ var OnAnyCallObserverUpdater = class {
3148
+ constructor(observver) {
3149
+ this.observver = observver;
3150
+ this._onNewObservedCall = this._onNewObservedCall.bind(this);
3151
+ this.observver.once("observer-closed", () => {
3152
+ this.observver.off("call-added", this._onNewObservedCall);
3153
+ });
3154
+ this.observver.on("call-added", this._onNewObservedCall);
3155
+ }
3156
+ observver;
3157
+ name = "OnAnyCallObserverUpdater";
3158
+ description = "Call Observer's update() method on any of the ObservedCall is updated";
3159
+ closed = false;
3160
+ close() {
3161
+ if (this.closed) return;
3162
+ this.closed = true;
3163
+ }
3164
+ _onNewObservedCall({ observedCall }) {
3165
+ if (this.closed) return;
3166
+ const onUpdate = () => {
3167
+ if (this.closed) return;
3168
+ this.observver.update();
3169
+ };
3170
+ observedCall.once("close", () => {
3171
+ observedCall.off("update", onUpdate);
3172
+ });
3173
+ observedCall.on("update", onUpdate);
3174
+ }
3175
+ };
3176
+
3177
+ // src/common/Middleware.ts
3178
+ var Executor = class {
3179
+ constructor(stack, finalCallback) {
3180
+ this.stack = stack;
3181
+ this.finalCallback = finalCallback;
3182
+ }
3183
+ stack;
3184
+ finalCallback;
3185
+ done = false;
3186
+ index = 0;
3187
+ prevIndex = -1;
3188
+ // Executes the middleware stack
3189
+ execute(input) {
3190
+ if (this.index <= this.prevIndex) {
3191
+ throw new Error("Middleware must call next() only once!");
3192
+ } else if (this.done) {
3193
+ throw new Error("Middleware stack has already been executed!");
3194
+ }
3195
+ this.prevIndex = this.index;
3196
+ const middleware = this.stack[this.index];
3197
+ this.index++;
3198
+ if (middleware) {
3199
+ return middleware(input, (nextInput) => this.execute(nextInput));
3200
+ }
3201
+ this.done = true;
3202
+ this.finalCallback?.(input);
3203
+ }
3204
+ };
3205
+ var MiddlewareProcessor = class {
3206
+ stack = [];
3207
+ finalCallback;
3208
+ addMiddleware(...middlewares) {
3209
+ if (middlewares && middlewares.length > 0) {
3210
+ this.stack.push(...middlewares);
3211
+ }
3212
+ return this;
3213
+ }
3214
+ removeMiddleware(...middlewares) {
3215
+ if (middlewares && middlewares.length > 0) {
3216
+ for (const middleware of middlewares) {
3217
+ const index = this.stack.indexOf(middleware);
3218
+ if (index >= 0) {
3219
+ this.stack.splice(index, 1);
3220
+ }
3221
+ }
3222
+ }
3223
+ return this;
3224
+ }
3225
+ process(value) {
3226
+ if (this.stack.length === 0) {
3227
+ return this.finalCallback?.(value);
3228
+ }
3229
+ const executor = new Executor(this.stack, this.finalCallback);
3230
+ executor.execute(value);
3231
+ }
3232
+ };
3233
+
3234
+ // src/ObservedMediasoupRouter.ts
3235
+ import { EventEmitter as EventEmitter5 } from "events";
3236
+ var logger6 = createLogger("ObservedMediasoupRouter");
3237
+ var ObservedMediasoupRouter = class extends EventEmitter5 {
3238
+ router;
3239
+ sample;
3240
+ appData;
3241
+ webrtcTransportIds = /* @__PURE__ */ new Set();
3242
+ get attachments() {
3243
+ return this.sample.attachments;
3244
+ }
3245
+ closed = false;
3246
+ constructor(settings) {
3247
+ super();
3248
+ this.router = settings.router;
3249
+ this.appData = settings.appData ?? {};
3250
+ this.sample = {
3251
+ routerId: this.router.id,
3252
+ attachments: settings.attachments ?? {},
3253
+ createdAt: Date.now(),
3254
+ producers: [],
3255
+ consumers: [],
3256
+ dataProducers: [],
3257
+ dataConsumers: [],
3258
+ transports: []
3259
+ };
3260
+ this.attachRouterListeners();
3261
+ }
3262
+ get id() {
3263
+ return this.router.id;
3264
+ }
3265
+ addTransport = (transport) => {
3266
+ let transportType = transport.type;
3267
+ if (!transportType) {
3268
+ if (transport.constructor.name === "WebRtcTransport") transportType = "webrtc";
3269
+ else if (transport.constructor.name === "PlainTransport") transportType = "plain";
3270
+ else if (transport.constructor.name === "PipeTransport") transportType = "pipe";
3271
+ else if (transport.constructor.name === "DirectTransport") transportType = "direct";
3272
+ }
3273
+ if (!transportType) return logger6.warn(`Unknown transport type for transport ${transport.id}: ${transport.constructor.name}`);
3274
+ switch (transportType) {
3275
+ case "webrtc":
3276
+ return this.addWebRtcTransport(transport);
3277
+ case "plain":
3278
+ return this.addPlainTransport(transport);
3279
+ case "pipe":
3280
+ return this.addPipeTransport(transport);
3281
+ case "direct":
3282
+ return this.addDirectTransport(transport);
3283
+ default:
3284
+ return logger6.warn(`Unsupported transport type for transport ${transport.id}: ${transportType}`);
3285
+ }
3286
+ };
3287
+ addWebRtcTransport(transport) {
3288
+ const history = [];
3289
+ const transportSample = {
3290
+ id: transport.id,
3291
+ type: "webrtc",
3292
+ createdAt: Date.now(),
3293
+ tuple: transport.iceSelectedTuple,
3294
+ history
3295
+ };
3296
+ this.sample.transports.push(transportSample);
3297
+ const onIceStateChange = (iceState) => {
3298
+ history.push({
3299
+ type: `icestate-changed-to-${iceState}`,
3300
+ timestamp: Date.now()
3301
+ });
3302
+ };
3303
+ const onIceSelectedTupleChange = (tuple) => {
3304
+ transportSample.tuple = tuple;
3305
+ history.push({
3306
+ type: "iceselectedtuple-changed",
3307
+ timestamp: Date.now(),
3308
+ ...tuple
3309
+ });
3310
+ };
3311
+ const onDtlsStateChange = (dtlsState) => {
3312
+ history.push({
3313
+ type: `dtlsstate-changed-to-${dtlsState}`,
3314
+ timestamp: Date.now()
3315
+ });
3316
+ if (dtlsState === "connected" && transportSample.connectedAt === void 0) {
3317
+ transportSample.connectedAt = Date.now();
3318
+ }
3319
+ };
3320
+ const onSctpStateChange = (sctpState) => {
3321
+ history.push({
3322
+ type: `sctpstate-changed-to-${sctpState}`,
3323
+ timestamp: Date.now()
3324
+ });
3325
+ };
3326
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3327
+ transport.observer.off("icestatechange", onIceStateChange);
3328
+ transport.observer.off("iceselectedtuplechange", onIceSelectedTupleChange);
3329
+ transport.observer.off("dtlsstatechange", onDtlsStateChange);
3330
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3331
+ this.webrtcTransportIds.delete(transport.id);
3332
+ });
3333
+ transport.observer.on("icestatechange", onIceStateChange);
3334
+ transport.observer.on("iceselectedtuplechange", onIceSelectedTupleChange);
3335
+ transport.observer.on("dtlsstatechange", onDtlsStateChange);
3336
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3337
+ this.webrtcTransportIds.add(transport.id);
3338
+ }
3339
+ addPlainTransport(transport) {
3340
+ const history = [];
3341
+ const transportSample = {
3342
+ id: transport.id,
3343
+ type: "plain",
3344
+ createdAt: Date.now(),
3345
+ tuple: transport.tuple,
3346
+ rtcpTuple: transport.rtcpTuple,
3347
+ history
3348
+ };
3349
+ this.sample.transports.push(transportSample);
3350
+ const onTuple = (tuple) => {
3351
+ transportSample.tuple = tuple;
3352
+ if (transportSample.connectedAt === void 0) transportSample.connectedAt = Date.now();
3353
+ history.push({ type: "tuple-changed", timestamp: Date.now(), ...tuple });
3354
+ };
3355
+ const onRtcpTuple = (rtcpTuple) => {
3356
+ transportSample.rtcpTuple = rtcpTuple;
3357
+ history.push({ type: "rtcptuple-changed", timestamp: Date.now(), ...rtcpTuple });
3358
+ };
3359
+ const onSctpStateChange = (sctpState) => {
3360
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3361
+ transportSample.connectedAt = Date.now();
3362
+ }
3363
+ history.push({
3364
+ type: `sctpstate-changed-to-${sctpState}`,
3365
+ timestamp: Date.now()
3366
+ });
3367
+ };
3368
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3369
+ transport.observer.off("tuple", onTuple);
3370
+ transport.observer.off("rtcptuple", onRtcpTuple);
3371
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3372
+ });
3373
+ transport.observer.on("tuple", onTuple);
3374
+ transport.observer.on("rtcptuple", onRtcpTuple);
3375
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3376
+ }
3377
+ addPipeTransport(transport) {
3378
+ const history = [];
3379
+ const transportSample = {
3380
+ id: transport.id,
3381
+ type: "pipe",
3382
+ createdAt: Date.now(),
3383
+ tuple: transport.tuple,
3384
+ history
3385
+ };
3386
+ this.sample.transports.push(transportSample);
3387
+ const onSctpStateChange = (sctpState) => {
3388
+ if (sctpState === "connected" && transportSample.connectedAt === void 0) {
3389
+ transportSample.connectedAt = Date.now();
3390
+ }
3391
+ history.push({
3392
+ type: `sctpstate-changed-to-${sctpState}`,
3393
+ timestamp: Date.now()
3394
+ });
3395
+ };
3396
+ this._attachTransportObserverListeners(transport, transportSample, () => {
3397
+ transport.observer.off("sctpstatechange", onSctpStateChange);
3398
+ });
3399
+ transport.observer.on("sctpstatechange", onSctpStateChange);
3400
+ }
3401
+ addDirectTransport(transport) {
3402
+ const now = Date.now();
3403
+ const transportSample = {
3404
+ id: transport.id,
3405
+ type: "direct",
3406
+ createdAt: now,
3407
+ // A DirectTransport has no network layer — it is connected the instant it exists.
3408
+ connectedAt: now,
3409
+ history: []
3410
+ };
3411
+ this.sample.transports.push(transportSample);
3412
+ this._attachTransportObserverListeners(transport, transportSample);
3413
+ }
3414
+ addProducer(transport, producer) {
3415
+ const firstCodec = producer.rtpParameters.codecs[0];
3416
+ const codecInfo = {
3417
+ mimeType: firstCodec?.mimeType ?? "unknown/unknown",
3418
+ payloadType: firstCodec?.payloadType ?? 0,
3419
+ clockRate: firstCodec?.clockRate ?? 0,
3420
+ channels: firstCodec?.channels,
3421
+ parameters: firstCodec?.parameters,
3422
+ rtcpFeedback: firstCodec?.rtcpFeedback
3423
+ };
3424
+ const producerSample = {
3425
+ id: producer.id,
3426
+ createdAt: Date.now(),
3427
+ kind: producer.kind,
3428
+ codecInfo,
3429
+ ssrcs: producer.rtpParameters.encodings?.map((encoding) => encoding.ssrc).filter((ssrc) => typeof ssrc === "number"),
3430
+ rids: producer.rtpParameters.encodings?.map((encoding) => encoding.rid).filter((rid) => typeof rid === "string"),
3431
+ history: [],
3432
+ transportId: transport.id
3433
+ };
3434
+ this.sample.producers.push(producerSample);
3435
+ const onPause = () => {
3436
+ producerSample.history.push({
3437
+ type: "pause",
3438
+ timestamp: Date.now()
3439
+ });
3440
+ };
3441
+ const onResume = () => {
3442
+ producerSample.history.push({
3443
+ type: "resume",
3444
+ timestamp: Date.now()
3445
+ });
3446
+ };
3447
+ producer.observer.once("close", () => {
3448
+ producer.observer.off("pause", onPause);
3449
+ producer.observer.off("resume", onResume);
3450
+ producerSample.closedAt = Date.now();
3451
+ });
3452
+ producer.observer.on("pause", onPause);
3453
+ producer.observer.on("resume", onResume);
3454
+ }
3455
+ addConsumer(transport, consumer) {
3456
+ const consumerSample = {
3457
+ id: consumer.id,
3458
+ producerId: consumer.producerId,
3459
+ transportId: transport.id,
3460
+ createdAt: Date.now(),
3461
+ kind: consumer.kind,
3462
+ history: []
3463
+ };
3464
+ this.sample.consumers.push(consumerSample);
3465
+ const onPause = () => {
3466
+ consumerSample.history.push({
3467
+ type: "pause",
3468
+ timestamp: Date.now()
3469
+ });
3470
+ };
3471
+ const onResume = () => {
3472
+ consumerSample.history.push({
3473
+ type: "resume",
3474
+ timestamp: Date.now()
3475
+ });
3476
+ };
3477
+ const onProducerPause = () => {
3478
+ consumerSample.history.push({
3479
+ type: "producerPaused",
3480
+ timestamp: Date.now()
3481
+ });
3482
+ };
3483
+ const onProducerResume = () => {
3484
+ consumerSample.history.push({
3485
+ type: "producerResumed",
3486
+ timestamp: Date.now()
3487
+ });
3488
+ };
3489
+ consumer.observer.once("close", () => {
3490
+ consumer.observer.off("pause", onPause);
3491
+ consumer.observer.off("resume", onResume);
3492
+ consumer.off("producerresume", onProducerResume);
3493
+ consumer.off("producerpause", onProducerPause);
3494
+ consumerSample.closedAt = Date.now();
3495
+ });
3496
+ consumer.observer.on("pause", onPause);
3497
+ consumer.observer.on("resume", onResume);
3498
+ consumer.on("producerpause", onProducerPause);
3499
+ consumer.on("producerresume", onProducerResume);
3500
+ }
3501
+ addDataProducer(transport, dataProducer) {
3502
+ const dataProducerSample = {
3503
+ id: dataProducer.id,
3504
+ transportId: transport.id,
3505
+ createdAt: Date.now(),
3506
+ label: dataProducer.label,
3507
+ protocol: dataProducer.protocol
3508
+ };
3509
+ this.sample.dataProducers.push(dataProducerSample);
3510
+ dataProducer.observer.once("close", () => {
3511
+ dataProducerSample.closedAt = Date.now();
3512
+ });
3513
+ }
3514
+ addDataConsumer(transport, dataConsumer) {
3515
+ const dataConsumerSample = {
3516
+ id: dataConsumer.id,
3517
+ dataProducerId: dataConsumer.dataProducerId,
3518
+ transportId: transport.id,
3519
+ createdAt: Date.now(),
3520
+ label: dataConsumer.label,
3521
+ protocol: dataConsumer.protocol
3522
+ };
3523
+ this.sample.dataConsumers.push(dataConsumerSample);
3524
+ dataConsumer.observer.once("close", () => {
3525
+ dataConsumerSample.closedAt = Date.now();
3526
+ });
3527
+ }
3528
+ close() {
3529
+ if (this.closed) return;
3530
+ this.closed = true;
3531
+ this.emit("close");
3532
+ }
3533
+ attachRouterListeners() {
3534
+ this.router.observer.once("close", () => {
3535
+ this.router.observer.off("newtransport", this.addTransport);
3536
+ this.sample.closedAt = Date.now();
3537
+ this.close();
3538
+ });
3539
+ this.router.observer.on("newtransport", this.addTransport);
3540
+ }
3541
+ _attachTransportObserverListeners(transport, transportSample, onClose) {
3542
+ const onNewProducer = (producer) => {
3543
+ this.addProducer(transport, producer);
3544
+ };
3545
+ const onNewConsumer = (consumer) => {
3546
+ this.addConsumer(transport, consumer);
3547
+ };
3548
+ const onNewDataProducer = (dataProducer) => {
3549
+ this.addDataProducer(transport, dataProducer);
3550
+ };
3551
+ const onNewDataConsumer = (dataConsumer) => {
3552
+ this.addDataConsumer(transport, dataConsumer);
3553
+ };
3554
+ transport.observer.once("close", () => {
3555
+ transport.observer.off("newproducer", onNewProducer);
3556
+ transport.observer.off("newconsumer", onNewConsumer);
3557
+ transport.observer.off("newdataproducer", onNewDataProducer);
3558
+ transport.observer.off("newdataconsumer", onNewDataConsumer);
3559
+ onClose?.();
3560
+ transportSample.closedAt = Date.now();
3561
+ });
3562
+ transport.observer.on("newproducer", onNewProducer);
3563
+ transport.observer.on("newconsumer", onNewConsumer);
3564
+ transport.observer.on("newdataproducer", onNewDataProducer);
3565
+ transport.observer.on("newdataconsumer", onNewDataConsumer);
3566
+ }
3567
+ };
3568
+
3569
+ // src/Observer.ts
3570
+ var logger7 = createLogger("Observer");
3571
+ var Observer = class extends EventEmitter6 {
3572
+ constructor(config = {
3573
+ updatePolicy: "update-when-all-call-updated",
3574
+ appData: {}
3575
+ }) {
3576
+ super();
3577
+ this.config = config;
3578
+ this.setMaxListeners(Infinity);
3579
+ this.update = this.update.bind(this);
3580
+ const currentUpdatePolicy = config?.updatePolicy ?? "update-when-all-call-updated";
3581
+ switch (currentUpdatePolicy) {
3582
+ case "update-on-any-call-updated":
3583
+ this.updater = new OnAnyCallObserverUpdater(this);
3584
+ break;
3585
+ case "update-when-all-call-updated":
3586
+ this.updater = new OnAllCallObserverUpdater(this);
3587
+ break;
3588
+ case "none":
3589
+ break;
3590
+ }
3591
+ }
3592
+ config;
3593
+ observedTURN = new ObservedTURN();
3594
+ observedCalls = /* @__PURE__ */ new Map();
3595
+ observedMediasoupRouters = /* @__PURE__ */ new Map();
3596
+ updater;
3597
+ /** Ancestry base shared by all Observer-bus events originating at the observer. */
3598
+ eventScope = { observer: this };
3599
+ closed = false;
3600
+ totalAddedCall = 0;
3601
+ totalRemovedCall = 0;
3602
+ numberOfClientsUsingTurn = 0;
3603
+ numberOfClients = 0;
3604
+ numberOfInboundRtpStreams = 0;
3605
+ numberOfOutboundRtpStreams = 0;
3606
+ numberOfDataChannels = 0;
3607
+ numberOfPeerConnections = 0;
3608
+ /** Global, pre-dispatch middleware chain run on every accepted sample. */
3609
+ acceptMiddlewares = new MiddlewareProcessor();
3610
+ get numberOfCalls() {
3611
+ return this.observedCalls.size;
3612
+ }
3613
+ get appData() {
3614
+ return this.config.appData;
3615
+ }
3616
+ getObservedCall(callId) {
3617
+ if (this.closed || !this.observedCalls.has(callId)) return;
3618
+ return this.observedCalls.get(callId);
3619
+ }
3620
+ createObservedCall(settings) {
3621
+ if (this.closed) {
3622
+ logger7.warn("Attempted to create a call (callId: %s) on a closed observer", settings.callId);
3623
+ return void 0;
3624
+ }
3625
+ if (this.observedCalls.has(settings.callId)) {
3626
+ logger7.warn("Observed Call with id %s already exists; returning the existing instance", settings.callId);
3627
+ return this.observedCalls.get(settings.callId);
3628
+ }
3629
+ const callSettings = {
3630
+ ...settings,
3631
+ updatePolicy: settings.updatePolicy ?? this.config.defaultCallUpdatePolicy,
3632
+ closeCallIfEmptyForMs: settings.closeCallIfEmptyForMs ?? this.config.closeCallIfEmptyForMs,
3633
+ appData: settings.appData ?? this.config.createCallAppData?.({ callId: settings.callId, observer: this })
3634
+ };
3635
+ const observedCall = new ObservedCall(callSettings, this);
3636
+ observedCall.remoteTrackResolver = this.config.createTrackResolver?.(observedCall);
3637
+ observedCall.once("close", () => {
3638
+ this.observedCalls.delete(observedCall.callId);
3639
+ ++this.totalRemovedCall;
3640
+ });
3641
+ this.observedCalls.set(observedCall.callId, observedCall);
3642
+ ++this.totalAddedCall;
3643
+ this._notify("call-added", { ...this.eventScope, observedCall });
3644
+ return observedCall;
3645
+ }
3646
+ getOrCreateObservedCall(settings) {
3647
+ return this.getObservedCall(settings.callId) ?? this.createObservedCall(settings);
3648
+ }
3649
+ createObservedMediasoupRouter(settings) {
3650
+ if (this.closed) {
3651
+ logger7.warn("Attempted to create mediasoup router (id: %d) on a closed observer", settings.router.id);
3652
+ return void 0;
3653
+ }
3654
+ if (this.observedMediasoupRouters.has(settings.router.id)) {
3655
+ logger7.warn("Observed Mediasoup Router (id %s) already exists; returning the existing instance", settings.router.id);
3656
+ return this.observedMediasoupRouters.get(settings.router.id);
3657
+ }
3658
+ const observedMediasoupRouter = new ObservedMediasoupRouter(settings);
3659
+ const observedMediasoupRouterScope = {
3660
+ observedMediasoupRouter,
3661
+ observer: this
3662
+ };
3663
+ const onPeerConnectionAdded = (observedPeerConnectionScope) => {
3664
+ if (!observedMediasoupRouter.webrtcTransportIds.has(observedPeerConnectionScope.observedPeerConnection.peerConnectionId)) return;
3665
+ this.emit("mediasoup-router-matched-with-peer-connection", {
3666
+ ...observedMediasoupRouterScope,
3667
+ ...observedPeerConnectionScope
3668
+ });
3669
+ };
3670
+ const stopMatching = () => this.off("peer-connection-added", onPeerConnectionAdded);
3671
+ this.on("peer-connection-added", onPeerConnectionAdded);
3672
+ this.once("observer-closed", stopMatching);
3673
+ observedMediasoupRouter.once("close", stopMatching);
3674
+ observedMediasoupRouter.once("close", () => {
3675
+ this.observedMediasoupRouters.delete(observedMediasoupRouter.id);
3676
+ this.emit("mediasoup-router-removed", observedMediasoupRouterScope);
3677
+ });
3678
+ this.observedMediasoupRouters.set(observedMediasoupRouter.id, observedMediasoupRouter);
3679
+ this.emit("mediasoup-router-added", observedMediasoupRouterScope);
3680
+ return observedMediasoupRouter;
3681
+ }
3682
+ close() {
3683
+ if (this.closed) {
3684
+ return logger7.debug("Attempted to close twice");
3685
+ }
3686
+ this.closed = true;
3687
+ this.observedCalls.forEach((call) => call.close());
3688
+ this._notify("observer-closed", { ...this.eventScope });
3689
+ }
3690
+ accept(sample, context) {
3691
+ if (this.closed) {
3692
+ return this._notify("sample-rejected", { ...this.eventScope, reason: "observer-closed", sample });
3693
+ }
3694
+ try {
3695
+ this.acceptMiddlewares.process({ sample, context });
3696
+ } catch (err) {
3697
+ logger7.warn("An accept middleware threw; dropping the sample. %o", err);
3698
+ }
3699
+ if (!sample.callId) {
3700
+ this._notify("sample-rejected", { ...this.eventScope, reason: "missing-callId", sample });
3701
+ return;
3702
+ }
3703
+ if (!sample.clientId) {
3704
+ this._notify("sample-rejected", { ...this.eventScope, reason: "missing-clientId", sample });
3705
+ return;
3706
+ }
3707
+ let call = this.getObservedCall(sample.callId);
3708
+ if (!call) {
3709
+ call = this.createObservedCall({
3710
+ callId: sample.callId,
3711
+ appData: this.config.createCallAppData?.({ callId: sample.callId, observer: this })
3712
+ });
3713
+ }
3714
+ if (!call) return;
3715
+ let client = call.getObservedClient(sample.clientId);
3716
+ if (!client) {
3717
+ client = call.createObservedClient({
3718
+ clientId: sample.clientId,
3719
+ appData: this.config.createClientAppData?.({ clientId: sample.clientId, observedCall: call })
3720
+ });
3721
+ }
3722
+ if (!client) return;
3723
+ client.accept(sample, context);
3724
+ }
3725
+ update() {
3726
+ if (this.closed) {
3727
+ return;
3728
+ }
3729
+ this.numberOfInboundRtpStreams = 0;
3730
+ this.numberOfOutboundRtpStreams = 0;
3731
+ this.numberOfPeerConnections = 0;
3732
+ this.numberOfDataChannels = 0;
3733
+ this.numberOfClients = 0;
3734
+ this.numberOfClientsUsingTurn = 0;
3735
+ for (const call of this.observedCalls.values()) {
3736
+ this.numberOfInboundRtpStreams += call.numberOfInboundRtpStreams;
3737
+ this.numberOfOutboundRtpStreams += call.numberOfOutboundRtpStreams;
3738
+ this.numberOfPeerConnections += call.numberOfPeerConnections;
3739
+ this.numberOfDataChannels += call.numberOfDataChannels;
3740
+ this.numberOfClients += call.numberOfClients;
3741
+ this.numberOfClientsUsingTurn += call.clientsUsedTurn.size;
3742
+ }
3743
+ this.observedTURN.update();
3744
+ this._notify("observer-updated", { ...this.eventScope });
3745
+ }
3746
+ /** Emit an Observer-bus event. */
3747
+ _notify(type, ...args) {
3748
+ this.emit(type, ...args);
3749
+ }
3750
+ };
3751
+
3752
+ // src/sinks/ClientSampleSink.ts
3753
+ import { EventEmitter as EventEmitter7 } from "events";
3754
+ var ClientSampleSink = class extends EventEmitter7 {
3755
+ };
3756
+
3757
+ // src/sinks/JsonlFileSink.ts
3758
+ import * as fs from "fs";
3759
+ import * as path from "path";
3760
+ var JsonlFileSink = class extends ClientSampleSink {
3761
+ /** The file this sink writes to. Read it (e.g. in a `close` handler) to upload/move the file. */
3762
+ path;
3763
+ _stream;
3764
+ _serializeSample;
3765
+ constructor(options) {
3766
+ super();
3767
+ this.path = options.path;
3768
+ this._stream = fs.createWriteStream(this.path, { flags: "a" });
3769
+ this._stream.on("close", () => this.emit("close"));
3770
+ this._stream.on("finish", () => this.emit("finish"));
3771
+ this._stream.on("drain", () => this.emit("drain"));
3772
+ this._stream.on("error", (err) => this.emit("error", err));
3773
+ this._serializeSample = options.serializeSample ?? ((sample) => JSON.stringify(sample));
3774
+ }
3775
+ write(sample) {
3776
+ return this._stream.write(`${this._serializeSample(sample)}
3777
+ `);
3778
+ }
3779
+ end() {
3780
+ this._stream.end();
3781
+ }
3782
+ };
3783
+ function createJsonlFileSink(options) {
3784
+ return new JsonlFileSink(options);
3785
+ }
3786
+ function createJsonlFileSinkFactory(options) {
3787
+ return ({ clientId, observedCall }) => {
3788
+ const filename = options.getFileName?.({ callId: observedCall.callId, clientId }) ?? `${observedCall.callId}__${clientId}.jsonl`;
3789
+ return createJsonlFileSink({
3790
+ path: path.join(options.directory, filename),
3791
+ serializeSample: options.serializeSample
3792
+ });
3793
+ };
3794
+ }
3795
+
3796
+ // src/sinks/InMemorySink.ts
3797
+ var InMemorySink = class extends ClientSampleSink {
3798
+ constructor(samples = []) {
3799
+ super();
3800
+ this.samples = samples;
3801
+ }
3802
+ samples;
3803
+ write(sample) {
3804
+ this.samples.push(sample);
3805
+ return true;
3806
+ }
3807
+ end() {
3808
+ this.emit("close");
3809
+ }
3810
+ };
3811
+ function createInMemorySink(samples) {
3812
+ return new InMemorySink(samples);
3813
+ }
3814
+
3815
+ // src/utils/RemoteTrackResolver.ts
3816
+ var RemoteTrackResolver = class {
3817
+ constructor(observedCall, resolvers) {
3818
+ this.observedCall = observedCall;
3819
+ this.resolvers = resolvers;
3820
+ const observer = observedCall.observer;
3821
+ const onInboundAdded = (p) => {
3822
+ if (p.observedCall === this.observedCall) this._addInboundTrack(p.observedInboundTrack);
3823
+ };
3824
+ const onInboundRemoved = (p) => {
3825
+ if (p.observedCall === this.observedCall) this._removeInboundTrack(p.observedInboundTrack);
3826
+ };
3827
+ const onOutboundAdded = (p) => {
3828
+ if (p.observedCall === this.observedCall) this._addOutboundTrack(p.observedOutboundTrack);
3829
+ };
3830
+ const onOutboundRemoved = (p) => {
3831
+ if (p.observedCall === this.observedCall) this._removeOutboundTrack(p.observedOutboundTrack);
3832
+ };
3833
+ observedCall.once("close", () => {
3834
+ observer.off("inbound-track-added", onInboundAdded);
3835
+ observer.off("inbound-track-removed", onInboundRemoved);
3836
+ observer.off("outbound-track-added", onOutboundAdded);
3837
+ observer.off("outbound-track-removed", onOutboundRemoved);
3838
+ });
3839
+ observer.on("inbound-track-added", onInboundAdded);
3840
+ observer.on("inbound-track-removed", onInboundRemoved);
3841
+ observer.on("outbound-track-added", onOutboundAdded);
3842
+ observer.on("outbound-track-removed", onOutboundRemoved);
3843
+ }
3844
+ observedCall;
3845
+ resolvers;
3846
+ _publisherIdToOutboundTrack = /* @__PURE__ */ new Map();
3847
+ _subscriberIdToInboundTrack = /* @__PURE__ */ new Map();
3848
+ /** The published (outbound) track for a publisher id, if any. */
3849
+ getOutboundTrackByPublisherId(publisherId) {
3850
+ return this._publisherIdToOutboundTrack.get(publisherId);
3851
+ }
3852
+ /** The subscribed (inbound) track for a subscriber id, if the strategy resolves subscriber ids. */
3853
+ getInboundTrackBySubscriberId(subscriberId) {
3854
+ return this._subscriberIdToInboundTrack.get(subscriberId);
3855
+ }
3856
+ // RemoteTrackResolver interface — thin readers over the links maintained on the tracks.
3857
+ resolveRemoteOutboundTrack(inboundTrack) {
3858
+ return inboundTrack.remoteOutboundTrack;
3859
+ }
3860
+ resolveRemoteInboundTracks(outboundTrack) {
3861
+ return [...outboundTrack.remoteInboundTracks];
3862
+ }
3863
+ _addInboundTrack(inboundTrack) {
3864
+ const publisherId = this.resolvers.resolveInboundTrackPublisherId(inboundTrack);
3865
+ if (!publisherId) return;
3866
+ const subscriberId = this.resolvers.resolveInboundTrackSubscriberId?.(inboundTrack);
3867
+ if (subscriberId) {
3868
+ this._subscriberIdToInboundTrack.set(subscriberId, inboundTrack);
3869
+ }
3870
+ const outboundTrack = this._publisherIdToOutboundTrack.get(publisherId);
3871
+ if (!outboundTrack) return;
3872
+ outboundTrack.remoteInboundTracks.add(inboundTrack);
3873
+ inboundTrack.remoteOutboundTrack = outboundTrack;
3874
+ }
3875
+ _removeInboundTrack(inboundTrack) {
3876
+ inboundTrack.remoteOutboundTrack?.remoteInboundTracks.delete(inboundTrack);
3877
+ inboundTrack.remoteOutboundTrack = void 0;
3878
+ const subscriberId = this.resolvers.resolveInboundTrackSubscriberId?.(inboundTrack);
3879
+ if (subscriberId) {
3880
+ this._subscriberIdToInboundTrack.delete(subscriberId);
3881
+ }
3882
+ }
3883
+ _addOutboundTrack(outboundTrack) {
3884
+ const publisherId = this.resolvers.resolveOutboundTrackPublisherId(outboundTrack);
3885
+ if (!publisherId) return;
3886
+ this._publisherIdToOutboundTrack.set(publisherId, outboundTrack);
3887
+ for (const observedClient of this.observedCall.observedClients.values()) {
3888
+ for (const observedPeerConnection of observedClient.observedPeerConnections.values()) {
3889
+ for (const inboundTrack of observedPeerConnection.observedInboundTracks.values()) {
3890
+ if (this.resolvers.resolveInboundTrackPublisherId(inboundTrack) !== publisherId) continue;
3891
+ outboundTrack.remoteInboundTracks.add(inboundTrack);
3892
+ inboundTrack.remoteOutboundTrack = outboundTrack;
3893
+ }
3894
+ }
3895
+ }
3896
+ }
3897
+ _removeOutboundTrack(outboundTrack) {
3898
+ const publisherId = this.resolvers.resolveOutboundTrackPublisherId(outboundTrack);
3899
+ if (publisherId && this._publisherIdToOutboundTrack.get(publisherId) === outboundTrack) {
3900
+ this._publisherIdToOutboundTrack.delete(publisherId);
3901
+ }
3902
+ for (const inboundTrack of outboundTrack.remoteInboundTracks) {
3903
+ inboundTrack.remoteOutboundTrack = void 0;
3904
+ }
3905
+ outboundTrack.remoteInboundTracks.clear();
3906
+ }
3907
+ };
3908
+
3909
+ // src/utils/RemoteTrackResolverFactories.ts
3910
+ function createDefaultMediasoupRemoteTrackResolverFactory() {
3911
+ return (observedCall) => new RemoteTrackResolver(observedCall, {
3912
+ resolveInboundTrackPublisherId: (inboundTrack) => inboundTrack.attachments?.producerId,
3913
+ resolveInboundTrackSubscriberId: (inboundTrack) => inboundTrack.attachments?.consumerId,
3914
+ resolveOutboundTrackPublisherId: (outboundTrack) => outboundTrack.attachments?.producerId
3915
+ });
3916
+ }
3917
+ function createP2pRemoteTrackResolverFactory() {
3918
+ return (observedCall) => new RemoteTrackResolver(observedCall, {
3919
+ resolveInboundTrackPublisherId: (inboundTrack) => {
3920
+ const ssrc = inboundTrack.getInboundRtp()?.ssrc;
3921
+ return ssrc ? `${ssrc}` : void 0;
3922
+ },
3923
+ resolveInboundTrackSubscriberId: (inboundTrack) => {
3924
+ const ssrc = inboundTrack.getInboundRtp()?.ssrc;
3925
+ return ssrc ? `${ssrc}` : void 0;
3926
+ },
3927
+ resolveOutboundTrackPublisherId: (outboundTrack) => {
3928
+ const rtps = outboundTrack.getOutboundRtps();
3929
+ if (!rtps || rtps.length === 0) return void 0;
3930
+ return rtps.map((rtp) => rtp.ssrc).join(",");
3931
+ }
3932
+ });
3933
+ }
3934
+ export {
3935
+ ClientEventTypes,
3936
+ ClientMetaTypes,
3937
+ ClientSampleSink,
3938
+ Detectors,
3939
+ InMemorySink,
3940
+ JsonlFileSink,
3941
+ ObservedCall,
3942
+ ObservedCertificate,
3943
+ ObservedClient,
3944
+ ObservedCodec,
3945
+ ObservedDataChannel,
3946
+ ObservedIceCandidate,
3947
+ ObservedIceCandidatePair,
3948
+ ObservedIceTransport,
3949
+ ObservedInboundRtp,
3950
+ ObservedInboundTrack,
3951
+ ObservedMediaPlayout,
3952
+ ObservedMediaSource,
3953
+ ObservedMediasoupRouter,
3954
+ ObservedOutboundRtp,
3955
+ ObservedOutboundTrack,
3956
+ ObservedPeerConnection,
3957
+ ObservedPeerConnectionTransport,
3958
+ ObservedRemoteInboundRtp,
3959
+ ObservedRemoteOutboundRtp,
3960
+ Observer,
3961
+ RemoteTrackResolver,
3962
+ createDefaultMediasoupRemoteTrackResolverFactory,
3963
+ createInMemorySink,
3964
+ createJsonlFileSink,
3965
+ createJsonlFileSinkFactory,
3966
+ createLogger,
3967
+ createP2pRemoteTrackResolverFactory,
3968
+ setObserverLogger
3969
+ };
3970
+ //# sourceMappingURL=index.mjs.map