@observertc/observer-js 0.42.9 → 1.0.1-57a1811.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/.prettierrc +6 -6
  2. package/README.md +0 -104
  3. package/lib/ObservedCall.d.ts +38 -41
  4. package/lib/ObservedCall.d.ts.map +1 -1
  5. package/lib/ObservedCall.js +97 -109
  6. package/lib/ObservedCertificate.d.ts +19 -0
  7. package/lib/ObservedCertificate.d.ts.map +1 -0
  8. package/lib/ObservedCertificate.js +38 -0
  9. package/lib/ObservedClient.d.ts +49 -127
  10. package/lib/ObservedClient.d.ts.map +1 -1
  11. package/lib/ObservedClient.js +201 -750
  12. package/lib/ObservedCodec.d.ts +22 -0
  13. package/lib/ObservedCodec.d.ts.map +1 -0
  14. package/lib/ObservedCodec.js +45 -0
  15. package/lib/ObservedDataChannel.d.ts +23 -69
  16. package/lib/ObservedDataChannel.d.ts.map +1 -1
  17. package/lib/ObservedDataChannel.js +67 -109
  18. package/lib/ObservedIceCandidate.d.ts +29 -0
  19. package/lib/ObservedIceCandidate.d.ts.map +1 -0
  20. package/lib/ObservedIceCandidate.js +59 -0
  21. package/lib/ObservedIceCandidatePair.d.ts +44 -0
  22. package/lib/ObservedIceCandidatePair.d.ts.map +1 -0
  23. package/lib/ObservedIceCandidatePair.js +111 -0
  24. package/lib/ObservedIceTransport.d.ts +32 -0
  25. package/lib/ObservedIceTransport.d.ts.map +1 -0
  26. package/lib/ObservedIceTransport.js +65 -0
  27. package/lib/ObservedInboundRtp.d.ts +92 -0
  28. package/lib/ObservedInboundRtp.d.ts.map +1 -0
  29. package/lib/ObservedInboundRtp.js +193 -0
  30. package/lib/ObservedInboundTrack.d.ts +29 -0
  31. package/lib/ObservedInboundTrack.d.ts.map +1 -0
  32. package/lib/ObservedInboundTrack.js +57 -0
  33. package/lib/ObservedMediaPlayout.d.ts +22 -0
  34. package/lib/ObservedMediaPlayout.d.ts.map +1 -0
  35. package/lib/ObservedMediaPlayout.js +42 -0
  36. package/lib/ObservedMediaSource.d.ts +28 -0
  37. package/lib/ObservedMediaSource.d.ts.map +1 -0
  38. package/lib/ObservedMediaSource.js +55 -0
  39. package/lib/ObservedOutboundRtp.d.ts +62 -0
  40. package/lib/ObservedOutboundRtp.d.ts.map +1 -0
  41. package/lib/ObservedOutboundRtp.js +144 -0
  42. package/lib/ObservedOutboundTrack.d.ts +29 -0
  43. package/lib/ObservedOutboundTrack.d.ts.map +1 -0
  44. package/lib/ObservedOutboundTrack.js +57 -0
  45. package/lib/ObservedPeerConnection.d.ts +128 -78
  46. package/lib/ObservedPeerConnection.d.ts.map +1 -1
  47. package/lib/ObservedPeerConnection.js +624 -357
  48. package/lib/ObservedPeerConnectionTransport.d.ts +17 -0
  49. package/lib/ObservedPeerConnectionTransport.d.ts.map +1 -0
  50. package/lib/ObservedPeerConnectionTransport.js +34 -0
  51. package/lib/ObservedRemoteInboundRtp.d.ts +30 -0
  52. package/lib/ObservedRemoteInboundRtp.d.ts.map +1 -0
  53. package/lib/ObservedRemoteInboundRtp.js +60 -0
  54. package/lib/ObservedRemoteOutboundRtp.d.ts +30 -0
  55. package/lib/ObservedRemoteOutboundRtp.d.ts.map +1 -0
  56. package/lib/ObservedRemoteOutboundRtp.js +60 -0
  57. package/lib/ObservedTURN.d.ts +31 -0
  58. package/lib/ObservedTURN.d.ts.map +1 -0
  59. package/lib/ObservedTURN.js +58 -0
  60. package/lib/ObservedTurnServer.d.ts +25 -0
  61. package/lib/ObservedTurnServer.d.ts.map +1 -0
  62. package/lib/ObservedTurnServer.js +59 -0
  63. package/lib/Observer.d.ts +31 -51
  64. package/lib/Observer.d.ts.map +1 -1
  65. package/lib/Observer.js +75 -229
  66. package/lib/common/Middleware.d.ts +10 -1
  67. package/lib/common/Middleware.d.ts.map +1 -1
  68. package/lib/common/Middleware.js +55 -36
  69. package/lib/common/logger.d.ts +8 -4
  70. package/lib/common/logger.d.ts.map +1 -1
  71. package/lib/common/logger.js +44 -68
  72. package/lib/common/types.d.ts +0 -21
  73. package/lib/common/types.d.ts.map +1 -1
  74. package/lib/common/types.js +1 -0
  75. package/lib/common/utils.d.ts +1 -0
  76. package/lib/common/utils.d.ts.map +1 -1
  77. package/lib/common/utils.js +13 -1
  78. package/lib/detectors/Detector.d.ts +5 -0
  79. package/lib/detectors/Detector.d.ts.map +1 -0
  80. package/lib/detectors/Detector.js +3 -0
  81. package/lib/detectors/Detectors.d.ts +11 -0
  82. package/lib/detectors/Detectors.d.ts.map +1 -0
  83. package/lib/detectors/Detectors.js +34 -0
  84. package/lib/index.d.ts +24 -25
  85. package/lib/index.d.ts.map +1 -1
  86. package/lib/index.js +41 -41
  87. package/lib/monitors/TurnUsageMonitor.d.ts +0 -52
  88. package/lib/monitors/TurnUsageMonitor.d.ts.map +1 -1
  89. package/lib/monitors/TurnUsageMonitor.js +145 -120
  90. package/lib/schema/ClientEventTypes.d.ts +33 -0
  91. package/lib/schema/ClientEventTypes.d.ts.map +1 -0
  92. package/lib/schema/ClientEventTypes.js +38 -0
  93. package/lib/schema/ClientMetaTypes.d.ts +34 -0
  94. package/lib/schema/ClientMetaTypes.d.ts.map +1 -0
  95. package/lib/schema/ClientMetaTypes.js +16 -0
  96. package/lib/schema/ClientSample.d.ts +1333 -0
  97. package/lib/schema/ClientSample.d.ts.map +1 -0
  98. package/lib/schema/ClientSample.js +4 -0
  99. package/lib/scores/CalculatedScore.d.ts +6 -0
  100. package/lib/scores/CalculatedScore.d.ts.map +1 -0
  101. package/lib/scores/DefaultCallScoreCalculator.d.ts +7 -0
  102. package/lib/scores/DefaultCallScoreCalculator.d.ts.map +1 -0
  103. package/lib/scores/DefaultCallScoreCalculator.js +21 -0
  104. package/lib/scores/ScoreCalculator.d.ts +4 -0
  105. package/lib/scores/ScoreCalculator.d.ts.map +1 -0
  106. package/lib/scores/ScoreCalculator.js +2 -0
  107. package/lib/utils/CallUpdater.d.ts +5 -0
  108. package/lib/utils/CallUpdater.d.ts.map +1 -0
  109. package/lib/utils/CallUpdater.js +2 -0
  110. package/lib/utils/OnAllClientCallUpdater.d.ts +16 -0
  111. package/lib/utils/OnAllClientCallUpdater.d.ts.map +1 -0
  112. package/lib/utils/OnAllClientCallUpdater.js +58 -0
  113. package/lib/utils/RemoteTrackResolver.d.ts +7 -0
  114. package/lib/utils/RemoteTrackResolver.d.ts.map +1 -0
  115. package/lib/utils/RemoteTrackResolver.js +2 -0
  116. package/lib/utils/onAnyClientCallUpdater.d.ts +14 -0
  117. package/lib/utils/onAnyClientCallUpdater.d.ts.map +1 -0
  118. package/lib/utils/onAnyClientCallUpdater.js +51 -0
  119. package/package.json +4 -4
  120. package/src/ObservedCall.ts +116 -166
  121. package/src/ObservedCertificate.ts +43 -0
  122. package/src/ObservedClient.ts +213 -1060
  123. package/src/ObservedCodec.ts +49 -0
  124. package/src/ObservedDataChannel.ts +68 -181
  125. package/src/ObservedIceCandidate.ts +64 -0
  126. package/src/ObservedIceCandidatePair.ts +118 -0
  127. package/src/ObservedIceTransport.ts +70 -0
  128. package/src/ObservedInboundRtp.ts +205 -0
  129. package/src/ObservedInboundTrack.ts +70 -0
  130. package/src/ObservedMediaPlayout.ts +49 -0
  131. package/src/ObservedMediaSource.ts +60 -0
  132. package/src/ObservedOutboundRtp.ts +156 -0
  133. package/src/ObservedOutboundTrack.ts +69 -0
  134. package/src/ObservedPeerConnection.ts +827 -405
  135. package/src/ObservedPeerConnectionTransport.ts +38 -0
  136. package/src/ObservedRemoteInboundRtp.ts +64 -0
  137. package/src/ObservedRemoteOutboundRtp.ts +65 -0
  138. package/src/ObservedTURN.ts +86 -0
  139. package/src/ObservedTurnServer.ts +72 -0
  140. package/src/Observer.ts +91 -302
  141. package/src/common/Middleware.ts +70 -42
  142. package/src/common/logger.ts +60 -69
  143. package/src/common/types.ts +0 -42
  144. package/src/common/utils.ts +12 -0
  145. package/src/detectors/Detector.ts +6 -0
  146. package/src/detectors/Detectors.ts +38 -0
  147. package/src/index.ts +24 -90
  148. package/src/monitors/TurnUsageMonitor.ts +164 -164
  149. package/src/schema/ClientEventTypes.ts +36 -0
  150. package/src/schema/ClientMetaTypes.ts +41 -0
  151. package/src/schema/ClientSample.ts +1682 -0
  152. package/src/scores/CalculatedScore.ts +6 -0
  153. package/src/scores/DefaultCallScoreCalculator.ts +23 -0
  154. package/src/scores/ScoreCalculator.ts +3 -0
  155. package/src/utils/CallUpdater.ts +5 -0
  156. package/src/utils/OnAllClientCallUpdater.ts +68 -0
  157. package/src/utils/RemoteTrackResolver.ts +12 -0
  158. package/src/utils/onAnyClientCallUpdater.ts +59 -0
  159. package/tsconfig.json +3 -3
  160. package/lib/ObservedICE.d.ts +0 -64
  161. package/lib/ObservedICE.d.ts.map +0 -1
  162. package/lib/ObservedICE.js +0 -141
  163. package/lib/ObservedInboundAudioTrack.d.ts +0 -96
  164. package/lib/ObservedInboundAudioTrack.d.ts.map +0 -1
  165. package/lib/ObservedInboundAudioTrack.js +0 -217
  166. package/lib/ObservedInboundVideoTrack.d.ts +0 -101
  167. package/lib/ObservedInboundVideoTrack.d.ts.map +0 -1
  168. package/lib/ObservedInboundVideoTrack.js +0 -232
  169. package/lib/ObservedOutboundAudioTrack.d.ts +0 -87
  170. package/lib/ObservedOutboundAudioTrack.d.ts.map +0 -1
  171. package/lib/ObservedOutboundAudioTrack.js +0 -186
  172. package/lib/ObservedOutboundVideoTrack.d.ts +0 -91
  173. package/lib/ObservedOutboundVideoTrack.d.ts.map +0 -1
  174. package/lib/ObservedOutboundVideoTrack.js +0 -193
  175. package/lib/ObservedSfu.d.ts +0 -59
  176. package/lib/ObservedSfu.d.ts.map +0 -1
  177. package/lib/ObservedSfu.js +0 -157
  178. package/lib/ObservedSfuInboundRtpPad.d.ts +0 -42
  179. package/lib/ObservedSfuInboundRtpPad.d.ts.map +0 -1
  180. package/lib/ObservedSfuInboundRtpPad.js +0 -71
  181. package/lib/ObservedSfuOutboundRtpPad.d.ts +0 -42
  182. package/lib/ObservedSfuOutboundRtpPad.d.ts.map +0 -1
  183. package/lib/ObservedSfuOutboundRtpPad.js +0 -72
  184. package/lib/ObservedSfuSctpChannel.d.ts +0 -42
  185. package/lib/ObservedSfuSctpChannel.d.ts.map +0 -1
  186. package/lib/ObservedSfuSctpChannel.js +0 -57
  187. package/lib/ObservedSfuTransport.d.ts +0 -58
  188. package/lib/ObservedSfuTransport.d.ts.map +0 -1
  189. package/lib/ObservedSfuTransport.js +0 -120
  190. package/lib/RemoteTrackAssigner.d.ts +0 -14
  191. package/lib/RemoteTrackAssigner.d.ts.map +0 -1
  192. package/lib/RemoteTrackAssigner.js +0 -52
  193. package/lib/ReportsCollector.d.ts +0 -109
  194. package/lib/ReportsCollector.d.ts.map +0 -1
  195. package/lib/ReportsCollector.js +0 -207
  196. package/lib/common/CalculatedScore.d.ts +0 -80
  197. package/lib/common/CalculatedScore.d.ts.map +0 -1
  198. package/lib/common/CalculatedScore.js +0 -162
  199. package/lib/common/CallEventType.d.ts +0 -63
  200. package/lib/common/CallEventType.d.ts.map +0 -1
  201. package/lib/common/CallEventType.js +0 -39
  202. package/lib/common/CallMetaReports.d.ts +0 -42
  203. package/lib/common/CallMetaReports.d.ts.map +0 -1
  204. package/lib/common/CallMetaReports.js +0 -36
  205. package/lib/common/QualityScoreUtils.d.ts +0 -1
  206. package/lib/common/QualityScoreUtils.d.ts.map +0 -1
  207. package/lib/common/QualityScoreUtils.js +0 -1
  208. package/lib/common/TypedEventEmitter.d.ts +0 -22
  209. package/lib/common/TypedEventEmitter.d.ts.map +0 -1
  210. package/lib/common/TypedEventEmitter.js +0 -11
  211. package/lib/common/callEventReports.d.ts +0 -10
  212. package/lib/common/callEventReports.d.ts.map +0 -1
  213. package/lib/common/callEventReports.js +0 -112
  214. package/lib/monitors/CallSummary.d.ts +0 -37
  215. package/lib/monitors/CallSummary.d.ts.map +0 -1
  216. package/lib/monitors/CallSummaryMonitor.d.ts +0 -32
  217. package/lib/monitors/CallSummaryMonitor.d.ts.map +0 -1
  218. package/lib/monitors/CallSummaryMonitor.js +0 -152
  219. package/lib/monitors/ClientIssueMonitor.d.ts +0 -32
  220. package/lib/monitors/ClientIssueMonitor.d.ts.map +0 -1
  221. package/lib/monitors/ClientIssueMonitor.js +0 -49
  222. package/lib/monitors/SfuServerMonitor.d.ts +0 -79
  223. package/lib/monitors/SfuServerMonitor.d.ts.map +0 -1
  224. package/lib/monitors/SfuServerMonitor.js +0 -198
  225. package/src/ObservedICE.ts +0 -237
  226. package/src/ObservedInboundAudioTrack.ts +0 -344
  227. package/src/ObservedInboundVideoTrack.ts +0 -361
  228. package/src/ObservedOutboundAudioTrack.ts +0 -307
  229. package/src/ObservedOutboundVideoTrack.ts +0 -319
  230. package/src/ObservedSfu.ts +0 -231
  231. package/src/ObservedSfuInboundRtpPad.ts +0 -106
  232. package/src/ObservedSfuOutboundRtpPad.ts +0 -107
  233. package/src/ObservedSfuSctpChannel.ts +0 -103
  234. package/src/ObservedSfuTransport.ts +0 -185
  235. package/src/RemoteTrackAssigner.ts +0 -68
  236. package/src/ReportsCollector.ts +0 -336
  237. package/src/common/CalculatedScore.ts +0 -188
  238. package/src/common/CallEventType.ts +0 -69
  239. package/src/common/CallMetaReports.ts +0 -82
  240. package/src/common/QualityScoreUtils.ts +0 -0
  241. package/src/common/TypedEventEmitter.ts +0 -32
  242. package/src/common/callEventReports.ts +0 -178
  243. package/src/monitors/CallSummary.ts +0 -38
  244. package/src/monitors/CallSummaryMonitor.ts +0 -192
  245. package/src/monitors/ClientIssueMonitor.ts +0 -88
  246. package/src/monitors/SfuServerMonitor.ts +0 -285
  247. /package/lib/{monitors/CallSummary.js → scores/CalculatedScore.js} +0 -0
@@ -1,65 +1,121 @@
1
1
  import { EventEmitter } from 'events';
2
- import { PeerConnectionTransport } from '@observertc/sample-schemas-js';
3
2
  import { ObservedClient } from './ObservedClient';
4
- import { CallEventReport, PeerConnectionTransportReport } from '@observertc/report-schemas-js';
5
- import { ObservedICE } from './ObservedICE';
3
+ import { CertificateStats, CodecStats, DataChannelStats, IceCandidateStats, InboundRtpStats, InboundTrackSample, MediaPlayoutStats, MediaSourceStats, OutboundRtpStats, OutboundTrackSample, PeerConnectionSample, PeerConnectionTransportStats, RemoteInboundRtpStats, RemoteOutboundRtpStats } from './schema/ClientSample';
4
+ import { ObservedInboundRtp } from './ObservedInboundRtp';
5
+ import { createLogger } from './common/logger';
6
+ import { MediaKind } from './common/types';
7
+ import { ObservedOutboundRtp } from './ObservedOutboundRtp';
8
+ import { ObservedCertificate } from './ObservedCertificate';
9
+ import { ObservedCodec } from './ObservedCodec';
6
10
  import { ObservedDataChannel } from './ObservedDataChannel';
7
- import { PartialBy } from './common/utils';
8
- import { ObservedInboundAudioTrack, ObservedInboundAudioTrackModel } from './ObservedInboundAudioTrack';
9
- import { ObservedInboundVideoTrack, ObservedInboundVideoTrackModel } from './ObservedInboundVideoTrack';
10
- import { ObservedOutboundAudioTrack, ObservedOutboundAudioTrackModel } from './ObservedOutboundAudioTrack';
11
- import { ObservedOutboundVideoTrack, ObservedOutboundVideoTrackModel } from './ObservedOutboundVideoTrack';
12
- import { CalculatedScore, getRttScore } from './common/CalculatedScore';
13
- import { ClientIssue } from './monitors/CallSummary';
11
+ import { ObservedIceCandidate } from './ObservedIceCandidate';
12
+ import { ObservedIceCandidatePair } from './ObservedIceCandidatePair';
13
+ import { ObservedIceTransport } from './ObservedIceTransport';
14
+ import { ObservedMediaSource } from './ObservedMediaSource';
15
+ import { ObservedPeerConnectionTransport } from './ObservedPeerConnectionTransport';
16
+ import { ObservedMediaPlayout } from './ObservedMediaPlayout';
17
+ import { ObservedRemoteInboundRtp } from './ObservedRemoteInboundRtp';
18
+ import { ObservedRemoteOutboundRtp } from './ObservedRemoteOutboundRtp';
19
+ import { ObservedInboundTrack } from './ObservedInboundTrack';
20
+ import { ObservedOutboundTrack } from './ObservedOutboundTrack';
21
+ import { CalculatedScore } from './scores/CalculatedScore';
22
+ import { ObservedTurnServer } from './ObservedTurnServer';
23
+
24
+ const logger = createLogger('ObservedPeerConnection');
14
25
 
15
26
  export type ObservedPeerConnectionEvents = {
16
- update: [{
17
- elapsedTimeInMs: number;
18
- }],
19
- close: [],
20
- score: [CalculatedScore],
21
- newinboudaudiotrack: [ObservedInboundAudioTrack],
22
- newinboudvideotrack: [ObservedInboundVideoTrack],
23
- newoutboundaudiotrack: [ObservedOutboundAudioTrack],
24
- newoutboundvideotrack: [ObservedOutboundVideoTrack],
25
- newdatachannel: [ObservedDataChannel],
26
- };
27
-
28
- export type ObservedPeerConnectionModel = {
29
- peerConnectionId: string;
30
- label?: string;
27
+ iceconnectionstatechange: [
28
+ {
29
+ state: string;
30
+ }
31
+ ];
32
+ icegatheringstatechange: [
33
+ {
34
+ state: string;
35
+ }
36
+ ];
37
+ connectionstatechange: [
38
+ {
39
+ state: string;
40
+ }
41
+ ];
42
+ selectedcandidatepair: [];
43
+
44
+ 'added-certificate': [ObservedCertificate];
45
+ 'added-codec': [ObservedCodec];
46
+ 'added-data-channel': [ObservedDataChannel];
47
+ 'added-ice-candidate': [ObservedIceCandidate];
48
+ 'added-ice-candidate-pair': [ObservedIceCandidatePair];
49
+ 'added-ice-transport': [ObservedIceTransport];
50
+ 'added-inbound-rtp': [ObservedInboundRtp];
51
+ 'added-inbound-track': [ObservedInboundTrack];
52
+ 'added-media-playout': [ObservedMediaPlayout];
53
+ 'added-media-source': [ObservedMediaSource];
54
+ 'added-outbound-rtp': [ObservedOutboundRtp];
55
+ 'added-outbound-track': [ObservedOutboundTrack];
56
+ 'added-peer-connection-transport': [ObservedPeerConnectionTransport];
57
+ 'added-remote-inbound-rtp': [ObservedRemoteInboundRtp];
58
+ 'added-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
59
+ 'removed-certificate': [ObservedCertificate];
60
+ 'removed-codec': [ObservedCodec];
61
+ 'removed-data-channel': [ObservedDataChannel];
62
+ 'removed-ice-candidate': [ObservedIceCandidate];
63
+ 'removed-ice-candidate-pair': [ObservedIceCandidatePair];
64
+ 'removed-ice-transport': [ObservedIceTransport];
65
+ 'removed-inbound-rtp': [ObservedInboundRtp];
66
+ 'removed-inbound-track': [ObservedInboundTrack];
67
+ 'removed-media-playout': [ObservedMediaPlayout];
68
+ 'removed-media-source': [ObservedMediaSource];
69
+ 'removed-outbound-rtp': [ObservedOutboundRtp];
70
+ 'removed-outbound-track': [ObservedOutboundTrack];
71
+ 'removed-peer-connection-transport': [ObservedPeerConnectionTransport];
72
+ 'removed-remote-inbound-rtp': [ObservedRemoteInboundRtp];
73
+ 'removed-remote-outbound-rtp': [ObservedRemoteOutboundRtp];
74
+ 'update': [],
75
+ close: [];
31
76
  };
32
77
 
33
- export type ObservedPeerConnectionStats = Omit<PeerConnectionTransport, 'transportId' | 'label'>;
34
-
35
78
  export declare interface ObservedPeerConnection {
36
- on<U extends keyof ObservedPeerConnectionEvents>(event: U, listener: (...args: ObservedPeerConnectionEvents[U]) => void): this;
37
- off<U extends keyof ObservedPeerConnectionEvents>(event: U, listener: (...args: ObservedPeerConnectionEvents[U]) => void): this;
38
- once<U extends keyof ObservedPeerConnectionEvents>(event: U, listener: (...args: ObservedPeerConnectionEvents[U]) => void): this;
79
+ on<U extends keyof ObservedPeerConnectionEvents>(
80
+ event: U,
81
+ listener: (...args: ObservedPeerConnectionEvents[U]) => void
82
+ ): this;
83
+ off<U extends keyof ObservedPeerConnectionEvents>(
84
+ event: U,
85
+ listener: (...args: ObservedPeerConnectionEvents[U]) => void
86
+ ): this;
87
+ once<U extends keyof ObservedPeerConnectionEvents>(
88
+ event: U,
89
+ listener: (...args: ObservedPeerConnectionEvents[U]) => void
90
+ ): this;
39
91
  emit<U extends keyof ObservedPeerConnectionEvents>(event: U, ...args: ObservedPeerConnectionEvents[U]): boolean;
40
92
  }
41
93
 
42
94
  export class ObservedPeerConnection extends EventEmitter {
43
- public readonly created = Date.now();
44
- public visited = true;
95
+ private _visited = true;
45
96
 
97
+ public appData?: Record<string, unknown>;
98
+ public readonly calculatedScore: CalculatedScore = {
99
+ weight: 1,
100
+ value: undefined,
101
+ };
102
+
103
+ public closed = false;
46
104
  // timestamp of the PEER_CONNECTION_OPENED event
47
- public opened?: number;
105
+ public openedAt?: number;
48
106
  // timestamp of the PEER_CONNECTION_CLOSED event
49
- public closedTimestamp?: number;
50
-
51
- private _elapsedTimeSinceLastUpdate?: number;
52
- private _statsTimestamp?: number;
53
- private _stabilityScores: number[] = [];
107
+ public closedAt?: number;
108
+ public updated = Date.now();
54
109
 
55
- public ωpendingIssuesForScores: ClientIssue[] = [];
56
-
57
- public score?: CalculatedScore;
110
+ public availableIncomingBitrate = 0;
111
+ public availableOutgoingBitrate = 0;
58
112
  public totalInboundPacketsLost = 0;
59
113
  public totalInboundPacketsReceived = 0;
60
114
  public totalOutboundPacketsSent = 0;
61
115
  public totalDataChannelBytesSent = 0;
62
116
  public totalDataChannelBytesReceived = 0;
117
+ public totalDataChannelMessagesSent = 0;
118
+ public totalDataChannelMessagesReceived = 0;
63
119
 
64
120
  public totalSentAudioBytes = 0;
65
121
  public totalSentVideoBytes = 0;
@@ -75,182 +131,161 @@ export class ObservedPeerConnection extends EventEmitter {
75
131
  public deltaOutboundPacketsSent = 0;
76
132
  public deltaDataChannelBytesSent = 0;
77
133
  public deltaDataChannelBytesReceived = 0;
134
+ public deltaDataChannelMessagesSent = 0;
135
+ public deltaDataChannelMessagesReceived = 0;
78
136
  public deltaInboundReceivedBytes = 0;
79
137
  public deltaOutboundSentBytes = 0;
80
-
138
+
81
139
  public deltaReceivedAudioBytes = 0;
82
140
  public deltaReceivedVideoBytes = 0;
83
141
  public deltaReceivedAudioPackets = 0;
84
142
  public deltaReceivedVideoPackets = 0;
85
143
  public deltaSentAudioBytes = 0;
86
144
  public deltaSentVideoBytes = 0;
145
+
87
146
  public receivingPacketsPerSecond = 0;
88
147
  public sendingPacketsPerSecond = 0;
89
148
  public sendingAudioBitrate = 0;
90
149
  public sendingVideoBitrate = 0;
91
150
  public receivingAudioBitrate = 0;
92
151
  public receivingVideoBitrate = 0;
93
-
152
+
94
153
  public avgRttInMs?: number;
95
154
  public avgJitter?: number;
96
155
 
97
- private _closed = false;
98
- private _updated = Date.now();
99
- private _sample?: ObservedPeerConnectionStats;
100
- private _marker?: string;
101
-
102
- public readonly ICE = ObservedICE.create(this);
103
- private readonly _inboundAudioTracks = new Map<string, ObservedInboundAudioTrack>();
104
- private readonly _inboundVideoTracks = new Map<string, ObservedInboundVideoTrack>();
105
- private readonly _outboundAudioTracks = new Map<string, ObservedOutboundAudioTrack>();
106
- private readonly _outboundVideoTracks = new Map<string, ObservedOutboundVideoTrack>();
107
- private readonly _dataChannels = new Map<number, ObservedDataChannel>();
108
-
109
- public constructor(
110
- private readonly _model: ObservedPeerConnectionModel,
111
- public readonly client: ObservedClient,
112
- ) {
156
+ public usingTCP = false;
157
+ public usingTURN = false;
158
+
159
+ public observedTurnServer?: ObservedTurnServer;
160
+ public readonly observedCertificates = new Map<string, ObservedCertificate>();
161
+ public readonly observedCodecs = new Map<string, ObservedCodec>();
162
+ public readonly observedDataChannels = new Map<string, ObservedDataChannel>();
163
+ public readonly observedIceCandidates = new Map<string, ObservedIceCandidate>();
164
+ public readonly observedIceCandidatesPair = new Map<string, ObservedIceCandidatePair>();
165
+ public readonly observedIceTransports = new Map<string, ObservedIceTransport>();
166
+ public readonly observedInboundRtps = new Map<number, ObservedInboundRtp>();
167
+ public readonly observedInboundTracks = new Map<string, ObservedInboundTrack>();
168
+ public readonly observedMediaPlayouts = new Map<string, ObservedMediaPlayout>();
169
+ public readonly observedMediaSources = new Map<string, ObservedMediaSource>();
170
+ public readonly observedOutboundRtps = new Map<number, ObservedOutboundRtp>();
171
+ public readonly observedOutboundTracks = new Map<string, ObservedOutboundTrack>();
172
+ public readonly observedPeerConnectionTransports = new Map<string, ObservedPeerConnectionTransport>();
173
+ public readonly observedRemoteInboundRtps = new Map<number, ObservedRemoteInboundRtp>();
174
+ public readonly observedRemoteOutboundRtps = new Map<number, ObservedRemoteOutboundRtp>();
175
+
176
+ public constructor(public readonly peerConnectionId: string, public readonly client: ObservedClient) {
113
177
  super();
114
178
  this.setMaxListeners(Infinity);
115
179
  }
116
180
 
117
- public get serviceId() {
118
- return this.client.serviceId;
119
- }
120
-
121
- public get roomId() {
122
- return this.client.roomId;
123
- }
124
-
125
- public get callId() {
126
- return this.client.callId;
127
- }
128
-
129
- public get clientId() {
130
- return this.client.clientId;
181
+ public get score() {
182
+ return this.calculatedScore.value;
131
183
  }
132
184
 
133
- public get mediaUnitId() {
134
- return this.client.mediaUnitId;
135
- }
185
+ public get visited() {
186
+ const visited = this._visited;
136
187
 
137
- public get label() {
138
- return this._model.label;
139
- }
188
+ this._visited = false;
140
189
 
141
- public set label(value: string | undefined) {
142
- this._model.label = value;
190
+ return visited;
143
191
  }
144
192
 
145
- public get usingTURN() {
146
- return this.ICE.usingTURN;
193
+ public get codecs() {
194
+ return [ ...this.observedCodecs.values() ];
147
195
  }
148
196
 
149
- public get availableOutgoingBitrate() {
150
- return this.ICE.stats?.availableOutgoingBitrate;
197
+ public get inboundRtps() {
198
+ return [ ...this.observedInboundRtps.values() ];
151
199
  }
152
200
 
153
- public get availableIncomingBitrate() {
154
- return this.ICE.stats?.availableIncomingBitrate;
201
+ public get remoteOutboundRtps() {
202
+ return [ ...this.observedRemoteOutboundRtps.values() ];
155
203
  }
156
204
 
157
- public get peerConnectionId(): string {
158
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
- return this._model.peerConnectionId!;
205
+ public get outboundRtps() {
206
+ return [ ...this.observedOutboundRtps.values() ];
160
207
  }
161
208
 
162
- public get reports() {
163
- return this.client.reports;
209
+ public get remoteInboundRtps() {
210
+ return [ ...this.observedRemoteInboundRtps.values() ];
164
211
  }
165
212
 
166
- public get stats(): ObservedPeerConnectionStats | undefined {
167
- return this._sample;
213
+ public get mediaSources() {
214
+ return [ ...this.observedMediaSources.values() ];
168
215
  }
169
216
 
170
- public get updated(): number {
171
- return this._updated;
217
+ public get mediaPlayouts() {
218
+ return [ ...this.observedMediaPlayouts.values() ];
172
219
  }
173
220
 
174
- public get inboundAudioTracks(): ReadonlyMap<string, ObservedInboundAudioTrack> {
175
- return this._inboundAudioTracks;
221
+ public get dataChannels() {
222
+ return [ ...this.observedDataChannels.values() ];
176
223
  }
177
224
 
178
- public get inboundVideoTracks(): ReadonlyMap<string, ObservedInboundVideoTrack> {
179
- return this._inboundVideoTracks;
225
+ public get peerConnectionTransports() {
226
+ return [ ...this.observedPeerConnectionTransports.values() ];
180
227
  }
181
228
 
182
- public get outboundAudioTracks(): ReadonlyMap<string, ObservedOutboundAudioTrack> {
183
- return this._outboundAudioTracks;
229
+ public get iceTransports() {
230
+ return [ ...this.observedIceTransports.values() ];
184
231
  }
185
232
 
186
- public get outboundVideoTracks(): ReadonlyMap<string, ObservedOutboundVideoTrack> {
187
- return this._outboundVideoTracks;
233
+ public get iceCandidates() {
234
+ return [ ...this.observedIceCandidates.values() ];
188
235
  }
189
236
 
190
- public get dataChannels(): ReadonlyMap<number, ObservedDataChannel> {
191
- return this._dataChannels;
237
+ public get iceCandidatePairs() {
238
+ return [ ...this.observedIceCandidatesPair.values() ];
192
239
  }
193
240
 
194
- public get uptimeInMs() {
195
- return this._updated - this.created;
241
+ public get certificates() {
242
+ return [ ...this.observedCertificates.values() ];
196
243
  }
197
244
 
198
- public get marker() {
199
- return this._marker;
245
+ public get selectedIceCandidatePairs() {
246
+ return this.iceTransports.map((iceTransport) => iceTransport.getSelectedCandidatePair())
247
+ .filter((pair) => pair !== undefined) as ObservedIceCandidatePair[];
200
248
  }
201
249
 
202
- public set marker(value: string | undefined) {
203
- this._marker = value;
204
- this._inboundAudioTracks.forEach((track) => (track.marker = value));
205
- this._inboundVideoTracks.forEach((track) => (track.marker = value));
206
- this._outboundAudioTracks.forEach((track) => (track.marker = value));
207
- this._outboundVideoTracks.forEach((track) => (track.marker = value));
208
- this._dataChannels.forEach((channel) => (channel.marker = value));
209
- this.ICE.marker = value;
210
- }
211
-
212
- public addEventReport(params: PartialBy<Omit<CallEventReport, 'serviceId' | 'roomId' | 'callId' | 'clientId' | 'userId' | 'peerConnectionId' | 'marker'>, 'timestamp'>) {
213
- this.reports.addCallEventReport({
214
- ...params,
215
- serviceId: this.serviceId,
216
- mediaUnitId: this.mediaUnitId,
217
- roomId: this.roomId,
218
- callId: this.callId,
219
- clientId: this.clientId,
220
- userId: this.client.userId,
221
- peerConnectionId: this.peerConnectionId,
222
- marker: this.client.marker,
223
- timestamp: params.timestamp ?? Date.now(),
224
- });
225
- }
226
-
227
- public get closed() {
228
- return this._closed;
250
+ public get selectedIceCandiadtePairForTurn() {
251
+ return this.selectedIceCandidatePairs
252
+ .filter((pair) =>
253
+ pair.getLocalCandidate()?.candidateType === 'relay' &&
254
+ pair.getRemoteCandidate()?.url?.startsWith('turn:')
255
+ );
229
256
  }
230
257
 
231
258
  public close() {
232
- if (this._closed) return;
233
- this._closed = true;
234
-
235
- Array.from(this._inboundAudioTracks.values()).forEach((track) => track.close());
236
- Array.from(this._inboundVideoTracks.values()).forEach((track) => track.close());
237
- Array.from(this._outboundAudioTracks.values()).forEach((track) => {
238
- this.client.call.sfuStreamIdToOutboundAudioTrack.delete(track.sfuStreamId ?? '');
239
- track.close();
240
- });
241
- Array.from(this._outboundVideoTracks.values()).forEach((track) => {
242
- this.client.call.sfuStreamIdToOutboundVideoTrack.delete(track.sfuStreamId ?? '');
243
- track.close();
244
- });
245
-
259
+ if (this.closed) return;
260
+ this.closed = true;
261
+
262
+ this.observedCertificates.clear();
263
+ this.observedCodecs.clear();
264
+ this.observedDataChannels.clear();
265
+ this.observedIceCandidates.clear();
266
+ this.observedIceCandidatesPair.clear();
267
+ this.observedIceTransports.clear();
268
+ this.observedInboundRtps.clear();
269
+ this.observedInboundTracks.clear();
270
+ this.observedMediaPlayouts.clear();
271
+ this.observedMediaSources.clear();
272
+ this.observedOutboundRtps.clear();
273
+ this.observedOutboundTracks.clear();
274
+ this.observedPeerConnectionTransports.clear();
275
+ this.observedRemoteInboundRtps.clear();
276
+ this.observedRemoteOutboundRtps.clear();
277
+
278
+ this.client.call.observer.observedTURN.removePeerConnection(this);
279
+
246
280
  this.emit('close');
247
281
  }
248
282
 
249
- public getTrack(trackId: string): ObservedInboundAudioTrack | ObservedInboundVideoTrack | ObservedOutboundAudioTrack | ObservedOutboundVideoTrack | undefined {
250
- return this._inboundAudioTracks.get(trackId) ?? this._inboundVideoTracks.get(trackId) ?? this._outboundAudioTracks.get(trackId) ?? this._outboundVideoTracks.get(trackId);
251
- }
283
+ public accept(sample: PeerConnectionSample) {
284
+ if (this.closed) return;
285
+ this._visited = true;
252
286
 
253
- public resetMetrics() {
287
+ this.availableIncomingBitrate = 0;
288
+ this.availableOutgoingBitrate = 0;
254
289
  this.deltaInboundPacketsLost = 0;
255
290
  this.deltaInboundPacketsReceived = 0;
256
291
  this.deltaOutboundPacketsSent = 0;
@@ -258,7 +293,6 @@ export class ObservedPeerConnection extends EventEmitter {
258
293
  this.deltaDataChannelBytesReceived = 0;
259
294
  this.deltaInboundReceivedBytes = 0;
260
295
  this.deltaOutboundSentBytes = 0;
261
-
262
296
  this.deltaReceivedAudioBytes = 0;
263
297
  this.deltaReceivedVideoBytes = 0;
264
298
  this.deltaReceivedAudioPackets = 0;
@@ -270,304 +304,692 @@ export class ObservedPeerConnection extends EventEmitter {
270
304
  this.receivingAudioBitrate = 0;
271
305
  this.receivingVideoBitrate = 0;
272
306
 
273
- this.ICE.resetMetrics();
307
+ const now = Date.now();
308
+ const elapsedTimeInMs = now - this.updated;
309
+ const elapsedTimeInSec = elapsedTimeInMs / 1000;
310
+ const rttMeasurementsInSec: number[] = [];
311
+ const jitterMeasurements: number[] = [];
312
+
313
+ if (sample.certificates) {
314
+ for (const certificate of sample.certificates) {
315
+ this._updateCertificateStats(certificate);
316
+ }
317
+ }
318
+ if (sample.codecs) {
319
+ for (const codec of sample.codecs) {
320
+ this._updateCodecStats(codec);
321
+ }
322
+ }
323
+ if (sample.dataChannels) {
324
+ for (const dataChannel of sample.dataChannels) {
325
+ const observedDataChannel = this._updateDataChannelStats(dataChannel);
326
+
327
+ if (!observedDataChannel) continue;
328
+
329
+ this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent;
330
+ this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived;
331
+ this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent;
332
+ this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived;
333
+ }
334
+ }
335
+ if (sample.iceCandidates) {
336
+ for (const iceCandidate of sample.iceCandidates) {
337
+ this._updateIceCandidateStats(iceCandidate);
338
+ }
339
+ }
340
+ if (sample.iceCandidatePairs) {
341
+ for (const iceCandidatePair of sample.iceCandidatePairs) {
342
+ const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair);
343
+
344
+ if (!observedCandidatePair) continue;
345
+
346
+ if (observedCandidatePair.currentRoundTripTime) {
347
+ rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime);
348
+ }
349
+ if (observedCandidatePair.availableIncomingBitrate) {
350
+ this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate;
351
+ }
352
+ if (observedCandidatePair.availableOutgoingBitrate) {
353
+ this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate;
354
+ }
355
+ }
356
+ }
357
+ if (sample.iceTransports) {
358
+ for (const iceTransport of sample.iceTransports) {
359
+ this._updateIceTransportStats(iceTransport);
360
+ }
361
+ }
362
+ if (sample.inboundRtps) {
363
+ for (const inboundRtp of sample.inboundRtps) {
364
+ const observedInboundRtp = this._updateInboundRtpStats(inboundRtp);
365
+
366
+ if (!observedInboundRtp) continue;
367
+
368
+ this.deltaInboundPacketsLost += observedInboundRtp.deltaLostPackets;
369
+ this.deltaInboundPacketsReceived += observedInboundRtp.deltaReceivedPackets;
370
+ this.deltaInboundReceivedBytes += observedInboundRtp.deltaBytesReceived;
371
+
372
+ switch (inboundRtp.kind) {
373
+ case 'audio':
374
+ this.deltaReceivedAudioBytes += observedInboundRtp.deltaBytesReceived;
375
+ this.deltaReceivedAudioPackets += observedInboundRtp.deltaReceivedPackets;
376
+ break;
377
+ case 'video':
378
+ this.deltaReceivedVideoBytes += observedInboundRtp.deltaBytesReceived;
379
+ this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets;
380
+ break;
381
+ }
382
+
383
+ if (observedInboundRtp.jitter) {
384
+ jitterMeasurements.push(observedInboundRtp.jitter);
385
+ }
386
+ }
387
+ }
388
+ if (sample.mediaPlayouts) {
389
+ for (const mediaPlayout of sample.mediaPlayouts) {
390
+ this._updateMediaPlayoutStats(mediaPlayout);
391
+ }
392
+ }
393
+ if (sample.mediaSources) {
394
+ for (const mediaSource of sample.mediaSources) {
395
+ this._updateMediaSourceStats(mediaSource);
396
+ }
397
+ }
398
+ if (sample.outboundRtps) {
399
+ for (const outboundRtp of sample.outboundRtps) {
400
+ const observedOutboundRtp = this._updateOutboundRtpStats(outboundRtp);
401
+
402
+ if (!observedOutboundRtp) continue;
403
+
404
+ this.deltaOutboundPacketsSent += observedOutboundRtp.deltaPacketsSent ?? 0;
405
+ this.deltaOutboundSentBytes += observedOutboundRtp.deltaBytesSent ?? 0;
406
+
407
+ switch (outboundRtp.kind) {
408
+ case 'audio':
409
+ this.deltaSentAudioBytes += observedOutboundRtp.deltaBytesSent;
410
+ this.deltaSentAudioBytes += observedOutboundRtp.deltaPacketsSent;
411
+ break;
412
+ case 'video':
413
+ this.deltaSentVideoBytes += observedOutboundRtp.deltaBytesSent;
414
+ this.deltaSentVideoBytes += observedOutboundRtp.deltaPacketsSent;
415
+ break;
416
+ }
417
+
418
+ }
419
+ }
420
+ if (sample.peerConnectionTransports) {
421
+ for (const peerConnectionTransport of sample.peerConnectionTransports) {
422
+ this._updatePeerConnectionTransportStats(peerConnectionTransport);
423
+ }
424
+ }
425
+ if (sample.remoteInboundRtps) {
426
+ for (const remoteInboundRtp of sample.remoteInboundRtps) {
427
+ const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp);
428
+
429
+ if (!observedRemoteInboundRtp) continue;
430
+
431
+ if (observedRemoteInboundRtp.roundTripTime) {
432
+ rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime);
433
+ }
434
+ }
435
+ }
436
+ if (sample.remoteOutboundRtps) {
437
+ for (const remoteOutboundRtp of sample.remoteOutboundRtps) {
438
+ const observedRemoteOutboundRtp = this._updateRemoteOutboundRtpStats(remoteOutboundRtp);
439
+
440
+ if (!observedRemoteOutboundRtp) continue;
441
+ }
442
+ }
443
+
444
+ // tracks should be updated last as they are derived stats
445
+ // and depends on base stats but they all received in the sample sample
446
+ if (sample.inboundTracks) {
447
+ for (const inboundTrack of sample.inboundTracks) {
448
+ this._updateInboundTrackSample(inboundTrack);
449
+ }
450
+ }
451
+ if (sample.outboundTracks) {
452
+ for (const outboundTrack of sample.outboundTracks) {
453
+ this._updateOutboundTrackSample(outboundTrack);
454
+ }
455
+ }
456
+
457
+ this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
458
+ this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
459
+ this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
460
+ this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
461
+ this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
462
+ this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent;
463
+ this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived;
464
+ this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
465
+ this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
466
+ this.totalSentAudioBytes += this.deltaSentAudioBytes;
467
+ this.totalSentVideoBytes += this.deltaSentVideoBytes;
468
+ this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets;
469
+ this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets;
470
+ this.totalSentAudioPackets += this.deltaSentAudioBytes;
471
+ this.totalSentVideoPackets += this.deltaSentVideoBytes;
472
+
473
+ this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec;
474
+ this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec;
475
+ this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / elapsedTimeInSec;
476
+ this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / elapsedTimeInSec;
477
+ this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / elapsedTimeInSec;
478
+ this.receivingVideoBitrate = (this.deltaReceivedVideoBytes * 8) / elapsedTimeInSec;
479
+
480
+ if (rttMeasurementsInSec.length > 0) {
481
+ this.avgRttInMs = rttMeasurementsInSec.reduce((acc, val) => acc + val, 0) / rttMeasurementsInSec.length;
482
+ } else {
483
+ this.avgRttInMs = undefined;
484
+ }
485
+ if (jitterMeasurements.length > 0) {
486
+ this.avgJitter = jitterMeasurements.reduce((acc, val) => acc + val, 0) / jitterMeasurements.length;
487
+ } else {
488
+ this.avgJitter = undefined;
489
+ }
490
+
491
+ const selectedIceCandidatePairs = this.selectedIceCandidatePairs;
492
+ const selectedCandidatePairForTurn = selectedIceCandidatePairs.filter((pair) =>
493
+ pair.getLocalCandidate()?.candidateType === 'relay' &&
494
+ pair.getRemoteCandidate()?.url?.startsWith('turn:')
495
+ );
496
+ const wasUsingTURN = this.usingTURN;
497
+
498
+ this.usingTCP = selectedIceCandidatePairs.some((pair) => pair.getLocalCandidate()?.protocol === 'tcp');
499
+ this.usingTURN = 0 < selectedCandidatePairForTurn.length;
500
+
501
+ if (this.usingTURN) {
502
+ if (!this.observedTurnServer) {
503
+ this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this);
504
+ }
505
+ this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn);
506
+ } else if (wasUsingTURN) {
507
+ if (!this.usingTURN) {
508
+ this.client.call.observer.observedTURN.removePeerConnection(this);
509
+ }
510
+ }
511
+ this.calculatedScore.value = sample.score;
512
+ this.updated = now;
513
+ this._checkVisited();
514
+
515
+ this.emit('update');
274
516
  }
275
517
 
276
- public update(sample: PeerConnectionTransport, timestamp: number) {
277
- if (this._closed) return;
278
- if (sample.peerConnectionId !== this._model.peerConnectionId) throw new Error(`TransportId mismatch. PeerConnectionId: ${ this._model.peerConnectionId } TransportId: ${ sample.transportId}`);
518
+ private _checkVisited() {
519
+ for (const certificate of [ ...this.observedCertificates.values() ]) {
520
+ if (certificate.visited) continue;
279
521
 
280
- this._sample = sample;
281
- if (this._model.label !== sample.label) {
282
- this._model.label = sample.label;
522
+ this.observedCertificates.delete(certificate.id);
283
523
  }
284
- const now = Date.now();
285
- const report: PeerConnectionTransportReport = {
286
- serviceId: this.client.call.serviceId,
287
- roomId: this.client.call.roomId,
288
- callId: this.client.call.callId,
289
- clientId: this.client.clientId,
290
- userId: this.client.userId,
291
- mediaUnitId: this.client.mediaUnitId,
292
- ...sample,
293
- timestamp,
294
- sampleSeq: -1, // deprecated
295
- marker: this.marker,
296
- };
297
-
298
- this.reports.addPeerConnectionTransportReports(report);
299
- this._elapsedTimeSinceLastUpdate = now - this._updated;
300
- this.visited = true;
301
- this._updated = now;
302
- this._statsTimestamp = timestamp;
303
- this.emit('update', {
304
- elapsedTimeInMs: this._elapsedTimeSinceLastUpdate,
305
- });
306
- }
307
-
308
- public createInboundAudioTrack(config: ObservedInboundAudioTrackModel): ObservedInboundAudioTrack {
309
- if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
310
-
311
- const result = new ObservedInboundAudioTrack(config, this);
524
+ for (const codec of [ ...this.observedCodecs.values() ]) {
525
+ if (codec.visited) continue;
312
526
 
313
- result.on('close', () => {
314
- this._inboundAudioTracks.delete(result.trackId);
315
- });
316
- this._inboundAudioTracks.set(result.trackId, result);
527
+ this.observedCodecs.delete(codec.id);
528
+ this.emit('removed-codec', codec);
529
+ }
530
+ for (const dataChannel of [ ...this.observedDataChannels.values() ]) {
531
+ if (dataChannel.visited) continue;
532
+
533
+ this.observedDataChannels.delete(dataChannel.id);
534
+ this.emit('removed-data-channel', dataChannel);
535
+ }
536
+ for (const iceCandidate of [ ...this.observedIceCandidates.values() ]) {
537
+ if (iceCandidate.visited) continue;
317
538
 
318
- this.emit('newinboudaudiotrack', result);
539
+ this.observedIceCandidates.delete(iceCandidate.id);
540
+ this.emit('removed-ice-candidate', iceCandidate);
541
+ }
542
+ for (const iceCandidatePair of [ ...this.observedIceCandidatesPair.values() ]) {
543
+ if (iceCandidatePair.visited) continue;
319
544
 
320
- return result;
545
+ this.observedIceCandidatesPair.delete(iceCandidatePair.id);
546
+ this.emit('removed-ice-candidate-pair', iceCandidatePair);
547
+ }
548
+ for (const iceTransport of [ ...this.observedIceTransports.values() ]) {
549
+ if (iceTransport.visited) continue;
550
+
551
+ this.observedIceTransports.delete(iceTransport.id);
552
+ this.emit('removed-ice-transport', iceTransport);
553
+ }
554
+ for (const inboundRtp of [ ...this.observedInboundRtps.values() ]) {
555
+ if (inboundRtp.visited) continue;
556
+
557
+ this.observedInboundRtps.delete(inboundRtp.ssrc);
558
+ this.emit('removed-inbound-rtp', inboundRtp);
559
+ }
560
+ for (const inboundTrack of [ ...this.observedInboundTracks.values() ]) {
561
+ if (inboundTrack.visited) continue;
562
+
563
+ this.observedInboundTracks.delete(inboundTrack.id);
564
+ this.emit('removed-inbound-track', inboundTrack);
565
+ }
566
+ for (const mediaPlayout of [ ...this.observedMediaPlayouts.values() ]) {
567
+ if (mediaPlayout.visited) continue;
568
+
569
+ this.observedMediaPlayouts.delete(mediaPlayout.id);
570
+ this.emit('removed-media-playout', mediaPlayout);
571
+ }
572
+ for (const mediaSource of [ ...this.observedMediaSources.values() ]) {
573
+ if (mediaSource.visited) continue;
574
+
575
+ this.observedMediaSources.delete(mediaSource.id);
576
+ this.emit('removed-media-source', mediaSource);
577
+ }
578
+ for (const outboundRtp of [ ...this.observedOutboundRtps.values() ]) {
579
+ if (outboundRtp.visited) continue;
580
+
581
+ this.observedOutboundRtps.delete(outboundRtp.ssrc);
582
+ this.emit('removed-outbound-rtp', outboundRtp);
583
+ }
584
+ for (const outboundTrack of [ ...this.observedOutboundTracks.values() ]) {
585
+ if (outboundTrack.visited) continue;
586
+
587
+ this.observedOutboundTracks.delete(outboundTrack.id);
588
+ this.emit('removed-outbound-track', outboundTrack);
589
+ }
590
+ for (const peerConnectionTransport of [ ...this.observedPeerConnectionTransports.values() ]) {
591
+ if (peerConnectionTransport.visited) continue;
592
+
593
+ this.observedPeerConnectionTransports.delete(peerConnectionTransport.id);
594
+ this.emit('removed-peer-connection-transport', peerConnectionTransport);
595
+ }
596
+ for (const remoteInboundRtp of [ ...this.observedRemoteInboundRtps.values() ]) {
597
+ if (remoteInboundRtp.visited) continue;
598
+
599
+ this.observedRemoteInboundRtps.delete(remoteInboundRtp.ssrc);
600
+ this.emit('removed-remote-inbound-rtp', remoteInboundRtp);
601
+ }
602
+ for (const remoteOutboundRtp of [ ...this.observedRemoteOutboundRtps.values() ]) {
603
+ if (remoteOutboundRtp.visited) continue;
604
+
605
+ this.observedRemoteOutboundRtps.delete(remoteOutboundRtp.ssrc);
606
+ this.emit('removed-remote-outbound-rtp', remoteOutboundRtp);
607
+ }
321
608
  }
322
609
 
323
- public createInboundVideoTrack(config: ObservedInboundVideoTrackModel): ObservedInboundVideoTrack {
324
- if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
325
-
326
- const result = new ObservedInboundVideoTrack(config, this);
610
+ private _updateCertificateStats(stats: CertificateStats) {
611
+ let observedCertificate = this.observedCertificates.get(stats.id);
612
+
613
+ if (!observedCertificate) {
614
+ if (!stats.timestamp || !stats.id || !stats.fingerprint) {
615
+ return logger.warn(
616
+ `ObservedPeerConnection received an invalid CertificateStats (missing timestamp OR id OR fingerprint field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
617
+ stats
618
+ );
619
+ }
327
620
 
328
- result.on('close', () => {
329
- this._inboundVideoTracks.delete(result.trackId);
330
- });
331
- this._inboundVideoTracks.set(result.trackId, result);
621
+ observedCertificate = new ObservedCertificate(stats.timestamp, stats.id, this);
332
622
 
333
- this.emit('newinboudvideotrack', result);
623
+ this.observedCertificates.set(stats.id, observedCertificate);
624
+ this.emit('added-certificate', observedCertificate);
625
+ }
334
626
 
335
- return result;
627
+ observedCertificate.update(stats);
628
+
629
+ return observedCertificate;
336
630
  }
337
631
 
338
- public createOutboundAudioTrack(config: ObservedOutboundAudioTrackModel): ObservedOutboundAudioTrack {
339
- if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
340
-
341
- const result = new ObservedOutboundAudioTrack(config, this);
632
+ private _updateCodecStats(stats: CodecStats) {
633
+ let observedCodec = this.observedCodecs.get(stats.id);
342
634
 
343
- result.on('close', () => {
344
- this._outboundAudioTracks.delete(result.trackId);
345
- });
346
- this._outboundAudioTracks.set(result.trackId, result);
635
+ if (!observedCodec) {
636
+ if (!stats.timestamp || !stats.id || !stats.mimeType) {
637
+ return logger.warn(
638
+ `ObservedPeerConnection received an invalid CodecStats (missing timestamp OR id OR mimeType field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
639
+ stats
640
+ );
641
+ }
347
642
 
348
- this.emit('newoutboundaudiotrack', result);
643
+ observedCodec = new ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this);
349
644
 
350
- return result;
645
+ this.observedCodecs.set(stats.id, observedCodec);
646
+ this.emit('added-codec', observedCodec);
647
+ }
648
+
649
+ observedCodec.update(stats);
650
+
651
+ return observedCodec;
351
652
  }
352
653
 
353
- public createOutboundVideoTrack(config: ObservedOutboundVideoTrackModel): ObservedOutboundVideoTrack {
354
- if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
355
-
356
- const result = new ObservedOutboundVideoTrack(config, this);
654
+ private _updateDataChannelStats(stats: DataChannelStats) {
655
+ let observedDataChannel = this.observedDataChannels.get(stats.id);
357
656
 
358
- result.on('close', () => {
359
- this._outboundVideoTracks.delete(result.trackId);
360
- });
361
- this._outboundVideoTracks.set(result.trackId, result);
657
+ if (!observedDataChannel) {
658
+ if (!stats.timestamp || !stats.id) {
659
+ return logger.warn(
660
+ `ObservedPeerConnection received an invalid DataChannelStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
661
+ stats
662
+ );
663
+ }
664
+
665
+ observedDataChannel = new ObservedDataChannel(stats.timestamp, stats.id, this);
666
+
667
+ this.observedDataChannels.set(stats.id, observedDataChannel);
668
+ this.emit('added-data-channel', observedDataChannel);
669
+ }
362
670
 
363
- this.emit('newoutboundvideotrack', result);
671
+ observedDataChannel.update(stats);
364
672
 
365
- return result;
673
+ return observedDataChannel;
366
674
  }
367
675
 
368
- public createDataChannel(channelId: number) {
369
- if (this._closed) throw new Error(`PeerConnection ${this.peerConnectionId} is closed`);
370
-
371
- const result = new ObservedDataChannel({
372
- channelId,
373
- }, this);
676
+ private _updateIceCandidateStats(stats: IceCandidateStats) {
677
+ let observedIceCandidate = this.observedIceCandidates.get(stats.id);
678
+
679
+ if (!observedIceCandidate) {
680
+ if (!stats.timestamp || !stats.id) {
681
+ return logger.warn(
682
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
683
+ stats
684
+ );
685
+ }
374
686
 
375
- result.on('close', () => {
376
- this._dataChannels.delete(result.channelId);
377
- });
378
- this._dataChannels.set(result.channelId, result);
687
+ observedIceCandidate = new ObservedIceCandidate(stats.timestamp, stats.id, this);
379
688
 
380
- this.emit('newdatachannel', result);
689
+ this.observedIceCandidates.set(stats.id, observedIceCandidate);
690
+ this.emit('added-ice-candidate', observedIceCandidate);
691
+ }
381
692
 
382
- return result;
693
+ observedIceCandidate.update(stats);
694
+
695
+ return observedIceCandidate;
383
696
  }
384
697
 
385
- public updateMetrics() {
386
- let sumRttInMs = 0;
387
- let sumJitter = 0;
388
- const trackScores: CalculatedScore[] = [];
698
+ private _updateIceCandidatePairStats(stats: IceCandidateStats) {
699
+ let observedIceCandidatePair = this.observedIceCandidatesPair.get(stats.id);
389
700
 
390
- this._inboundAudioTracks.forEach((track) => {
391
- track.updateMetrics();
701
+ if (!observedIceCandidatePair) {
702
+ if (!stats.timestamp || !stats.id) {
703
+ return logger.warn(
704
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
705
+ stats
706
+ );
707
+ }
392
708
 
393
- this.deltaInboundPacketsLost += track.deltaLostPackets;
394
- this.deltaInboundPacketsReceived += track.deltaReceivedPackets;
395
- this.deltaInboundReceivedBytes += track.deltaBytesReceived;
396
-
397
- this.deltaReceivedAudioBytes += track.deltaBytesReceived;
398
- this.deltaReceivedAudioPackets += track.deltaReceivedPackets;
399
-
400
- this.receivingAudioBitrate += track.bitrate;
709
+ observedIceCandidatePair = new ObservedIceCandidatePair(stats.timestamp, stats.id, this);
401
710
 
402
- sumRttInMs += (track.rttInMs ?? 0);
403
- sumJitter += (track.jitter ?? 0);
711
+ this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair);
712
+ this.emit('added-ice-candidate-pair', observedIceCandidatePair);
713
+ }
404
714
 
405
- track.score && trackScores.push(track.score);
406
- });
715
+ observedIceCandidatePair.update(stats);
407
716
 
408
- this._inboundVideoTracks.forEach((track) => {
409
- track.updateMetrics();
717
+ return observedIceCandidatePair;
718
+ }
410
719
 
411
- this.deltaInboundPacketsLost += track.deltaLostPackets;
412
- this.deltaInboundPacketsReceived += track.deltaReceivedPackets;
413
- this.deltaInboundReceivedBytes += track.deltaBytesReceived;
414
-
415
- this.deltaReceivedVideoBytes += track.deltaBytesReceived;
416
- this.deltaReceivedVideoPackets += track.deltaReceivedPackets;
417
-
418
- this.receivingVideoBitrate += track.bitrate;
720
+ private _updateIceTransportStats(stats: IceCandidateStats) {
721
+ let observedIceTransport = this.observedIceTransports.get(stats.id);
419
722
 
420
- sumRttInMs += (track.rttInMs ?? 0);
421
- sumJitter += (track.jitter ?? 0);
723
+ if (!observedIceTransport) {
724
+ if (!stats.timestamp || !stats.id) {
725
+ return logger.warn(
726
+ `ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
727
+ stats
728
+ );
729
+ }
422
730
 
423
- track.score && trackScores.push(track.score);
424
- });
731
+ observedIceTransport = new ObservedIceTransport(stats.timestamp, stats.id, this);
425
732
 
426
- this._outboundAudioTracks.forEach((track) => {
427
- track.updateMetrics();
733
+ this.observedIceTransports.set(stats.id, observedIceTransport);
734
+ this.emit('added-ice-transport', observedIceTransport);
735
+ }
428
736
 
429
- this.deltaOutboundPacketsSent += track.deltaSentPackets;
430
- this.deltaOutboundSentBytes += track.deltaSentBytes;
431
-
432
- this.deltaSentAudioBytes += track.deltaSentBytes;
433
- this.deltaSentAudioBytes += track.deltaSentPackets;
434
-
435
- this.sendingAudioBitrate += track.bitrate;
737
+ observedIceTransport.update(stats);
436
738
 
437
- sumRttInMs += (track.rttInMs ?? 0);
438
- sumJitter += (track.jitter ?? 0);
739
+ return observedIceTransport;
740
+ }
439
741
 
440
- track.score && trackScores.push(track.score);
441
- });
742
+ private _updateInboundRtpStats(stats: InboundRtpStats) {
743
+ let observedInboundRtp = this.observedInboundRtps.get(stats.ssrc);
442
744
 
443
- this._outboundVideoTracks.forEach((track) => {
444
- track.updateMetrics();
745
+ if (!observedInboundRtp) {
746
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind || !stats.trackIdentifier) {
747
+ return logger.warn(
748
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR ssrc OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
749
+ stats
750
+ );
751
+ }
445
752
 
446
- this.deltaOutboundPacketsSent += track.deltaSentPackets;
447
- this.deltaOutboundSentBytes += track.deltaSentBytes;
448
-
449
- this.deltaSentVideoBytes += track.deltaSentBytes;
450
- this.deltaSentVideoBytes += track.deltaSentPackets;
451
-
452
- this.sendingVideoBitrate += track.bitrate;
753
+ observedInboundRtp = new ObservedInboundRtp(
754
+ stats.timestamp,
755
+ stats.id,
756
+ stats.ssrc,
757
+ stats.kind as MediaKind,
758
+ stats.trackIdentifier,
759
+ this
760
+ );
761
+
762
+ this.observedInboundRtps.set(stats.ssrc, observedInboundRtp);
763
+ this.emit('added-inbound-rtp', observedInboundRtp);
764
+ }
453
765
 
454
- sumRttInMs += (track.rttInMs ?? 0);
455
- sumJitter += (track.jitter ?? 0);
766
+ observedInboundRtp.update(stats);
456
767
 
457
- track.score && trackScores.push(track.score);
458
- });
768
+ return observedInboundRtp;
769
+ }
459
770
 
460
- this._dataChannels.forEach((channel) => {
461
- this.deltaDataChannelBytesSent += channel.deltaBytesSent;
462
- this.deltaDataChannelBytesReceived += channel.deltaBytesReceived;
463
- });
771
+ private _updateInboundTrackSample(stats: InboundTrackSample) {
772
+ let observedInboundTrack = this.observedInboundTracks.get(stats.id);
464
773
 
465
- const iceRttInMs = this.ICE.stats?.currentRoundTripTime;
466
- let nrOfBelongings = this._inboundAudioTracks.size + this._inboundVideoTracks.size + this._outboundAudioTracks.size + this._outboundVideoTracks.size;
774
+ if (!observedInboundTrack) {
775
+ if (!stats.timestamp || !stats.id || !stats.kind) {
776
+ return logger.warn(
777
+ `ObservedPeerConnection received an invalid InboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
778
+ stats
779
+ );
780
+ }
467
781
 
468
- this.avgJitter = 0 < nrOfBelongings ? sumJitter / nrOfBelongings : undefined;
782
+ const inboundRtp = [ ...this.observedInboundRtps.values() ].find((inbRtp) => inbRtp.trackIdentifier === stats.id);
783
+ const mediaPlayout = inboundRtp ?
784
+ [ ...this.observedMediaPlayouts.values() ].find((mp) => mp.id === inboundRtp.playoutId) : undefined;
785
+
786
+ observedInboundTrack = new ObservedInboundTrack(
787
+ stats.timestamp,
788
+ stats.id,
789
+ stats.kind as MediaKind,
790
+ this,
791
+ inboundRtp,
792
+ mediaPlayout,
793
+ );
794
+
795
+ this.observedInboundTracks.set(stats.id, observedInboundTrack);
796
+ this.emit('added-inbound-track', observedInboundTrack);
797
+ }
798
+
799
+ observedInboundTrack.update(stats);
469
800
 
470
- if (iceRttInMs) {
471
- sumRttInMs += iceRttInMs;
472
- nrOfBelongings += 1;
801
+ return observedInboundTrack;
802
+ }
803
+
804
+ private _updateMediaPlayoutStats(stats: MediaPlayoutStats) {
805
+ let observedMediaPlayout = this.observedMediaPlayouts.get(stats.id);
806
+
807
+ if (!observedMediaPlayout) {
808
+ if (!stats.timestamp || !stats.id || !stats.kind) {
809
+ return logger.warn(
810
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
811
+ stats
812
+ );
813
+ }
814
+
815
+ observedMediaPlayout = new ObservedMediaPlayout(
816
+ stats.timestamp,
817
+ stats.id,
818
+ stats.kind as MediaKind,
819
+ this
820
+ );
821
+
822
+ this.observedMediaPlayouts.set(stats.id, observedMediaPlayout);
823
+ this.emit('added-media-playout', observedMediaPlayout);
473
824
  }
474
825
 
475
- this.avgRttInMs = 0 < nrOfBelongings ? sumRttInMs / nrOfBelongings : undefined;
476
- this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived;
477
- this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent;
478
- this.totalInboundPacketsLost += this.deltaInboundPacketsLost;
479
- this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived;
480
- this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent;
481
- this.totalSentAudioBytes += this.deltaSentAudioBytes;
482
- this.totalSentVideoBytes += this.deltaSentVideoBytes;
483
- this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes;
484
- this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes;
485
- this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets;
486
- this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets;
487
- this.totalSentAudioPackets += this.deltaSentAudioBytes;
488
- this.totalSentVideoPackets += this.deltaSentVideoBytes;
826
+ observedMediaPlayout.update(stats);
489
827
 
490
- if (this._elapsedTimeSinceLastUpdate && 0 < this._elapsedTimeSinceLastUpdate) {
491
- this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / (this._elapsedTimeSinceLastUpdate / 1000);
492
- this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / (this._elapsedTimeSinceLastUpdate / 1000);
493
- } else {
494
- this.sendingPacketsPerSecond = 0;
495
- this.receivingPacketsPerSecond = 0;
496
- }
497
-
498
- // calculate quality score
499
- this._updateQualityScore(trackScores);
500
- }
501
-
502
- private _updateQualityScore(trackScores: CalculatedScore[]) {
503
- // Packet Jitter measured in seconds
504
- // we use RTT and lost packets to calculate the base score for the connection
505
- const rttInMs = this.avgRttInMs ?? 0;
506
- const latencyFactor = rttInMs < 150 ? 1.0 : getRttScore(rttInMs);
507
- const totalPackets = Math.max(1, (this.totalInboundPacketsReceived ?? 0) + (this.totalOutboundPacketsSent ?? 0));
508
- const lostPackets = (this.totalInboundPacketsLost ?? 0) + (this.deltaOutboundPacketsSent ?? 0);
509
- const deliveryFactor = 1.0 - ((lostPackets) / (lostPackets + totalPackets));
510
- // let's push the actual stability score
511
- const stabilityScore = ((latencyFactor * 0.5) + (deliveryFactor * 0.5)) ** 2;
512
-
513
- this._stabilityScores.push(stabilityScore);
514
- if (10 < this._stabilityScores.length) {
515
- this._stabilityScores.shift();
516
- } else if (this._stabilityScores.length < 5) {
517
- return;
518
- }
519
- let counter = 0;
520
- let weight = 0;
521
- let totalScore = 0;
522
-
523
- for (const score of this._stabilityScores) {
524
- weight += 1;
525
- counter += weight;
526
- totalScore += weight * score;
527
- }
528
- const weightedStabilityScore = totalScore / counter;
529
- let sumTrackScores = 0;
530
-
531
- for (const trackScore of trackScores) {
532
- sumTrackScores += trackScore.score;
533
- }
534
-
535
- const avgTrackScores = sumTrackScores / trackScores.length;
536
-
537
- const score: CalculatedScore = {
538
- remarks: [ {
539
- severity: 'none',
540
- text: `Peer Connection stability score: ${weightedStabilityScore}`,
541
- },
542
- {
543
- severity: 'none',
544
- text: `Avg. Track score: ${avgTrackScores}`,
545
- } ],
546
- score: Math.round(((weightedStabilityScore + avgTrackScores) * 5 * 100)) / 100.0,
547
- timestamp: this._statsTimestamp ?? Date.now(),
548
- };
549
-
550
- for (const pendingIssue of this.ωpendingIssuesForScores) {
551
- switch (pendingIssue.severity) {
552
- case 'critical':
553
- score.score = 0.0;
554
- break;
555
- case 'major':
556
- score.score *= 0.5;
557
- break;
558
- case 'minor':
559
- score.score *= 0.8;
560
- break;
561
- }
562
- score.remarks.push({
563
- severity: pendingIssue.severity,
564
- text: pendingIssue.description ?? 'Issue occurred',
565
- });
566
- }
567
-
568
- this.ωpendingIssuesForScores = [];
569
- this.score = score;
570
-
571
- this.emit('score', score);
572
- }
573
- }
828
+ return observedMediaPlayout;
829
+ }
830
+
831
+ private _updateMediaSourceStats(stats: MediaSourceStats) {
832
+ let observedMediaSource = this.observedMediaSources.get(stats.id);
833
+
834
+ if (!observedMediaSource) {
835
+ if (!stats.timestamp || !stats.id || !stats.kind) {
836
+ return logger.warn(
837
+ `ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
838
+ stats
839
+ );
840
+ }
841
+
842
+ observedMediaSource = new ObservedMediaSource(
843
+ stats.timestamp,
844
+ stats.id,
845
+ stats.kind as MediaKind,
846
+ this
847
+ );
848
+
849
+ this.observedMediaSources.set(stats.id, observedMediaSource);
850
+ this.emit('added-media-source', observedMediaSource);
851
+ }
852
+
853
+ observedMediaSource.update(stats);
854
+
855
+ return observedMediaSource;
856
+ }
857
+
858
+ private _updateOutboundRtpStats(stats: OutboundRtpStats) {
859
+ let observedOutboundRtp = this.observedOutboundRtps.get(stats.ssrc);
860
+
861
+ if (!observedOutboundRtp) {
862
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
863
+ return logger.warn(
864
+ `ObservedPeerConnection received an invalid OutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
865
+ stats
866
+ );
867
+ }
868
+
869
+ observedOutboundRtp = new ObservedOutboundRtp(
870
+ stats.timestamp,
871
+ stats.id,
872
+ stats.ssrc,
873
+ stats.kind as MediaKind,
874
+ this
875
+ );
876
+
877
+ this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp);
878
+ this.emit('added-outbound-rtp', observedOutboundRtp);
879
+ }
880
+
881
+ observedOutboundRtp.update(stats);
882
+
883
+ return observedOutboundRtp;
884
+ }
885
+
886
+ public _updateOutboundTrackSample(stats: OutboundTrackSample) {
887
+ let observedOutboundTrack = this.observedOutboundTracks.get(stats.id);
888
+
889
+ if (!observedOutboundTrack) {
890
+ if (!stats.timestamp || !stats.id || !stats.kind) {
891
+ return logger.warn(
892
+ `ObservedPeerConnection received an invalid OutboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
893
+ stats
894
+ );
895
+ }
896
+ const observedMediaSource = [ ...this.observedMediaSources.values() ].find((mediaSource) => mediaSource.trackIdentifier === stats.id);
897
+ const outboundRtps = observedMediaSource
898
+ ? [ ...this.observedOutboundRtps.values() ].filter((outboundRtp) => outboundRtp.mediaSourceId === observedMediaSource?.id) : undefined;
899
+
900
+ observedOutboundTrack = new ObservedOutboundTrack(
901
+ stats.timestamp,
902
+ stats.id,
903
+ stats.kind as MediaKind,
904
+ this,
905
+ outboundRtps,
906
+ observedMediaSource,
907
+ );
908
+
909
+ this.observedOutboundTracks.set(stats.id, observedOutboundTrack);
910
+ this.emit('added-outbound-track', observedOutboundTrack);
911
+ }
912
+
913
+ observedOutboundTrack.update(stats);
914
+
915
+ return observedOutboundTrack;
916
+ }
917
+
918
+ private _updatePeerConnectionTransportStats(stats: PeerConnectionTransportStats) {
919
+ let observedPeerConnectionTransport = this.observedPeerConnectionTransports.get(stats.id);
920
+
921
+ if (!observedPeerConnectionTransport) {
922
+ if (!stats.timestamp || !stats.id) {
923
+ return logger.warn(
924
+ `ObservedPeerConnection received an invalid PeerConnectionTransportStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
925
+ stats
926
+ );
927
+ }
928
+
929
+ observedPeerConnectionTransport = new ObservedPeerConnectionTransport(stats.timestamp, stats.id, this);
930
+
931
+ this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport);
932
+ this.emit('added-peer-connection-transport', observedPeerConnectionTransport);
933
+ }
934
+
935
+ observedPeerConnectionTransport.update(stats);
936
+
937
+ return observedPeerConnectionTransport;
938
+ }
939
+
940
+ private _updateRemoteInboundRtpStats(stats: RemoteInboundRtpStats) {
941
+ let observedRemoteInboundRtp = this.observedRemoteInboundRtps.get(stats.ssrc);
942
+
943
+ if (!observedRemoteInboundRtp) {
944
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
945
+ return logger.warn(
946
+ `ObservedPeerConnection received an invalid RemoteInboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
947
+ stats
948
+ );
949
+ }
950
+
951
+ observedRemoteInboundRtp = new ObservedRemoteInboundRtp(
952
+ stats.timestamp,
953
+ stats.id,
954
+ stats.ssrc,
955
+ stats.kind as MediaKind,
956
+ this
957
+ );
958
+
959
+ this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp);
960
+ this.emit('added-remote-inbound-rtp', observedRemoteInboundRtp);
961
+ }
962
+
963
+ observedRemoteInboundRtp.update(stats);
964
+
965
+ return observedRemoteInboundRtp;
966
+ }
967
+
968
+ private _updateRemoteOutboundRtpStats(stats: RemoteOutboundRtpStats) {
969
+ let observedRemoteOutboundRtp = this.observedRemoteOutboundRtps.get(stats.ssrc);
970
+
971
+ if (!observedRemoteOutboundRtp) {
972
+ if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) {
973
+ return logger.warn(
974
+ `ObservedPeerConnection received an invalid RemoteOutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`,
975
+ stats
976
+ );
977
+ }
978
+
979
+ observedRemoteOutboundRtp = new ObservedRemoteOutboundRtp(
980
+ stats.timestamp,
981
+ stats.id,
982
+ stats.ssrc,
983
+ stats.kind as MediaKind,
984
+ this
985
+ );
986
+
987
+ this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp);
988
+ this.emit('added-remote-outbound-rtp', observedRemoteOutboundRtp);
989
+ }
990
+
991
+ observedRemoteOutboundRtp.update(stats);
992
+
993
+ return observedRemoteOutboundRtp;
994
+ }
995
+ }