@observertc/observer-js 0.42.8 → 1.0.0-beta.1

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 (299) hide show
  1. package/.prettierrc +6 -6
  2. package/README.md +493 -63
  3. package/lib/ObservedCall.d.ts +63 -43
  4. package/lib/ObservedCall.d.ts.map +1 -1
  5. package/lib/ObservedCall.js +188 -108
  6. package/lib/ObservedCallEventMonitor.d.ts +104 -0
  7. package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
  8. package/lib/ObservedCallEventMonitor.js +314 -0
  9. package/lib/ObservedCallSummary.d.ts +18 -0
  10. package/lib/ObservedCallSummary.d.ts.map +1 -0
  11. package/lib/ObservedCertificate.d.ts +19 -0
  12. package/lib/ObservedCertificate.d.ts.map +1 -0
  13. package/lib/ObservedCertificate.js +38 -0
  14. package/lib/ObservedClient.d.ts +81 -136
  15. package/lib/ObservedClient.d.ts.map +1 -1
  16. package/lib/ObservedClient.js +581 -744
  17. package/lib/ObservedClientEventMonitor.d.ts +91 -0
  18. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  19. package/lib/ObservedClientEventMonitor.js +254 -0
  20. package/lib/ObservedClientSummary.d.ts +23 -0
  21. package/lib/ObservedClientSummary.d.ts.map +1 -0
  22. package/lib/ObservedClientSummary.js +2 -0
  23. package/lib/ObservedCodec.d.ts +22 -0
  24. package/lib/ObservedCodec.d.ts.map +1 -0
  25. package/lib/ObservedCodec.js +45 -0
  26. package/lib/ObservedDataChannel.d.ts +25 -69
  27. package/lib/ObservedDataChannel.d.ts.map +1 -1
  28. package/lib/ObservedDataChannel.js +69 -109
  29. package/lib/ObservedIceCandidate.d.ts +29 -0
  30. package/lib/ObservedIceCandidate.d.ts.map +1 -0
  31. package/lib/ObservedIceCandidate.js +59 -0
  32. package/lib/ObservedIceCandidatePair.d.ts +45 -0
  33. package/lib/ObservedIceCandidatePair.d.ts.map +1 -0
  34. package/lib/ObservedIceCandidatePair.js +127 -0
  35. package/lib/ObservedIceTransport.d.ts +36 -0
  36. package/lib/ObservedIceTransport.d.ts.map +1 -0
  37. package/lib/ObservedIceTransport.js +93 -0
  38. package/lib/ObservedInboundRtp.d.ts +92 -0
  39. package/lib/ObservedInboundRtp.d.ts.map +1 -0
  40. package/lib/ObservedInboundRtp.js +193 -0
  41. package/lib/ObservedInboundTrack.d.ts +32 -0
  42. package/lib/ObservedInboundTrack.d.ts.map +1 -0
  43. package/lib/ObservedInboundTrack.js +60 -0
  44. package/lib/ObservedMediaPlayout.d.ts +22 -0
  45. package/lib/ObservedMediaPlayout.d.ts.map +1 -0
  46. package/lib/ObservedMediaPlayout.js +42 -0
  47. package/lib/ObservedMediaSource.d.ts +28 -0
  48. package/lib/ObservedMediaSource.d.ts.map +1 -0
  49. package/lib/ObservedMediaSource.js +55 -0
  50. package/lib/ObservedOutboundRtp.d.ts +62 -0
  51. package/lib/ObservedOutboundRtp.d.ts.map +1 -0
  52. package/lib/ObservedOutboundRtp.js +144 -0
  53. package/lib/ObservedOutboundTrack.d.ts +32 -0
  54. package/lib/ObservedOutboundTrack.d.ts.map +1 -0
  55. package/lib/ObservedOutboundTrack.js +60 -0
  56. package/lib/ObservedPeerConnection.d.ts +154 -80
  57. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  58. package/lib/ObservedPeerConnection.js +709 -357
  59. package/lib/ObservedPeerConnectionTransport.d.ts +17 -0
  60. package/lib/ObservedPeerConnectionTransport.d.ts.map +1 -0
  61. package/lib/ObservedPeerConnectionTransport.js +34 -0
  62. package/lib/ObservedRemoteInboundRtp.d.ts +30 -0
  63. package/lib/ObservedRemoteInboundRtp.d.ts.map +1 -0
  64. package/lib/ObservedRemoteInboundRtp.js +60 -0
  65. package/lib/ObservedRemoteOutboundRtp.d.ts +30 -0
  66. package/lib/ObservedRemoteOutboundRtp.d.ts.map +1 -0
  67. package/lib/ObservedRemoteOutboundRtp.js +60 -0
  68. package/lib/ObservedTURN.d.ts +31 -0
  69. package/lib/ObservedTURN.d.ts.map +1 -0
  70. package/lib/ObservedTURN.js +58 -0
  71. package/lib/ObservedTurnServer.d.ts +25 -0
  72. package/lib/ObservedTurnServer.d.ts.map +1 -0
  73. package/lib/ObservedTurnServer.js +59 -0
  74. package/lib/Observer.d.ts +49 -54
  75. package/lib/Observer.d.ts.map +1 -1
  76. package/lib/Observer.js +131 -226
  77. package/lib/ObserverEventMonitor.d.ts +138 -0
  78. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  79. package/lib/ObserverEventMonitor.js +433 -0
  80. package/lib/ObserverSummary.d.ts +13 -0
  81. package/lib/ObserverSummary.d.ts.map +1 -0
  82. package/lib/ObserverSummary.js +2 -0
  83. package/lib/common/Middleware.d.ts +10 -1
  84. package/lib/common/Middleware.d.ts.map +1 -1
  85. package/lib/common/Middleware.js +55 -36
  86. package/lib/common/logger.d.ts +8 -4
  87. package/lib/common/logger.d.ts.map +1 -1
  88. package/lib/common/logger.js +44 -68
  89. package/lib/common/types.d.ts +0 -21
  90. package/lib/common/types.d.ts.map +1 -1
  91. package/lib/common/types.js +1 -0
  92. package/lib/common/utils.d.ts +3 -1
  93. package/lib/common/utils.d.ts.map +1 -1
  94. package/lib/common/utils.js +23 -3
  95. package/lib/detectors/Detector.d.ts +5 -0
  96. package/lib/detectors/Detector.d.ts.map +1 -0
  97. package/lib/detectors/Detector.js +3 -0
  98. package/lib/detectors/Detectors.d.ts +11 -0
  99. package/lib/detectors/Detectors.d.ts.map +1 -0
  100. package/lib/detectors/Detectors.js +34 -0
  101. package/lib/index.d.ts +26 -24
  102. package/lib/index.d.ts.map +1 -1
  103. package/lib/index.js +45 -41
  104. package/lib/mediasoup/ObservedMediaRouter.d.ts +10 -0
  105. package/lib/mediasoup/ObservedMediaRouter.d.ts.map +1 -0
  106. package/lib/mediasoup/ObservedMediaRouter.js +6 -0
  107. package/lib/monitors/TurnUsageMonitor.d.ts +0 -52
  108. package/lib/monitors/TurnUsageMonitor.d.ts.map +1 -1
  109. package/lib/monitors/TurnUsageMonitor.js +145 -120
  110. package/lib/schema/ClientEventTypes.d.ts +228 -0
  111. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  112. package/lib/schema/ClientEventTypes.js +41 -0
  113. package/lib/schema/ClientMetaTypes.d.ts +34 -0
  114. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  115. package/lib/schema/ClientMetaTypes.js +16 -0
  116. package/lib/schema/ClientSample.d.ts +1333 -0
  117. package/lib/schema/ClientSample.d.ts.map +1 -0
  118. package/lib/schema/ClientSample.js +4 -0
  119. package/lib/scores/CalculatedScore.d.ts +6 -0
  120. package/lib/scores/CalculatedScore.d.ts.map +1 -0
  121. package/lib/scores/CalculatedScore.js +2 -0
  122. package/lib/scores/DefaultCallScoreCalculator.d.ts +7 -0
  123. package/lib/scores/DefaultCallScoreCalculator.d.ts.map +1 -0
  124. package/lib/scores/DefaultCallScoreCalculator.js +21 -0
  125. package/lib/scores/ScoreCalculator.d.ts +4 -0
  126. package/lib/scores/ScoreCalculator.d.ts.map +1 -0
  127. package/lib/scores/ScoreCalculator.js +2 -0
  128. package/lib/updaters/CallUpdater.d.ts +5 -0
  129. package/lib/updaters/CallUpdater.d.ts.map +1 -0
  130. package/lib/updaters/CallUpdater.js +2 -0
  131. package/lib/updaters/ObserverUpdater.d.ts +5 -0
  132. package/lib/updaters/ObserverUpdater.d.ts.map +1 -0
  133. package/lib/updaters/ObserverUpdater.js +2 -0
  134. package/lib/updaters/OnAllCallObserverUpdater.d.ts +15 -0
  135. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  136. package/lib/updaters/OnAllCallObserverUpdater.js +50 -0
  137. package/lib/updaters/OnAllClientCallUpdater.d.ts +15 -0
  138. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -0
  139. package/lib/updaters/OnAllClientCallUpdater.js +53 -0
  140. package/lib/updaters/OnAnyCallObserverUpdater.d.ts +12 -0
  141. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  142. package/lib/updaters/OnAnyCallObserverUpdater.js +37 -0
  143. package/lib/updaters/OnAnyClientCallUpdater.d.ts +12 -0
  144. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -0
  145. package/lib/updaters/OnAnyClientCallUpdater.js +36 -0
  146. package/lib/updaters/OnIntervalUpdater.d.ts +10 -0
  147. package/lib/updaters/OnIntervalUpdater.d.ts.map +1 -0
  148. package/lib/updaters/OnIntervalUpdater.js +17 -0
  149. package/lib/updaters/Updater.d.ts +6 -0
  150. package/lib/updaters/Updater.d.ts.map +1 -0
  151. package/lib/updaters/Updater.js +2 -0
  152. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  153. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  154. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  155. package/lib/utils/RemoteTrackResolver.d.ts +7 -0
  156. package/lib/utils/RemoteTrackResolver.d.ts.map +1 -0
  157. package/lib/utils/RemoteTrackResolver.js +2 -0
  158. package/package.json +4 -4
  159. package/src/ObservedCall.ts +276 -164
  160. package/src/ObservedCallEventMonitor.ts +392 -0
  161. package/src/ObservedCallSummary.ts +22 -0
  162. package/src/ObservedCertificate.ts +43 -0
  163. package/src/ObservedClient.ts +625 -1006
  164. package/src/ObservedClientEventMonitor.ts +309 -0
  165. package/src/ObservedClientSummary.ts +24 -0
  166. package/src/ObservedCodec.ts +49 -0
  167. package/src/ObservedDataChannel.ts +71 -181
  168. package/src/ObservedIceCandidate.ts +64 -0
  169. package/src/ObservedIceCandidatePair.ts +134 -0
  170. package/src/ObservedIceTransport.ts +99 -0
  171. package/src/ObservedInboundRtp.ts +205 -0
  172. package/src/ObservedInboundTrack.ts +75 -0
  173. package/src/ObservedMediaPlayout.ts +49 -0
  174. package/src/ObservedMediaSource.ts +60 -0
  175. package/src/ObservedOutboundRtp.ts +156 -0
  176. package/src/ObservedOutboundTrack.ts +74 -0
  177. package/src/ObservedPeerConnection.ts +917 -402
  178. package/src/ObservedPeerConnectionTransport.ts +38 -0
  179. package/src/ObservedRemoteInboundRtp.ts +64 -0
  180. package/src/ObservedRemoteOutboundRtp.ts +65 -0
  181. package/src/ObservedTURN.ts +86 -0
  182. package/src/ObservedTurnServer.ts +72 -0
  183. package/src/Observer.ts +182 -298
  184. package/src/ObserverEventMonitor.ts +551 -0
  185. package/src/ObserverSummary.ts +15 -0
  186. package/src/common/Middleware.ts +70 -42
  187. package/src/common/logger.ts +60 -69
  188. package/src/common/types.ts +0 -42
  189. package/src/common/utils.ts +24 -2
  190. package/src/detectors/Detector.ts +6 -0
  191. package/src/detectors/Detectors.ts +38 -0
  192. package/src/index.ts +26 -88
  193. package/src/mediasoup/ObservedMediaRouter.ts +10 -0
  194. package/src/monitors/TurnUsageMonitor.ts +164 -164
  195. package/src/schema/ClientEventTypes.ts +280 -0
  196. package/src/schema/ClientMetaTypes.ts +41 -0
  197. package/src/schema/ClientSample.ts +1682 -0
  198. package/src/scores/CalculatedScore.ts +6 -0
  199. package/src/scores/DefaultCallScoreCalculator.ts +23 -0
  200. package/src/scores/ScoreCalculator.ts +3 -0
  201. package/src/updaters/CallUpdater.ts +5 -0
  202. package/src/updaters/ObserverUpdater.ts +5 -0
  203. package/src/updaters/OnAllCallObserverUpdater.ts +59 -0
  204. package/src/updaters/OnAllClientCallUpdater.ts +61 -0
  205. package/src/updaters/OnAnyCallObserverUpdater.ts +41 -0
  206. package/src/updaters/OnAnyClientCallUpdater.ts +39 -0
  207. package/src/updaters/OnIntervalUpdater.ts +19 -0
  208. package/src/updaters/Updater.ts +5 -0
  209. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  210. package/src/utils/RemoteTrackResolver.ts +12 -0
  211. package/tsconfig.json +3 -3
  212. package/lib/ObservedICE.d.ts +0 -64
  213. package/lib/ObservedICE.d.ts.map +0 -1
  214. package/lib/ObservedICE.js +0 -141
  215. package/lib/ObservedInboundAudioTrack.d.ts +0 -96
  216. package/lib/ObservedInboundAudioTrack.d.ts.map +0 -1
  217. package/lib/ObservedInboundAudioTrack.js +0 -217
  218. package/lib/ObservedInboundVideoTrack.d.ts +0 -101
  219. package/lib/ObservedInboundVideoTrack.d.ts.map +0 -1
  220. package/lib/ObservedInboundVideoTrack.js +0 -232
  221. package/lib/ObservedOutboundAudioTrack.d.ts +0 -87
  222. package/lib/ObservedOutboundAudioTrack.d.ts.map +0 -1
  223. package/lib/ObservedOutboundAudioTrack.js +0 -186
  224. package/lib/ObservedOutboundVideoTrack.d.ts +0 -91
  225. package/lib/ObservedOutboundVideoTrack.d.ts.map +0 -1
  226. package/lib/ObservedOutboundVideoTrack.js +0 -193
  227. package/lib/ObservedSfu.d.ts +0 -59
  228. package/lib/ObservedSfu.d.ts.map +0 -1
  229. package/lib/ObservedSfu.js +0 -157
  230. package/lib/ObservedSfuInboundRtpPad.d.ts +0 -42
  231. package/lib/ObservedSfuInboundRtpPad.d.ts.map +0 -1
  232. package/lib/ObservedSfuInboundRtpPad.js +0 -71
  233. package/lib/ObservedSfuOutboundRtpPad.d.ts +0 -42
  234. package/lib/ObservedSfuOutboundRtpPad.d.ts.map +0 -1
  235. package/lib/ObservedSfuOutboundRtpPad.js +0 -72
  236. package/lib/ObservedSfuSctpChannel.d.ts +0 -42
  237. package/lib/ObservedSfuSctpChannel.d.ts.map +0 -1
  238. package/lib/ObservedSfuSctpChannel.js +0 -57
  239. package/lib/ObservedSfuTransport.d.ts +0 -58
  240. package/lib/ObservedSfuTransport.d.ts.map +0 -1
  241. package/lib/ObservedSfuTransport.js +0 -120
  242. package/lib/RemoteTrackAssigner.d.ts +0 -14
  243. package/lib/RemoteTrackAssigner.d.ts.map +0 -1
  244. package/lib/RemoteTrackAssigner.js +0 -52
  245. package/lib/ReportsCollector.d.ts +0 -109
  246. package/lib/ReportsCollector.d.ts.map +0 -1
  247. package/lib/ReportsCollector.js +0 -207
  248. package/lib/common/CalculatedScore.d.ts +0 -80
  249. package/lib/common/CalculatedScore.d.ts.map +0 -1
  250. package/lib/common/CalculatedScore.js +0 -162
  251. package/lib/common/CallEventType.d.ts +0 -63
  252. package/lib/common/CallEventType.d.ts.map +0 -1
  253. package/lib/common/CallEventType.js +0 -39
  254. package/lib/common/CallMetaReports.d.ts +0 -42
  255. package/lib/common/CallMetaReports.d.ts.map +0 -1
  256. package/lib/common/CallMetaReports.js +0 -36
  257. package/lib/common/QualityScoreUtils.d.ts +0 -1
  258. package/lib/common/QualityScoreUtils.d.ts.map +0 -1
  259. package/lib/common/QualityScoreUtils.js +0 -1
  260. package/lib/common/TypedEventEmitter.d.ts +0 -22
  261. package/lib/common/TypedEventEmitter.d.ts.map +0 -1
  262. package/lib/common/TypedEventEmitter.js +0 -11
  263. package/lib/common/callEventReports.d.ts +0 -10
  264. package/lib/common/callEventReports.d.ts.map +0 -1
  265. package/lib/common/callEventReports.js +0 -112
  266. package/lib/monitors/CallSummary.d.ts +0 -37
  267. package/lib/monitors/CallSummary.d.ts.map +0 -1
  268. package/lib/monitors/CallSummaryMonitor.d.ts +0 -32
  269. package/lib/monitors/CallSummaryMonitor.d.ts.map +0 -1
  270. package/lib/monitors/CallSummaryMonitor.js +0 -152
  271. package/lib/monitors/ClientIssueMonitor.d.ts +0 -32
  272. package/lib/monitors/ClientIssueMonitor.d.ts.map +0 -1
  273. package/lib/monitors/ClientIssueMonitor.js +0 -49
  274. package/lib/monitors/SfuServerMonitor.d.ts +0 -79
  275. package/lib/monitors/SfuServerMonitor.d.ts.map +0 -1
  276. package/lib/monitors/SfuServerMonitor.js +0 -198
  277. package/src/ObservedICE.ts +0 -237
  278. package/src/ObservedInboundAudioTrack.ts +0 -344
  279. package/src/ObservedInboundVideoTrack.ts +0 -361
  280. package/src/ObservedOutboundAudioTrack.ts +0 -307
  281. package/src/ObservedOutboundVideoTrack.ts +0 -319
  282. package/src/ObservedSfu.ts +0 -231
  283. package/src/ObservedSfuInboundRtpPad.ts +0 -106
  284. package/src/ObservedSfuOutboundRtpPad.ts +0 -107
  285. package/src/ObservedSfuSctpChannel.ts +0 -103
  286. package/src/ObservedSfuTransport.ts +0 -185
  287. package/src/RemoteTrackAssigner.ts +0 -68
  288. package/src/ReportsCollector.ts +0 -336
  289. package/src/common/CalculatedScore.ts +0 -188
  290. package/src/common/CallEventType.ts +0 -69
  291. package/src/common/CallMetaReports.ts +0 -82
  292. package/src/common/QualityScoreUtils.ts +0 -0
  293. package/src/common/TypedEventEmitter.ts +0 -32
  294. package/src/common/callEventReports.ts +0 -178
  295. package/src/monitors/CallSummary.ts +0 -38
  296. package/src/monitors/CallSummaryMonitor.ts +0 -192
  297. package/src/monitors/ClientIssueMonitor.ts +0 -88
  298. package/src/monitors/SfuServerMonitor.ts +0 -285
  299. /package/lib/{monitors/CallSummary.js → ObservedCallSummary.js} +0 -0
@@ -1,123 +1,92 @@
1
- import { Browser, ClientSample, Engine, IceLocalCandidate, IceRemoteCandidate, MediaCodecStats, MediaDevice, OperationSystem, Platform } from '@observertc/sample-schemas-js';
2
- import { ObservedCall } from './ObservedCall';
3
1
  import { EventEmitter } from 'events';
4
2
  import { ObservedPeerConnection } from './ObservedPeerConnection';
5
3
  import { createLogger } from './common/logger';
6
- import { CallMetaType, createCallMetaReport } from './common/CallMetaReports';
7
4
  // eslint-disable-next-line camelcase
8
- import { PartialBy, isValidUuid } from './common/utils';
9
- import { CallEventType } from './common/CallEventType';
10
- import { ObservedSfu } from './ObservedSfu';
11
- import { ClientIssue } from './monitors/CallSummary';
12
- import { CallEventReport } from '@observertc/report-schemas-js';
13
- import { CalculatedScore } from './common/CalculatedScore';
5
+ import { ClientEvent, ClientMetaData, ClientSample, PeerConnectionSample, ClientIssue, ExtensionStat } from './schema/ClientSample';
6
+ import * as MetaData from './schema/ClientMetaTypes';
7
+ import { ClientEventTypes } from './schema/ClientEventTypes';
8
+ import { ObservedCall } from './ObservedCall';
9
+ import { ClientMetaTypes } from './schema/ClientMetaTypes';
10
+ import { parseJsonAs } from './common/utils';
11
+ import { CalculatedScore } from './scores/CalculatedScore';
12
+ import { Detectors } from './detectors/Detectors';
14
13
 
15
14
  const logger = createLogger('ObservedClient');
16
15
 
17
- export type ObservedClientModel= {
16
+ export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
18
17
  clientId: string;
19
- mediaUnitId: string;
20
- userId?: string,
21
- marker?: string,
22
- operationSystem?: OperationSystem,
23
- engine?: Engine,
24
- platform?: Platform,
25
- browser?: Browser,
26
- coordinates?: {
27
- latitude: number;
28
- longitude: number;
29
- },
18
+ appData?: AppData;
30
19
  };
31
20
 
32
21
  export type ObservedClientEvents = {
33
- update: [{
34
- sample: ClientSample,
35
- elapsedTimeInMs: number,
36
- }],
37
- close: [],
38
- joined: [],
39
- left: [],
40
- newpeerconnection: [ObservedPeerConnection],
41
- iceconnectionstatechange: [{
42
- peerConnection: ObservedPeerConnection,
43
- state: string,
44
- }],
45
- icegatheringstatechange: [{
46
- peerConnection: ObservedPeerConnection,
47
- state: string,
48
- }],
49
- connectionstatechange: [{
50
- peerConnection: ObservedPeerConnection,
51
- state: string,
52
- }],
53
- selectedcandidatepair: [{
54
- peerConnection: ObservedPeerConnection,
55
- localCandidate: IceLocalCandidate,
56
- remoteCandidate: IceRemoteCandidate,
57
- }],
58
- issue: [ClientIssue],
59
- usingturn: [boolean],
60
- usermediaerror: [string],
61
- rejoined: [{
62
- lastJoined: number,
63
- }],
64
- score: [CalculatedScore],
22
+ update: [sample: ClientSample, elapsedTimeInMs: number];
23
+ close: [];
24
+ joined: [];
25
+ issue: [ClientIssue];
26
+ metaData: [ClientMetaData];
27
+ rejoined: [timestamp: number];
28
+ left: [];
29
+ usingturn: [boolean];
30
+ usermediaerror: [string];
31
+ extensionStats: [ExtensionStat];
32
+ clientEvent: [ClientEvent];
33
+
34
+ newpeerconnection: [ObservedPeerConnection];
65
35
  };
66
36
 
67
- export declare interface ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> {
37
+ export declare interface ObservedClient {
68
38
  on<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
69
39
  off<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
70
40
  once<U extends keyof ObservedClientEvents>(event: U, listener: (...args: ObservedClientEvents[U]) => void): this;
71
41
  emit<U extends keyof ObservedClientEvents>(event: U, ...args: ObservedClientEvents[U]): boolean;
72
- readonly appData: AppData;
73
42
  }
74
43
 
75
- type PendingPeerConnectionTimestamp = {
76
- type: 'opened' | 'closed'
77
- peerConnectionId: string;
78
- timestamp: number;
79
- }
44
+ export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
45
+ public readonly detectors: Detectors;
80
46
 
81
- type PendingMediaTrackTimestamp = {
82
- type: 'added' | 'removed'
83
- peerConnectionId: string;
84
- mediaTrackId: string;
85
- timestamp: number;
86
- }
47
+ public readonly clientId: string;
48
+ public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
49
+ public readonly calculatedScore: CalculatedScore = {
50
+ weight: 1,
51
+ value: undefined,
52
+ };
53
+
54
+ public appData: AppData;
55
+ public attachments?: Record<string, unknown>;
87
56
 
88
- export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
89
-
90
- public readonly created = Date.now();
91
57
  public updated = Date.now();
92
- public sfuId?: string;
93
-
94
- private readonly _peerConnections = new Map<string, ObservedPeerConnection>();
95
-
96
- private _closed = false;
97
-
98
- private _acceptedSamples = 0;
99
- private _timeZoneOffsetInHours?: number;
58
+ public acceptedSamples = 0;
59
+ public closed = false;
100
60
 
61
+ public joinedAt?: number;
62
+ public leftAt?: number;
63
+ public closedAt?: number;
64
+ public lastSampleTimestamp?: number;
101
65
  // the timestamp of the CLIENT_JOINED event
102
- private _joined?: number;
103
- private _left?: number;
104
- public lastStatsTimestamp?: number;
66
+ public operationSystem?: MetaData.OperationSystem;
67
+ public engine?: MetaData.Engine;
68
+ public platform?: MetaData.Platform;
69
+ public browser?: MetaData.Browser;
70
+ public mediaConstraints: string[] = [];
105
71
 
106
- public score?: CalculatedScore;
107
72
  public usingTURN = false;
73
+ public usingTCP = false;
108
74
  public availableOutgoingBitrate = 0;
109
75
  public availableIncomingBitrate = 0;
76
+
110
77
  public totalInboundPacketsLost = 0;
111
78
  public totalInboundPacketsReceived = 0;
112
79
  public totalOutboundPacketsSent = 0;
113
80
  public totalDataChannelBytesSent = 0;
114
81
  public totalDataChannelBytesReceived = 0;
115
- public totalSentBytes = 0;
116
- public totalReceivedBytes = 0;
82
+ public totalDataChannelMessagesSent = 0;
83
+ public totalDataChannelMessagesReceived = 0;
117
84
  public totalReceivedAudioBytes = 0;
118
85
  public totalReceivedVideoBytes = 0;
119
86
  public totalSentAudioBytes = 0;
120
87
  public totalSentVideoBytes = 0;
88
+ public totalSentBytes = 0;
89
+ public totalReceivedBytes = 0;
121
90
 
122
91
  public deltaReceivedAudioBytes = 0;
123
92
  public deltaReceivedVideoBytes = 0;
@@ -125,1045 +94,695 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
125
94
  public deltaSentVideoBytes = 0;
126
95
  public deltaDataChannelBytesSent = 0;
127
96
  public deltaDataChannelBytesReceived = 0;
97
+ public deltaDataChannelMessagesSent = 0;
98
+ public deltaDataChannelMessagesReceived = 0;
128
99
  public deltaInboundPacketsLost = 0;
129
100
  public deltaInboundPacketsReceived = 0;
130
101
  public deltaOutboundPacketsSent = 0;
131
-
132
- public avgRttInMs?: number;
133
- public outboundAudioBitrate?: number;
134
- public outboundVideoBitrate?: number;
135
- public inboundAudioBitrate?: number;
136
- public inboundVideoBitrate?: number;
137
- public mediaConstraints: string[] = [];
138
-
139
- public readonly mediaDevices: MediaDevice[] = [];
140
- public readonly mediaCodecs: MediaCodecStats[] = [];
141
- public readonly userMediaErrors: string[] = [];
102
+ public deltaTransportSentBytes = 0;
103
+ public deltaTransportReceivedBytes = 0;
104
+ public deltaRttLt50Measurements = 0;
105
+ public deltaRttLt150Measurements = 0;
106
+ public deltaRttLt300Measurements = 0;
107
+ public deltaRttGtOrEq300Measurements = 0;
108
+
109
+ public currentMaxRttInMs?: number;
110
+ public currentMinRttInMs?: number;
111
+ public currentAvgRttInMs?: number;
112
+
113
+ public sendingAudioBitrate = 0;
114
+ public sendingVideoBitrate = 0;
115
+ public receivingAudioBitrate = 0;
116
+ public receivingVideoBitrate = 0;
117
+
118
+ public numberOfInboundRtpStreams = 0;
119
+ public numberOfInbundTracks = 0;
120
+ public numberOfOutboundRtpStreams = 0;
121
+ public numberOfOutboundTracks = 0;
122
+ public numberOfDataChannels = 0;
123
+
124
+ public totalRttLt50Measurements = 0;
125
+ public totalRttLt150Measurements = 0;
126
+ public totalRttLt300Measurements = 0;
127
+ public totalRttGtOrEq300Measurements = 0;
128
+ public deltaNumberOfIssues = 0;
129
+
130
+ public totalScoreSum = 0;
131
+ public numberOfScoreMeasurements = 0;
132
+ public totalNumberOfIssues = 0;
133
+
134
+ public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
142
135
  public issues: ClientIssue[] = [];
143
136
 
144
- public ωpendingPeerConnectionTimestamps: PendingPeerConnectionTimestamp[] = [];
145
- public ωpendingMediaTrackTimestamps: PendingMediaTrackTimestamp[] = [];
146
-
147
- public constructor(
148
- private readonly _model: ObservedClientModel,
149
- public readonly call: ObservedCall,
150
- public readonly appData: AppData
151
- ) {
137
+ private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
138
+
139
+ public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
152
140
  super();
153
141
  this.setMaxListeners(Infinity);
154
- }
155
-
156
- public getSfu<T extends Record<string, unknown> = Record<string, unknown>>(): ObservedSfu<T> | undefined {
157
- const sfu = this.call.observer.observedSfus.get(this.sfuId ?? '');
158
-
159
- if (!sfu) return;
160
-
161
- return sfu as ObservedSfu<T>;
162
- }
163
-
164
- public get joined() {
165
- return this._joined;
166
- }
167
-
168
- public set joined(value: number | undefined) {
169
- this._joined = value;
170
-
171
- if (this._joined) {
172
- this.emit('joined');
173
- }
174
- }
175
-
176
- public get left() {
177
- return this._left;
178
- }
179
-
180
- public set left(value: number | undefined) {
181
- this._left = value;
182
-
183
- if (this._left) {
184
- this.emit('left');
185
- }
186
- }
187
142
 
188
- public get sfu(): ObservedSfu | undefined {
189
- if (!this.sfuId) return;
143
+ this.clientId = settings.clientId;
144
+ this.appData = settings.appData ?? {} as AppData;
190
145
 
191
- return this.call.observer.observedSfus.get(this.sfuId);
146
+ this.detectors = new Detectors();
192
147
  }
193
-
194
- public get coordinates() {
195
- return this._model.coordinates;
196
- }
197
-
198
- public set coordinates(value: { latitude: number; longitude: number } | undefined) {
199
- this._model.coordinates = value;
200
- }
201
-
202
- public get clientId(): string {
203
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
204
- return this._model.clientId!;
205
- }
206
-
207
- public get roomId() {
208
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
209
- return this.call.roomId!;
210
- }
211
-
212
- public get serviceId() {
213
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
214
- return this.call.serviceId!;
215
- }
216
-
217
- public get callId() {
218
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
219
- return this.call.callId!;
220
- }
221
-
222
- public get mediaUnitId() {
223
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
224
- return this._model.mediaUnitId!;
225
- }
226
-
227
- public get reports() {
228
- return this.call.reports;
229
- }
230
-
231
- public get userId() {
232
- return this._model.userId;
233
- }
234
-
235
- public set userId(userId: string | undefined) {
236
- this._model.userId = userId;
237
- }
238
-
239
- public get timeZoneOffsetInHours() {
240
- return this._timeZoneOffsetInHours;
241
- }
242
-
243
- public get marker() {
244
- return this._model.marker;
148
+
149
+ public get numberOfPeerConnections() {
150
+ return this.observedPeerConnections.size;
245
151
  }
246
152
 
247
- public get browser() {
248
- return this._model.browser;
153
+ public get score() {
154
+ return this.calculatedScore.value;
249
155
  }
250
156
 
251
- public get engine() {
252
- return this._model.engine;
253
- }
157
+ public close() {
158
+ if (this.closed) return;
159
+ this.closed = true;
254
160
 
255
- public get operationSystem() {
256
- return this._model.operationSystem;
257
- }
161
+ this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
162
+ this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
163
+ this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
164
+ this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
258
165
 
259
- public get platform() {
260
- return this._model.platform;
261
- }
166
+ Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
167
+ if (!this.leftAt) {
168
+ this.leftAt = this.lastSampleTimestamp;
262
169
 
263
- public get acceptedSamples() {
264
- return this._acceptedSamples;
265
- }
170
+ if (this.leftAt) {
171
+ this.emit('left');
172
+ }
173
+ }
174
+ this.closedAt = Date.now();
266
175
 
267
- public get closed() {
268
- return this._closed;
176
+ this.emit('close');
269
177
  }
270
178
 
271
- public get peerConnections(): ReadonlyMap<string, ObservedPeerConnection> {
272
- return this._peerConnections;
273
- }
274
-
275
- public close() {
276
- if (this._closed) return;
277
- this._closed = true;
179
+ public accept(sample: ClientSample): void {
180
+ if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
278
181
 
279
- Array.from(this._peerConnections.values()).forEach((peerConnection) => peerConnection.close());
182
+ const now = Date.now();
183
+ const elapsedInMs = now - this.updated;
184
+ const elapsedInSeconds = elapsedInMs / 1000;
185
+ let sumOfRtts = 0;
186
+ let numberOfRttMeasurements = 0;
187
+
188
+ ++this.acceptedSamples;
189
+
190
+ this.availableIncomingBitrate = 0;
191
+ this.availableOutgoingBitrate = 0;
192
+ this.deltaDataChannelBytesReceived = 0;
193
+ this.deltaDataChannelBytesSent = 0;
194
+ this.deltaDataChannelMessagesReceived = 0;
195
+ this.deltaDataChannelMessagesSent = 0;
196
+ this.deltaInboundPacketsLost = 0;
197
+ this.deltaInboundPacketsReceived = 0;
198
+ this.deltaOutboundPacketsSent = 0;
199
+ this.deltaReceivedAudioBytes = 0;
200
+ this.deltaReceivedVideoBytes = 0;
201
+ this.deltaSentAudioBytes = 0;
202
+ this.deltaSentVideoBytes = 0;
203
+ this.deltaTransportReceivedBytes = 0;
204
+ this.deltaTransportSentBytes = 0;
205
+ this.deltaRttLt50Measurements = 0;
206
+ this.deltaRttLt150Measurements = 0;
207
+ this.deltaRttLt300Measurements = 0;
208
+ this.deltaRttGtOrEq300Measurements = 0;
209
+ this.deltaNumberOfIssues = 0;
210
+
211
+ this.numberOfDataChannels = 0;
212
+ this.numberOfInboundRtpStreams = 0;
213
+ this.numberOfInbundTracks = 0;
214
+ this.numberOfOutboundRtpStreams = 0;
215
+ this.numberOfOutboundTracks = 0;
216
+ this.usingTURN = false;
217
+ this.usingTCP = false;
218
+ this.currentMinRttInMs = undefined;
219
+ this.currentMaxRttInMs = undefined;
280
220
 
281
- this.emit('close');
282
- }
221
+ this._mergeInjections(sample);
283
222
 
284
- public addEventReport(params: PartialBy<Omit<CallEventReport, 'serviceId' | 'roomId' | 'callId' | 'clientId' | 'userId' | 'marker' | 'attachments'>, 'timestamp'> & { attachments?: Record<string, unknown> }) {
285
- const {
286
- attachments,
287
- ...fields
288
- } = params;
289
-
290
- this.reports.addCallEventReport({
291
- ...fields,
292
- serviceId: this.serviceId,
293
- mediaUnitId: this.mediaUnitId,
294
- roomId: this.roomId,
295
- callId: this.callId,
296
- clientId: this.clientId,
297
- userId: this.userId,
298
- timestamp: params.timestamp ?? Date.now(),
299
- marker: this.marker,
300
- attachments: attachments ? JSON.stringify(attachments) : undefined,
301
- });
302
- }
223
+ const clientEventsPostBuffer: ClientEvent[] = [];
303
224
 
304
- public addExtensionStatsReport(extensionType: string, payload?: Record<string, unknown>) {
305
- this.reports.addClientExtensionReport({
306
- serviceId: this.serviceId,
307
- mediaUnitId: this.mediaUnitId,
308
- roomId: this.roomId,
309
- callId: this.callId,
310
- clientId: this.clientId,
311
- userId: this.userId,
312
- timestamp: Date.now(),
313
- extensionType,
314
- payload: JSON.stringify(payload),
315
- marker: this.marker,
316
- });
317
- }
225
+ for (const clientEvent of sample.clientEvents ?? []) {
226
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
318
227
 
319
- public addIssue(issue: ClientIssue) {
320
- try {
321
-
322
- this.reports.addCallEventReport({
323
- serviceId: this.serviceId,
324
- mediaUnitId: this.mediaUnitId,
325
- roomId: this.roomId,
326
- callId: this.callId,
327
- clientId: this.clientId,
328
- userId: this.userId,
329
-
330
- name: CallEventType.CLIENT_ISSUE,
331
- value: issue.severity,
332
- peerConnectionId: issue.peerConnectionId,
333
- mediaTrackId: issue.trackId,
334
- message: issue.description,
335
- timestamp: issue.timestamp ?? Date.now(),
336
- attachments: issue.attachments ? JSON.stringify(issue.attachments): undefined,
337
- });
338
- } catch (err) {
339
- logger.warn(`Error adding client issue: ${(err as Error)?.message}`);
228
+ this.call.observer.emit('client-event', this, clientEvent);
340
229
  }
341
230
 
342
- this._addAndEmitIssue(issue);
343
- }
344
-
345
- public accept(sample: ClientSample): void {
346
- if (this._closed) throw new Error(`Client ${this.clientId} is closed`);
347
- if (sample.clientId && sample.clientId !== 'NULL' && sample.clientId !== this.clientId) {
348
- throw new Error(`Sample client id (${sample.clientId}) does not match the client id of the observed client (${this.clientId})`);
231
+ for (const metaData of sample.clientMetaItems ?? []) {
232
+ this.addMetadata(metaData);
349
233
  }
350
- const now = Date.now();
351
234
 
352
- ++this._acceptedSamples;
353
- if (0 < this.issues.length) {
354
- // we reset the issues every time we accept a new sample
355
- this.issues = [];
356
- }
357
- for (const peerConnection of this._peerConnections.values()) {
358
- if (peerConnection.closed) continue;
359
- peerConnection.resetMetrics();
360
- }
235
+ for (const issue of sample.clientIssues ?? []) {
236
+ this.addIssue(issue);
361
237
 
362
- if (this._model.userId) {
363
- if (sample.userId && sample.userId !== 'NULL' && sample.userId !== this._model.userId) {
364
- this._model.userId = sample.userId;
365
- }
366
- } else if (sample.userId && sample.userId !== 'NULL') {
367
- this._model.userId = sample.userId;
368
- }
369
-
370
- if (this._model.marker !== sample.marker) {
371
- this._model.marker = sample.marker;
372
- this._peerConnections.forEach((peerConnection) => (peerConnection.marker = sample.marker));
238
+ ++this.deltaNumberOfIssues;
373
239
  }
374
240
 
375
- if (this._timeZoneOffsetInHours !== sample.timeZoneOffsetInHours) {
376
- this._timeZoneOffsetInHours = sample.timeZoneOffsetInHours;
241
+ for (const extensionStat of sample.extensionStats ?? []) {
242
+ this.addExtensionStats(extensionStat);
377
243
  }
378
244
 
379
- if (sample.os && (
380
- this._model.operationSystem?.name !== sample.os.name ||
381
- this._model.operationSystem?.version !== sample.os.version ||
382
- this._model.operationSystem?.versionName !== sample.os.versionName
383
- )) {
384
- this._model.operationSystem = sample.os;
385
-
386
- const callMetaReport = createCallMetaReport(
387
- this.serviceId,
388
- this.mediaUnitId,
389
- this.roomId,
390
- this.callId,
391
- this.clientId,
392
- {
393
- type: CallMetaType.OPERATION_SYSTEM,
394
- payload: sample.os,
395
- },
396
- this.userId,
397
- undefined,
398
- sample.timestamp
399
- );
400
-
401
- this.reports.addCallMetaReport(callMetaReport);
402
- }
245
+ for (const pcSample of sample.peerConnections ?? []) {
246
+ const observedPeerConnection = this._updatePeerConnection(pcSample);
403
247
 
404
- if (sample.engine && (
405
- this._model.engine?.name !== sample.engine.name ||
406
- this._model.engine?.version !== sample.engine.version
407
- )) {
408
- this._model.engine = sample.engine;
409
-
410
- const callMetaReport = createCallMetaReport(
411
- this.serviceId,
412
- this.mediaUnitId,
413
- this.roomId,
414
- this.callId,
415
- this.clientId,
416
- {
417
- type: CallMetaType.ENGINE,
418
- payload: sample.engine,
419
- },
420
- this.userId,
421
- undefined,
422
- sample.timestamp
423
- );
424
-
425
- this.reports.addCallMetaReport(callMetaReport);
426
- }
248
+ if (!observedPeerConnection) continue;
427
249
 
428
- if (sample.platform && (
429
- this._model.platform?.model !== sample.platform.model ||
430
- this._model.platform?.type !== sample.platform.type ||
431
- this._model.platform?.vendor !== sample.platform.vendor
432
- )) {
433
- this._model.platform = sample.platform;
434
-
435
- const callMetaReport = createCallMetaReport(
436
- this.serviceId,
437
- this.mediaUnitId,
438
- this.roomId,
439
- this.callId,
440
- this.clientId,
441
- {
442
- type: CallMetaType.PLATFORM,
443
- payload: sample.platform,
444
- },
445
- this.userId,
446
- undefined,
447
- sample.timestamp
448
- );
449
-
450
- this.reports.addCallMetaReport(callMetaReport);
451
- }
250
+ this.deltaDataChannelBytesReceived += observedPeerConnection.deltaDataChannelBytesReceived;
251
+ this.deltaDataChannelBytesSent += observedPeerConnection.deltaDataChannelBytesSent;
252
+ this.deltaDataChannelMessagesReceived += observedPeerConnection.deltaDataChannelMessagesReceived;
253
+ this.deltaDataChannelMessagesSent += observedPeerConnection.deltaDataChannelMessagesSent;
254
+ this.deltaInboundPacketsLost += observedPeerConnection.deltaInboundPacketsLost;
255
+ this.deltaInboundPacketsReceived += observedPeerConnection.deltaInboundPacketsReceived;
256
+ this.deltaOutboundPacketsSent += observedPeerConnection.deltaOutboundPacketsSent;
257
+ this.deltaReceivedAudioBytes += observedPeerConnection.deltaReceivedAudioBytes;
258
+ this.deltaReceivedVideoBytes += observedPeerConnection.deltaReceivedVideoBytes;
259
+ this.deltaSentAudioBytes += observedPeerConnection.deltaSentAudioBytes;
260
+ this.deltaSentVideoBytes += observedPeerConnection.deltaSentVideoBytes;
261
+ this.deltaTransportReceivedBytes += observedPeerConnection.deltaTransportReceivedBytes;
262
+ this.deltaTransportSentBytes += observedPeerConnection.deltaTransportSentBytes;
263
+
264
+ this.availableIncomingBitrate += observedPeerConnection.availableIncomingBitrate;
265
+ this.availableOutgoingBitrate += observedPeerConnection.availableOutgoingBitrate;
452
266
 
453
- if (sample.browser && (
454
- this._model.browser?.name !== sample.browser.name ||
455
- this._model.browser?.version !== sample.browser.version
456
- )) {
457
- this._model.browser = sample.browser;
458
-
459
- const callMetaReport = createCallMetaReport(
460
- this.serviceId,
461
- this.mediaUnitId,
462
- this.roomId,
463
- this.callId,
464
- this.clientId,
465
- {
466
- type: CallMetaType.BROWSER,
467
- payload: sample.browser,
468
- },
469
- this.userId,
470
- undefined,
471
- sample.timestamp
472
- );
473
-
474
- this.reports.addCallMetaReport(callMetaReport);
475
- }
267
+ this.numberOfDataChannels += observedPeerConnection.observedDataChannels.size;
268
+ this.numberOfInbundTracks += observedPeerConnection.observedInboundTracks.size;
269
+ this.numberOfOutboundRtpStreams += observedPeerConnection.observedOutboundRtps.size;
270
+ this.numberOfOutboundTracks += observedPeerConnection.observedOutboundTracks.size;
271
+ this.numberOfInboundRtpStreams += observedPeerConnection.observedInboundRtps.size;
476
272
 
477
- for (const mediaConstraint of sample.mediaConstraints ?? []) {
478
- const callMetaReport = createCallMetaReport(
479
- this.serviceId,
480
- this.mediaUnitId,
481
- this.roomId,
482
- this.callId,
483
- this.clientId,
484
- {
485
- type: CallMetaType.MEDIA_CONSTRAINT,
486
- payload: mediaConstraint,
487
- },
488
- this.userId,
489
- undefined,
490
- sample.timestamp
491
- );
492
-
493
- this.reports.addCallMetaReport(callMetaReport);
494
- this.mediaConstraints.push(mediaConstraint);
495
- }
273
+ if (observedPeerConnection.usingTURN) {
274
+ this.usingTURN = true;
275
+ }
276
+ if (observedPeerConnection.usingTCP) {
277
+ this.usingTCP = true;
278
+ }
279
+
280
+ if (observedPeerConnection.currentRttInMs) {
281
+ if (this.currentMinRttInMs === undefined || observedPeerConnection.currentRttInMs < this.currentMinRttInMs) {
282
+ this.currentMinRttInMs = observedPeerConnection.currentRttInMs;
283
+ }
284
+ if (this.currentMaxRttInMs === undefined || observedPeerConnection.currentRttInMs > this.currentMaxRttInMs) {
285
+ this.currentMaxRttInMs = observedPeerConnection.currentRttInMs;
286
+ }
287
+ if (observedPeerConnection.currentRttInMs < 50) {
288
+ this.deltaRttLt50Measurements += 1;
289
+ } else if (observedPeerConnection.currentRttInMs < 150) {
290
+ this.deltaRttLt150Measurements += 1;
291
+ } else if (observedPeerConnection.currentRttInMs < 300) {
292
+ this.deltaRttLt300Measurements += 1;
293
+ } else if (300 <= observedPeerConnection.currentRttInMs) {
294
+ this.deltaRttGtOrEq300Measurements += 1;
295
+ }
496
296
 
497
- for (const localSDP of sample.localSDPs ?? []) {
498
- const callMetaReport = createCallMetaReport(
499
- this.serviceId,
500
- this.mediaUnitId,
501
- this.roomId,
502
- this.callId,
503
- this.clientId,
504
- {
505
- type: CallMetaType.LOCAL_SDP,
506
- payload: localSDP,
507
- },
508
- this.userId,
509
- undefined,
510
- sample.timestamp
511
- );
512
-
513
- this.reports.addCallMetaReport(callMetaReport);
297
+ sumOfRtts += observedPeerConnection.currentRttInMs;
298
+ ++numberOfRttMeasurements;
299
+ }
514
300
  }
515
301
 
516
- for (const extensionStats of sample.extensionStats ?? []) {
517
- this.reports.addClientExtensionReport({
518
- serviceId: this.serviceId,
519
- mediaUnitId: this.mediaUnitId,
520
- roomId: this.roomId,
521
- callId: this.callId,
522
- clientId: this.clientId,
523
- userId: this.userId,
524
- timestamp: now,
525
- payload: extensionStats.payload,
526
- extensionType: extensionStats.type,
527
- });
302
+ for (const clientEvent of clientEventsPostBuffer) {
303
+ this._processClientEvent(clientEvent);
528
304
  }
529
305
 
530
- for (const { timestamp, ...callEvent } of sample.customCallEvents ?? []) {
531
- switch (callEvent.name) {
532
- case CallEventType.CLIENT_JOINED: {
533
- const lastJoined = this.joined;
306
+ // emit new attachments?
307
+ this.attachments = sample.attachments;
534
308
 
535
- this.joined = timestamp;
309
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
310
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
311
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
312
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
313
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
314
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
315
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
316
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
317
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
318
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
319
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
320
+ this.totalReceivedBytes += this.deltaTransportReceivedBytes;
321
+ this.totalSentBytes += this.deltaTransportSentBytes;
322
+ this.totalRttLt50Measurements += this.deltaRttLt50Measurements;
323
+ this.totalRttLt150Measurements += this.deltaRttLt150Measurements;
324
+ this.totalRttLt300Measurements += this.deltaRttLt300Measurements;
325
+ this.totalRttGtOrEq300Measurements += this.deltaRttGtOrEq300Measurements;
326
+ this.totalNumberOfIssues += this.deltaNumberOfIssues;
327
+
328
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (elapsedInSeconds);
329
+ this.receivingVideoBitrate = (this.totalReceivedVideoBytes * 8) / (elapsedInSeconds);
330
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / (elapsedInSeconds);
331
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / (elapsedInSeconds);
332
+ this.currentAvgRttInMs = 0 < numberOfRttMeasurements ? sumOfRtts / numberOfRttMeasurements : undefined;
333
+
334
+ this.calculatedScore.value = sample.score;
335
+ this.detectors.update();
336
+
337
+ this.lastSampleTimestamp = sample.timestamp;
338
+ // emit update
339
+ this.emit('update',
340
+ sample,
341
+ now - this.updated,
342
+ );
343
+ this.updated = now;
536
344
 
537
- // if it is joined before and it is joined again
538
- if (lastJoined && this.joined && lastJoined !== this.joined) {
539
- this.emit('rejoined', { lastJoined });
345
+ // if result changed after update
346
+ if (this.calculatedScore.value) {
347
+ this.totalScoreSum += this.calculatedScore.value;
348
+ ++this.numberOfScoreMeasurements;
349
+ }
350
+ }
351
+
352
+ private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
353
+ // eslint-disable-next-line no-console
354
+ // console.warn('ClientEvent', event);
355
+ switch (event.type) {
356
+ case ClientEventTypes.CLIENT_JOINED: {
357
+ if (event.timestamp) {
358
+ if (!this.joinedAt) {
359
+ this.joinedAt = event.timestamp;
360
+ this.emit('joined');
361
+ } else if (this.joinedAt < event.timestamp) {
362
+ this.emit('rejoined', event.timestamp);
363
+ } else {
364
+ this.joinedAt = event.timestamp;
365
+ logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
540
366
  }
541
- // in case we have a left event before the joined event
542
- if (this.left && this.joined && this.left < this.joined) {
543
- this.left = undefined;
544
- }
545
- break;
546
- }
547
- case CallEventType.CLIENT_LEFT: {
548
- this.left = timestamp;
549
- break;
550
367
  }
551
- case CallEventType.PEER_CONNECTION_OPENED: {
552
- callEvent.peerConnectionId && this.ωpendingPeerConnectionTimestamps.push({
553
- type: 'opened',
554
- peerConnectionId: callEvent.peerConnectionId,
555
- timestamp: timestamp ?? sample.timestamp,
556
- });
557
- break;
368
+ logger.debug('Client %s joined at %o', this.clientId, event);
369
+ break;
370
+ }
371
+ case ClientEventTypes.CLIENT_LEFT: {
372
+ if (event.timestamp) {
373
+ if (!this.leftAt) {
374
+ this.leftAt = event.timestamp;
375
+ this.emit('left');
376
+ } else {
377
+ logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
378
+ }
379
+
558
380
  }
559
- case CallEventType.PEER_CONNECTION_CLOSED: {
560
- callEvent.peerConnectionId && this.ωpendingPeerConnectionTimestamps.push({
561
- type: 'closed',
562
- peerConnectionId: callEvent.peerConnectionId,
563
- timestamp: timestamp ?? sample.timestamp,
564
- });
565
- break;
381
+ logger.debug('Client %s left at %o', this.clientId, event);
382
+ break;
383
+ }
384
+ case ClientEventTypes.PEER_CONNECTION_OPENED: {
385
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
386
+
387
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
388
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
389
+
390
+ if (observedPeerConnection) {
391
+ observedPeerConnection.openedAt = event.timestamp;
392
+ } else if (postBuffer) {
393
+ postBuffer.push(event);
394
+ } else {
395
+ logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o', event);
396
+ }
397
+
566
398
  }
567
- case CallEventType.MEDIA_TRACK_ADDED: {
568
- callEvent.peerConnectionId && callEvent.mediaTrackId && this.ωpendingMediaTrackTimestamps.push({
569
- type: 'added',
570
- peerConnectionId: callEvent.peerConnectionId,
571
- mediaTrackId: callEvent.mediaTrackId,
572
- timestamp: timestamp ?? sample.timestamp,
573
- });
574
- break;
399
+ break;
400
+ }
401
+ case ClientEventTypes.PEER_CONNECTION_CLOSED: {
402
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
403
+
404
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
405
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
406
+
407
+ if (observedPeerConnection) {
408
+ observedPeerConnection.closedAt = event.timestamp;
409
+ } else if (postBuffer) {
410
+ postBuffer.push(event);
411
+ } else {
412
+ logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o', event);
413
+ }
575
414
  }
576
- case CallEventType.MEDIA_TRACK_REMOVED: {
577
- callEvent.peerConnectionId && callEvent.mediaTrackId && this.ωpendingMediaTrackTimestamps.push({
578
- type: 'removed',
579
- peerConnectionId: callEvent.peerConnectionId,
580
- mediaTrackId: callEvent.mediaTrackId,
581
- timestamp: timestamp ?? sample.timestamp,
582
- });
583
- break;
415
+ break;
416
+ }
417
+ case ClientEventTypes.MEDIA_TRACK_ADDED: {
418
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
419
+
420
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
421
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
422
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
423
+
424
+ if (observedTrack) {
425
+ observedTrack.addedAt = event.timestamp;
426
+ } else if (postBuffer) {
427
+ postBuffer.push(event);
428
+ } else {
429
+ logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o', event);
430
+ }
584
431
  }
585
- case CallEventType.CLIENT_ISSUE: {
586
- const severity = callEvent.value ? callEvent.value as ClientIssue['severity'] : 'minor';
587
-
588
- try {
589
- const issue: ClientIssue = {
590
- severity,
591
- timestamp: timestamp ?? Date.now(),
592
- description: callEvent.message,
593
- peerConnectionId: callEvent.peerConnectionId,
594
- trackId: callEvent.mediaTrackId,
595
- attachments: callEvent.attachments ? JSON.parse(callEvent.attachments) : undefined,
596
- };
597
-
598
- this._addAndEmitIssue(issue);
599
- } catch (err) {
600
- logger.warn(`Error parsing client issue: ${(err as Error)?.message}`);
432
+ break;
433
+ }
434
+ case ClientEventTypes.MEDIA_TRACK_REMOVED: {
435
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
436
+
437
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
438
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
439
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
440
+
441
+ if (observedTrack) {
442
+ observedTrack.removedAt = event.timestamp;
443
+ } else if (postBuffer) {
444
+ postBuffer.push(event);
445
+ } else {
446
+ logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o', event);
601
447
  }
602
- break;
603
448
  }
449
+ break;
604
450
  }
605
-
606
- this.reports.addCallEventReport({
607
- serviceId: this.serviceId,
608
- mediaUnitId: this.mediaUnitId,
609
- roomId: this.roomId,
610
- callId: this.callId,
611
- clientId: this.clientId,
612
- userId: this.userId,
613
- timestamp: timestamp ?? now,
614
- ...callEvent,
615
- });
616
-
617
- this.call.emit('callevent', {
618
- mediaUnitId: this.mediaUnitId,
619
- clientId: this.clientId,
620
- userId: this.userId,
621
- timestamp: timestamp ?? now,
622
- ...callEvent,
623
- });
624
-
625
- }
626
-
627
- for (const userMediaError of sample.userMediaErrors ?? []) {
628
- const callMetaReport = createCallMetaReport(
629
- this.serviceId,
630
- this.mediaUnitId,
631
- this.roomId,
632
- this.callId,
633
- this.clientId, {
634
- type: CallMetaType.USER_MEDIA_ERROR,
635
- payload: userMediaError,
636
- },
637
- this.userId,
638
- );
639
-
640
- this.reports.addCallMetaReport(callMetaReport);
641
- this.userMediaErrors.push(userMediaError);
642
- this.emit('usermediaerror', userMediaError);
643
- }
644
-
645
- for (const certificate of sample.certificates ?? []) {
646
- const callMetaReport = createCallMetaReport(
647
- this.serviceId,
648
- this.mediaUnitId,
649
- this.roomId,
650
- this.callId,
651
- this.clientId, {
652
- type: CallMetaType.CERTIFICATE,
653
- payload: certificate,
654
- },
655
- this.userId
656
- );
657
-
658
- this.reports.addCallMetaReport(callMetaReport);
659
- }
660
-
661
- for (const codec of sample.codecs ?? []) {
662
- const callMetaReport = createCallMetaReport(
663
- this.serviceId,
664
- this.mediaUnitId,
665
- this.roomId,
666
- this.callId,
667
- this.clientId, {
668
- type: CallMetaType.CODEC,
669
- payload: codec,
670
- }, this.userId);
671
-
672
- this.reports.addCallMetaReport(callMetaReport);
673
- if (codec.mimeType && !this.mediaCodecs.find((c) => c.mimeType === codec.mimeType)) {
674
- this.mediaCodecs.push(codec);
451
+ case ClientEventTypes.DATA_CHANNEL_OPEN: {
452
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
453
+
454
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
455
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
456
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
457
+
458
+ if (observedDataChannel) {
459
+ observedDataChannel.addedAt = event.timestamp;
460
+ } else if (postBuffer) {
461
+ postBuffer.push(event);
462
+ } else {
463
+ logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
464
+ }
465
+ }
466
+ break;
675
467
  }
676
- }
677
-
678
- for (const iceServer of sample.iceServers ?? []) {
679
- const callMetaReport = createCallMetaReport(
680
- this.serviceId,
681
- this.mediaUnitId,
682
- this.roomId,
683
- this.callId,
684
- this.clientId, {
685
- type: CallMetaType.ICE_SERVER,
686
- payload: iceServer,
687
- }, this.userId);
688
-
689
- this.reports.addCallMetaReport(callMetaReport);
690
- }
691
-
692
- for (const mediaDevice of sample.mediaDevices ?? []) {
693
- const callMetaReport = createCallMetaReport(
694
- this.serviceId,
695
- this.mediaUnitId,
696
- this.roomId,
697
- this.callId,
698
- this.clientId, {
699
- type: CallMetaType.MEDIA_DEVICE,
700
- payload: mediaDevice,
701
- }, this.userId);
702
-
703
- this.reports.addCallMetaReport(callMetaReport);
704
- if (mediaDevice.id && !this.mediaDevices.find((d) => d.id === mediaDevice.id)) {
705
- this.mediaDevices.push(mediaDevice);
468
+ case ClientEventTypes.DATA_CHANNEL_CLOSED: {
469
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
470
+
471
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
472
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
473
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
474
+
475
+ if (observedDataChannel) {
476
+ observedDataChannel.removedAt = event.timestamp;
477
+ } else if (postBuffer) {
478
+ postBuffer.push(event);
479
+ } else {
480
+ logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
481
+ }
482
+ }
483
+ break;
706
484
  }
707
- }
708
-
709
- for (const mediaSource of sample.mediaSources ?? []) {
710
- const callMetaReport = createCallMetaReport(
711
- this.serviceId,
712
- this.mediaUnitId,
713
- this.roomId,
714
- this.callId,
715
- this.clientId, {
716
- type: CallMetaType.MEDIA_SOURCE,
717
- payload: mediaSource,
718
- }, this.userId);
719
-
720
- this.reports.addCallMetaReport(callMetaReport);
721
- }
722
-
723
- for (const transport of sample.pcTransports ?? []) {
724
- try {
725
- const peerConnection = this._peerConnections.get(transport.transportId) ?? this._createPeerConnection(transport.peerConnectionId);
485
+ case ClientEventTypes.MEDIA_TRACK_MUTED: {
486
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
487
+
488
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
489
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
490
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
491
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
492
+
493
+ if (observedPeerConnection) {
494
+ if (observedInboundTrack) {
495
+ observedInboundTrack.muted = true;
496
+ observedPeerConnection?.emit('muted-inbound-track', observedInboundTrack);
497
+ } else if (observedOutboundTrack) {
498
+ observedOutboundTrack.muted = true;
499
+ observedPeerConnection?.emit('muted-outbound-track', observedOutboundTrack);
500
+ }
501
+ } else if (postBuffer) {
502
+ postBuffer.push(event);
503
+ } else {
504
+ logger.warn('Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
505
+ }
506
+ }
726
507
 
727
- !peerConnection.label && transport.label && (peerConnection.label = transport.label);
728
- peerConnection.update(transport, sample.timestamp);
729
- } catch (err) {
730
- logger.error(`Error creating peer connection: ${(err as Error)?.message}`);
508
+ break;
731
509
  }
732
-
733
- }
734
-
735
- for (const track of sample.inboundAudioTracks ?? []) {
736
- if (!track.peerConnectionId || !track.trackId) {
737
- logger.warn(`InboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
738
-
739
- continue;
510
+ case ClientEventTypes.MEDIA_TRACK_UNMUTED: {
511
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
512
+
513
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
514
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
515
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
516
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
517
+
518
+ if (observedPeerConnection) {
519
+ if (observedInboundTrack) {
520
+ observedInboundTrack.muted = false;
521
+ observedPeerConnection?.emit('unmuted-inbound-track', observedInboundTrack);
522
+ } else if (observedOutboundTrack) {
523
+ observedOutboundTrack.muted = false;
524
+ observedPeerConnection?.emit('unmuted-outbound-track', observedOutboundTrack);
525
+ }
526
+ } else if (postBuffer) {
527
+ postBuffer.push(event);
528
+ } else {
529
+ logger.warn('Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
530
+ }
531
+ }
532
+ break;
740
533
  }
534
+ case ClientEventTypes.ICE_CONNECTION_STATE_CHANGED: {
535
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
741
536
 
742
- try {
743
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
537
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceConnectionState && typeof payload.iceConnectionState === 'string') {
538
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
744
539
 
745
- if (!peerConnection) continue;
746
-
747
- const inboundAudioTrack = peerConnection.inboundAudioTracks.get(track.trackId) ?? peerConnection.createInboundAudioTrack({
748
- trackId: track.trackId,
749
- sfuStreamId: track.sfuStreamId,
750
- sfuSinkId: track.sfuSinkId,
751
- });
752
-
753
- inboundAudioTrack.update(track, sample.timestamp);
754
-
755
- if (!inboundAudioTrack.remoteOutboundTrack) {
756
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundAudioTrack.get(inboundAudioTrack.sfuStreamId ?? '');
757
-
758
- if (remoteOutboundTrack) {
759
- inboundAudioTrack.remoteOutboundTrack = remoteOutboundTrack;
760
-
761
- inboundAudioTrack.once('close', () => {
762
- remoteOutboundTrack?.remoteInboundTracks.delete(inboundAudioTrack.trackId ?? '');
540
+ if (observedPeerConnection) {
541
+ observedPeerConnection.iceConnectionState = payload.iceConnectionState;
542
+ observedPeerConnection.emit('iceconnectionstatechange', {
543
+ state: payload.iceConnectionState,
763
544
  });
764
- remoteOutboundTrack.remoteInboundTracks.set(inboundAudioTrack.trackId ?? '', inboundAudioTrack);
545
+ } else if (postBuffer) {
546
+ postBuffer.push(event);
547
+ } else {
548
+ logger.warn('Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
765
549
  }
766
550
  }
767
- } catch (err) {
768
- logger.error(`Error creating inbound audio track: ${(err as Error)?.message}`);
551
+ break;
769
552
  }
770
- }
553
+ case ClientEventTypes.ICE_GATHERING_STATE_CHANGED: {
554
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
771
555
 
772
- for (const track of sample.inboundVideoTracks ?? []) {
773
- if (!track.peerConnectionId || !track.trackId) {
774
- logger.warn(`InboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
556
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceGatheringState && typeof payload.iceGatheringState === 'string') {
557
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
775
558
 
776
- continue;
777
- }
778
- if (isValidUuid(track.trackId) === false) {
779
- // mediasoup-probator trackId is not a valid UUID, no need to warn about it
780
- if (track.trackId === 'probator') continue;
781
- logger.warn(`InboundVideoTrack with invalid trackId: ${track.trackId}`);
782
- continue;
559
+ if (observedPeerConnection) {
560
+ observedPeerConnection.iceGatheringState = payload.iceGatheringState;
561
+ observedPeerConnection.emit('icegatheringstatechange', {
562
+ state: payload.iceGatheringState,
563
+ });
564
+ } else if (postBuffer) {
565
+ postBuffer.push(event);
566
+ } else {
567
+ logger.warn('Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
568
+ }
569
+ }
570
+ break;
783
571
  }
572
+ case ClientEventTypes.PEER_CONNECTION_STATE_CHANGED: {
573
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
784
574
 
785
- try {
786
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
787
-
788
- if (!peerConnection) continue;
789
-
790
- const inboundVideoTrack = peerConnection.inboundVideoTracks.get(track.trackId) ?? peerConnection.createInboundVideoTrack({
791
- trackId: track.trackId,
792
- sfuStreamId: track.sfuStreamId,
793
- sfuSinkId: track.sfuSinkId,
794
- });
795
-
796
- inboundVideoTrack.update(track, sample.timestamp);
575
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.peerConnectionState && typeof payload.peerConnectionState === 'string') {
576
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
797
577
 
798
- if (!inboundVideoTrack.remoteOutboundTrack) {
799
- const remoteOutboundTrack = this.call.sfuStreamIdToOutboundVideoTrack.get(inboundVideoTrack.sfuStreamId ?? '');
800
-
801
- if (remoteOutboundTrack) {
802
- inboundVideoTrack.remoteOutboundTrack = remoteOutboundTrack;
803
-
804
- inboundVideoTrack.once('close', () => {
805
- remoteOutboundTrack?.remoteInboundTracks.delete(inboundVideoTrack.trackId ?? '');
578
+ if (observedPeerConnection) {
579
+ observedPeerConnection.connectionState = payload.peerConnectionState;
580
+ observedPeerConnection.emit('connectionstatechange', {
581
+ state: payload.peerConnectionState,
806
582
  });
807
- remoteOutboundTrack.remoteInboundTracks.set(inboundVideoTrack.trackId ?? '', inboundVideoTrack);
583
+ } else if (postBuffer) {
584
+ postBuffer.push(event);
585
+ } else {
586
+ logger.warn('Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
808
587
  }
809
588
  }
810
- } catch (err) {
811
- logger.error(`Error creating inbound video track: ${(err as Error)?.message}`);
589
+ break;
812
590
  }
813
591
  }
592
+
593
+ this.emit('clientEvent', event);
594
+ }
595
+
596
+ public injectMetaData(metaData: ClientMetaData) {
597
+ if (this.closed) return;
598
+
599
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
814
600
 
815
- for (const track of sample.outboundAudioTracks ?? []) {
816
- if (!track.peerConnectionId || !track.trackId) {
817
- logger.warn(`OutboundAudioTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
601
+ this._injections.clientMetaItems.push(metaData);
602
+ }
818
603
 
819
- continue;
820
- }
604
+ public injectEvent(event: ClientEvent) {
605
+ if (this.closed) return;
821
606
 
822
- try {
823
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
607
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
824
608
 
825
- if (!peerConnection) continue;
826
-
827
- const outboundAudioTrack = peerConnection.outboundAudioTracks.get(track.trackId) ?? peerConnection.createOutboundAudioTrack({
828
- trackId: track.trackId,
829
- sfuStreamId: track.sfuStreamId,
830
- });
609
+ this._injections.clientEvents.push(event);
610
+ }
831
611
 
832
- outboundAudioTrack.update(track, sample.timestamp);
612
+ public injectIssue(issue: ClientIssue) {
613
+ if (this.closed) return;
833
614
 
834
- if (outboundAudioTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundAudioTrack.has(outboundAudioTrack.sfuStreamId)) {
835
- this.call.sfuStreamIdToOutboundAudioTrack.set(outboundAudioTrack.sfuStreamId, outboundAudioTrack);
836
- }
615
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
837
616
 
838
- } catch (err) {
839
- logger.error(`Error creating outbound audio track: ${(err as Error)?.message}`);
840
- }
841
-
842
- }
617
+ this._injections.clientIssues.push(issue);
618
+ }
843
619
 
844
- for (const track of sample.outboundVideoTracks ?? []) {
845
- if (!track.peerConnectionId || !track.trackId) {
846
- logger.warn(`OutboundVideoTrack without peerConnectionId or trackId: ${JSON.stringify(track)}`);
620
+ public injectExtensionStat(stat: ExtensionStat) {
621
+ if (this.closed) return;
847
622
 
848
- continue;
849
- }
623
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
850
624
 
851
- try {
852
- const peerConnection = this._peerConnections.get(track.peerConnectionId) ?? this._createPeerConnection(track.peerConnectionId);
625
+ this._injections.extensionStats.push(stat);
626
+ }
853
627
 
854
- if (!peerConnection) continue;
855
-
856
- const outboundVideoTrack = peerConnection.outboundVideoTracks.get(track.trackId) ?? peerConnection.createOutboundVideoTrack({
857
- trackId: track.trackId,
858
- sfuStreamId: track.sfuStreamId,
859
- });
860
-
861
- outboundVideoTrack.update(track, sample.timestamp);
628
+ public injectAttachment(key: string, value: unknown) {
629
+ if (this.closed) return;
862
630
 
863
- if (outboundVideoTrack.sfuStreamId && !this.call.sfuStreamIdToOutboundVideoTrack.has(outboundVideoTrack.sfuStreamId)) {
864
- this.call.sfuStreamIdToOutboundVideoTrack.set(outboundVideoTrack.sfuStreamId, outboundVideoTrack);
865
- }
631
+ if (!this._injections.attachments) this._injections.attachments = {};
866
632
 
867
- } catch (err) {
868
- logger.error(`Error creating outbound video track: ${(err as Error)?.message}`);
869
- }
870
- }
633
+ this._injections.attachments[key] = value;
634
+
635
+ }
871
636
 
872
- for (const iceLocalCandidate of sample.iceLocalCandidates ?? []) {
873
- if (!iceLocalCandidate.peerConnectionId) {
874
- logger.warn(`Local ice candidate without peerConnectionId: ${JSON.stringify(iceLocalCandidate)}`);
875
- continue;
637
+ public addMetadata(metadata: ClientMetaData) {
638
+ if (this.closed) return;
639
+
640
+ switch (metadata.type) {
641
+ case ClientMetaTypes.BROWSER: {
642
+ this.browser = parseJsonAs(metadata.payload);
643
+ break;
876
644
  }
877
- const peerConnection = this._peerConnections.get(iceLocalCandidate.peerConnectionId);
878
-
879
- if (!peerConnection) {
880
- logger.debug(`Peer connection ${iceLocalCandidate.peerConnectionId} not found for ice local candidate ${iceLocalCandidate.id}`);
881
- continue;
645
+ case ClientMetaTypes.ENGINE: {
646
+ this.engine = parseJsonAs(metadata.payload);
647
+ break;
882
648
  }
883
-
884
- peerConnection.ICE.addLocalCandidate(iceLocalCandidate, sample.timestamp);
885
- }
886
-
887
- for (const iceRemoteCandidate of sample.iceRemoteCandidates ?? []) {
888
- if (!iceRemoteCandidate.peerConnectionId) {
889
- logger.warn(`Remote ice candidate without peerConnectionId: ${JSON.stringify(iceRemoteCandidate)}`);
890
- continue;
649
+ case ClientMetaTypes.PLATFORM: {
650
+ this.platform = parseJsonAs(metadata.payload);
651
+ break;
891
652
  }
892
- const peerConnection = this._peerConnections.get(iceRemoteCandidate.peerConnectionId);
893
-
894
- if (!peerConnection) {
895
- logger.debug(`Peer connection ${iceRemoteCandidate.peerConnectionId} not found for ice remote candidate ${iceRemoteCandidate.id}`);
896
- continue;
653
+ case ClientMetaTypes.OPERATION_SYSTEM: {
654
+ this.operationSystem = parseJsonAs(metadata.payload);
655
+ break;
897
656
  }
898
-
899
- peerConnection.ICE.addRemoteCandidate(iceRemoteCandidate, sample.timestamp);
900
657
  }
901
658
 
902
- for (const candidatePair of sample.iceCandidatePairs ?? []) {
903
- const peerConnection = this._peerConnections.get(candidatePair.peerConnectionId);
659
+ this.call.observer.emit('client-metadata', this, metadata);
660
+ }
904
661
 
905
- if (!peerConnection) {
906
- logger.debug(`Peer connection ${candidatePair.peerConnectionId} not found for ice candidate pair ${candidatePair.localCandidateId}, ${candidatePair.remoteCandidateId}`);
662
+ public addIssue(issue: ClientIssue) {
663
+ if (this.closed) return;
907
664
 
908
- continue;
909
- }
665
+ this.emit('issue', issue);
666
+ this.call.observer.emit('client-issue', this, issue);
667
+ }
910
668
 
911
- peerConnection.ICE.update(candidatePair, sample.timestamp);
912
- }
669
+ public addExtensionStats(stats: ExtensionStat) {
670
+ this.call.observer.emit('client-extension-stats', this, stats);
671
+ this.emit('extensionStats', stats);
672
+ }
913
673
 
914
- for (const dataChannel of sample.dataChannels ?? []) {
915
- if (!dataChannel.peerConnectionId || !dataChannel.dataChannelIdentifier) {
916
- logger.warn(`DataChannel without peerConnectionId or dataChannelIdentifier: ${JSON.stringify(dataChannel)}`);
674
+ private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
675
+ let observedPeerConnection = this.observedPeerConnections.get(sample.peerConnectionId);
917
676
 
918
- continue;
677
+ if (!observedPeerConnection) {
678
+ if (!sample.peerConnectionId) {
679
+ return (logger.warn(
680
+ `ObservedClient received an invalid PeerConnectionSample (missing peerConnectionId field). ClientId: ${this.clientId}, CallId: ${this.call.callId}`,
681
+ sample
682
+ ), void 0);
919
683
  }
920
684
 
921
- try {
922
- const peerConnection = this._peerConnections.get(dataChannel.peerConnectionId) ?? this._createPeerConnection(dataChannel.peerConnectionId);
685
+ observedPeerConnection = new ObservedPeerConnection(sample.peerConnectionId, this);
923
686
 
924
- if (!peerConnection) continue;
925
-
926
- const observedDataChannel = peerConnection.dataChannels.get(dataChannel.dataChannelIdentifier) ?? peerConnection.createDataChannel(dataChannel.dataChannelIdentifier);
927
-
928
- observedDataChannel.update(dataChannel, sample.timestamp);
929
- } catch (err) {
930
- logger.error(`Error creating data channel: ${(err as Error)?.message}`);
931
- }
687
+ observedPeerConnection.once('close', () => {
688
+ this.observedPeerConnections.delete(sample.peerConnectionId);
689
+ });
690
+ this.observedPeerConnections.set(sample.peerConnectionId, observedPeerConnection);
691
+
692
+ this.emit('newpeerconnection', observedPeerConnection);
932
693
  }
933
694
 
934
- // try to set the timestamps of the peer connections
935
- if (0 < this.ωpendingPeerConnectionTimestamps.length) {
936
- const newPendingTimestamps: PendingPeerConnectionTimestamp[] = [];
695
+ observedPeerConnection.accept(sample);
937
696
 
938
- for (const pendingTimestamp of this.ωpendingPeerConnectionTimestamps) {
939
- const peerConnection = this._peerConnections.get(pendingTimestamp.peerConnectionId);
697
+ return observedPeerConnection;
698
+ }
940
699
 
941
- if (!peerConnection) {
942
- newPendingTimestamps.push(pendingTimestamp);
943
- continue;
944
- }
945
- if (pendingTimestamp.type === 'opened') peerConnection.opened = pendingTimestamp.timestamp;
946
- else if (pendingTimestamp.type === 'closed') peerConnection.closedTimestamp = pendingTimestamp.timestamp;
947
- }
700
+ private _mergeInjections(sample: ClientSample): ClientSample {
701
+ if (this.closed) return sample;
948
702
 
949
- this.ωpendingPeerConnectionTimestamps = newPendingTimestamps;
703
+ if (this._injections.clientEvents) {
704
+ if (!sample.clientEvents) sample.clientEvents = [];
705
+ sample.clientEvents.push(...this._injections.clientEvents);
706
+
707
+ this._injections.clientEvents = undefined;
950
708
  }
951
-
952
- // try to set the timestamps of the media tracks
953
- if (0 < this.ωpendingMediaTrackTimestamps.length) {
954
- const newPendingTimestamps: PendingMediaTrackTimestamp[] = [];
955
-
956
- for (const pendingTimestamp of this.ωpendingMediaTrackTimestamps) {
957
- const peerConnection = this._peerConnections.get(pendingTimestamp.peerConnectionId);
958
- const mediaTrack = peerConnection?.inboundAudioTracks.get(pendingTimestamp.mediaTrackId) ??
959
- peerConnection?.inboundVideoTracks.get(pendingTimestamp.mediaTrackId) ??
960
- peerConnection?.outboundAudioTracks.get(pendingTimestamp.mediaTrackId) ??
961
- peerConnection?.outboundVideoTracks.get(pendingTimestamp.mediaTrackId);
962
-
963
- if (!mediaTrack) {
964
- newPendingTimestamps.push(pendingTimestamp);
965
- continue;
966
- }
967
-
968
- if (pendingTimestamp.type === 'added') mediaTrack.added = pendingTimestamp.timestamp;
969
- else if (pendingTimestamp.type === 'removed') mediaTrack.removed = pendingTimestamp.timestamp;
970
- }
971
709
 
972
- this.ωpendingMediaTrackTimestamps = newPendingTimestamps;
710
+ if (this._injections.clientIssues) {
711
+ if (!sample.clientIssues) sample.clientIssues = [];
712
+ sample.clientIssues.push(...this._injections.clientIssues);
713
+
714
+ this._injections.clientIssues = undefined;
973
715
  }
974
716
 
975
- // close resources that are not visited
976
- for (const peerConnection of this._peerConnections.values()) {
977
- if (!peerConnection.visited) {
978
- peerConnection.close();
979
-
980
- continue;
981
- }
982
-
983
- for (const track of peerConnection.inboundAudioTracks.values()) {
984
- if (!track.visited) {
985
- track.close();
986
-
987
- continue;
988
- }
989
-
990
- track.visited = false;
991
- }
992
- for (const track of peerConnection.inboundVideoTracks.values()) {
993
- if (!track.visited) {
994
- track.close();
995
-
996
- continue;
997
- }
998
-
999
- track.visited = false;
1000
- }
1001
- for (const track of peerConnection.outboundAudioTracks.values()) {
1002
- if (!track.visited) {
1003
- track.close();
1004
-
1005
- continue;
1006
- }
1007
-
1008
- track.visited = false;
1009
- }
1010
- for (const dataChannel of peerConnection.dataChannels.values()) {
1011
- if (!dataChannel.visited) {
1012
- dataChannel.close();
1013
-
1014
- continue;
1015
- }
1016
-
1017
- dataChannel.visited = false;
1018
- }
1019
-
1020
- peerConnection.visited = false;
717
+ if (this._injections.extensionStats) {
718
+ if (!sample.extensionStats) sample.extensionStats = [];
719
+ sample.extensionStats.push(...this._injections.extensionStats);
720
+
721
+ this._injections.extensionStats = undefined;
1021
722
  }
1022
723
 
1023
- // update metrics
1024
- const wasUsingTURN = this.usingTURN;
1025
- const elapsedTimeInMs = now - this.updated;
1026
-
1027
- this.usingTURN = false;
1028
- this.availableIncomingBitrate = 0;
1029
- this.availableOutgoingBitrate = 0;
1030
- this.deltaInboundPacketsLost = 0;
1031
- this.deltaInboundPacketsReceived = 0;
1032
- this.deltaOutboundPacketsSent = 0;
1033
- this.deltaReceivedAudioBytes = 0;
1034
- this.deltaReceivedVideoBytes = 0;
1035
- this.deltaSentAudioBytes = 0;
1036
- this.deltaSentVideoBytes = 0;
1037
- this.deltaDataChannelBytesReceived = 0;
1038
- this.deltaDataChannelBytesSent = 0;
1039
-
1040
- this.outboundAudioBitrate = 0;
1041
- this.outboundVideoBitrate = 0;
1042
- this.inboundAudioBitrate = 0;
1043
- this.inboundVideoBitrate = 0;
1044
- let sumRttInMs = 0;
1045
- let anyPeerConnectionUsingTurn = false;
1046
-
1047
- let minPcScore: number | undefined;
1048
- let maxPcScore: number | undefined;
1049
- let numberOfScoredPeerConnections = 0;
1050
-
1051
- for (const peerConnection of this._peerConnections.values()) {
1052
- if (peerConnection.closed) continue;
1053
- peerConnection.updateMetrics();
1054
- this.deltaInboundPacketsLost += peerConnection.deltaInboundPacketsLost;
1055
- this.deltaInboundPacketsReceived += peerConnection.deltaInboundPacketsReceived;
1056
- this.deltaOutboundPacketsSent += peerConnection.deltaOutboundPacketsSent;
1057
- this.deltaReceivedAudioBytes += peerConnection.deltaReceivedAudioBytes;
1058
- this.deltaReceivedVideoBytes += peerConnection.deltaReceivedVideoBytes;
1059
- this.deltaSentAudioBytes += peerConnection.deltaSentAudioBytes;
1060
- this.deltaSentVideoBytes += peerConnection.deltaSentVideoBytes;
1061
- this.deltaDataChannelBytesReceived += peerConnection.deltaDataChannelBytesReceived;
1062
- this.deltaDataChannelBytesSent += peerConnection.deltaDataChannelBytesSent;
1063
-
1064
- this.availableIncomingBitrate += peerConnection.availableIncomingBitrate ?? 0;
1065
- this.availableOutgoingBitrate += peerConnection.availableOutgoingBitrate ?? 0;
1066
-
1067
- sumRttInMs += peerConnection.avgRttInMs ?? 0;
1068
-
1069
- anyPeerConnectionUsingTurn ||= peerConnection.usingTURN;
1070
-
1071
- if (peerConnection.score) {
1072
- if (minPcScore === undefined || peerConnection.score.score < minPcScore) minPcScore = peerConnection.score.score;
1073
- if (maxPcScore === undefined || peerConnection.score.score > maxPcScore) maxPcScore = peerConnection.score.score;
1074
-
1075
- ++numberOfScoredPeerConnections;
1076
- }
724
+ if (this._injections.attachments) {
725
+ if (!sample.attachments) sample.attachments = {};
726
+ Object.assign(sample.attachments, this._injections.attachments);
727
+
728
+ this._injections.attachments = undefined;
1077
729
  }
1078
730
 
1079
- this.score = {
1080
- score: minPcScore ?? -1,
1081
- remarks: [ {
1082
- severity: 'none',
1083
- text: `Min and max score of all peer connections: ${minPcScore}, ${maxPcScore}, number of PeerConnections with scores: ${numberOfScoredPeerConnections}`,
1084
- } ],
1085
- timestamp: sample.timestamp,
1086
- };
1087
- this.emit('score', this.score);
1088
-
1089
- this.usingTURN = anyPeerConnectionUsingTurn === true;
1090
-
1091
- if (wasUsingTURN !== this.usingTURN) this.emit('usingturn', this.usingTURN);
1092
-
1093
- this.totalSentBytes += this.deltaSentAudioBytes + this.deltaSentVideoBytes;
1094
- this.totalReceivedBytes += this.deltaReceivedAudioBytes + this.deltaReceivedVideoBytes;
1095
- this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
1096
- this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
1097
- this.totalSentAudioBytes += this.deltaSentAudioBytes;
1098
- this.totalSentVideoBytes += this.deltaSentVideoBytes;
1099
- this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
1100
- this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
1101
- this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
1102
- this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
1103
- this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
731
+ if (this._injections.clientMetaItems) {
732
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
733
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
734
+
735
+ this._injections.clientMetaItems = undefined;
736
+ }
737
+
738
+ return sample;
739
+ }
740
+
741
+ // public resetSummaryMetrics() {
742
+ // this.totalDataChannelBytesReceived = 0;
743
+ // this.totalDataChannelBytesSent = 0;
744
+ // this.totalDataChannelMessagesReceived = 0;
745
+ // this.totalDataChannelMessagesSent = 0;
746
+ // this.totalInboundPacketsLost = 0;
747
+ // this.totalInboundPacketsReceived = 0;
748
+ // this.totalOutboundPacketsSent = 0;
749
+ // this.totalReceivedAudioBytes = 0;
750
+ // this.totalReceivedVideoBytes = 0;
751
+ // this.totalSentAudioBytes = 0;
752
+ // this.totalSentVideoBytes = 0;
753
+ // this.totalSentBytes = 0;
754
+ // this.totalReceivedBytes = 0;
1104
755
 
1105
- this.outboundAudioBitrate = (this.deltaSentAudioBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
1106
- this.outboundVideoBitrate = (this.deltaSentVideoBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
1107
- this.inboundAudioBitrate = (this.deltaReceivedAudioBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
1108
- this.inboundVideoBitrate = (this.deltaReceivedVideoBytes * 8) / (Math.max(elapsedTimeInMs, 1) / 1000);
1109
-
1110
- this.avgRttInMs = this._peerConnections.size ? sumRttInMs / this._peerConnections.size : undefined;
756
+ // this.totalNumberOfIssues = 0;
1111
757
 
1112
- // to make sure when sample is emitted it can be associated to this client
1113
- sample.clientId = this.clientId;
1114
-
1115
- this.updated = now;
1116
- this.lastStatsTimestamp = sample.timestamp;
1117
- this.emit('update', {
1118
- sample,
1119
- elapsedTimeInMs,
1120
- });
1121
- }
1122
-
1123
- private _addAndEmitIssue(issue: ClientIssue) {
1124
- this.issues.push(issue);
1125
-
1126
- if (issue.peerConnectionId) {
1127
- const peerConnection = this._peerConnections.get(issue.peerConnectionId);
1128
-
1129
- if (peerConnection) {
1130
-
1131
- if (issue.trackId) {
1132
- const track = peerConnection.inboundAudioTracks.get(issue.trackId) ?? peerConnection.inboundVideoTracks.get(issue.trackId);
1133
-
1134
- if (track) track.ωpendingIssuesForScores.push(issue);
1135
- } else {
1136
- peerConnection.ωpendingIssuesForScores.push(issue);
1137
- }
1138
- }
1139
- }
1140
-
1141
- this.emit('issue', issue);
1142
- }
1143
-
1144
- private _createPeerConnection(peerConnectionId: string, label?: string): ObservedPeerConnection {
1145
- const result = new ObservedPeerConnection({
1146
- peerConnectionId,
1147
- label,
1148
- }, this);
1149
-
1150
- const onNewSelectedCandidatePair = ({ localCandidate, remoteCandidate }: { localCandidate: IceLocalCandidate, remoteCandidate: IceRemoteCandidate }) => {
1151
- this.emit('selectedcandidatepair', {
1152
- peerConnection: result,
1153
- localCandidate,
1154
- remoteCandidate,
1155
- });
1156
- };
1157
-
1158
- result.once('close', () => {
1159
- result.ICE.off('new-selected-candidate-pair', onNewSelectedCandidatePair);
1160
- this._peerConnections.delete(peerConnectionId);
1161
- });
1162
- result.ICE.on('new-selected-candidate-pair', onNewSelectedCandidatePair);
1163
- this._peerConnections.set(peerConnectionId, result);
1164
-
1165
- this.emit('newpeerconnection', result);
758
+ // this.totalScoreSum = 0;
759
+ // this.numberOfScoreMeasurements = 0;
760
+ // }
761
+
762
+ // public createSummary(): ObservedClientSummary {
763
+ // return {
764
+ // totalRttLt50Measurements: this.totalRttLt50Measurements,
765
+ // totalRttLt150Measurements: this.totalRttLt150Measurements,
766
+ // totalRttLt300Measurements: this.totalRttLt300Measurements,
767
+ // totalRttGtOrEq300Measurements: this.totalRttGtOrEq300Measurements,
768
+ // totalDataChannelBytesReceived: this.totalDataChannelBytesReceived,
769
+ // totalDataChannelBytesSent: this.totalDataChannelBytesSent,
770
+ // totalDataChannelMessagesReceived: this.totalDataChannelMessagesReceived,
771
+ // totalDataChannelMessagesSent: this.totalDataChannelMessagesSent,
772
+ // totalInboundPacketsLost: this.totalInboundPacketsLost,
773
+ // totalInboundPacketsReceived: this.totalInboundPacketsReceived,
774
+ // totalOutboundPacketsSent: this.totalOutboundPacketsSent,
775
+ // totalReceivedAudioBytes: this.totalReceivedAudioBytes,
776
+ // totalReceivedVideoBytes: this.totalReceivedVideoBytes,
777
+ // totalSentAudioBytes: this.totalSentAudioBytes,
778
+ // totalSentVideoBytes: this.totalSentVideoBytes,
779
+ // totalSentBytes: this.totalSentBytes,
780
+ // totalReceivedBytes: this.totalReceivedBytes,
781
+
782
+ // numberOfIssues: this.totalNumberOfIssues,
1166
783
 
1167
- return result;
1168
- }
1169
- }
784
+ // totalScoreSum: this.totalScoreSum,
785
+ // numberOfScoreMeasurements: this.numberOfScoreMeasurements,
786
+ // };
787
+ // }
788
+ }