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