@observertc/observer-js 0.42.9 → 1.0.0-beta.2

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