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

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