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