@webex/plugin-meetings 3.0.0-beta.39 → 3.0.0-beta.391

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 (393) hide show
  1. package/README.md +58 -8
  2. package/dist/annotation/annotation.types.js +7 -0
  3. package/dist/annotation/annotation.types.js.map +1 -0
  4. package/dist/annotation/constants.js +49 -0
  5. package/dist/annotation/constants.js.map +1 -0
  6. package/dist/annotation/index.js +342 -0
  7. package/dist/annotation/index.js.map +1 -0
  8. package/dist/breakouts/breakout.js +94 -15
  9. package/dist/breakouts/breakout.js.map +1 -1
  10. package/dist/breakouts/events.js +45 -0
  11. package/dist/breakouts/events.js.map +1 -0
  12. package/dist/breakouts/index.js +671 -81
  13. package/dist/breakouts/index.js.map +1 -1
  14. package/dist/breakouts/utils.js +45 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/errors/no-meeting-info.js +51 -0
  17. package/dist/common/errors/no-meeting-info.js.map +1 -0
  18. package/dist/common/errors/reclaim-host-role-errors.js +158 -0
  19. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  20. package/dist/common/errors/webex-errors.js +48 -7
  21. package/dist/common/errors/webex-errors.js.map +1 -1
  22. package/dist/common/logs/logger-proxy.js +1 -1
  23. package/dist/common/logs/logger-proxy.js.map +1 -1
  24. package/dist/common/logs/request.js +5 -1
  25. package/dist/common/logs/request.js.map +1 -1
  26. package/dist/common/queue.js +24 -9
  27. package/dist/common/queue.js.map +1 -1
  28. package/dist/config.js +5 -10
  29. package/dist/config.js.map +1 -1
  30. package/dist/constants.js +242 -33
  31. package/dist/constants.js.map +1 -1
  32. package/dist/controls-options-manager/enums.js +14 -2
  33. package/dist/controls-options-manager/enums.js.map +1 -1
  34. package/dist/controls-options-manager/index.js +109 -15
  35. package/dist/controls-options-manager/index.js.map +1 -1
  36. package/dist/controls-options-manager/types.js +7 -0
  37. package/dist/controls-options-manager/types.js.map +1 -0
  38. package/dist/controls-options-manager/util.js +309 -18
  39. package/dist/controls-options-manager/util.js.map +1 -1
  40. package/dist/index.js +110 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/interceptors/index.js +15 -0
  43. package/dist/interceptors/index.js.map +1 -0
  44. package/dist/interceptors/locusRetry.js +93 -0
  45. package/dist/interceptors/locusRetry.js.map +1 -0
  46. package/dist/interpretation/collection.js +23 -0
  47. package/dist/interpretation/collection.js.map +1 -0
  48. package/dist/interpretation/index.js +380 -0
  49. package/dist/interpretation/index.js.map +1 -0
  50. package/dist/interpretation/siLanguage.js +25 -0
  51. package/dist/interpretation/siLanguage.js.map +1 -0
  52. package/dist/locus-info/controlsUtils.js +91 -2
  53. package/dist/locus-info/controlsUtils.js.map +1 -1
  54. package/dist/locus-info/index.js +386 -62
  55. package/dist/locus-info/index.js.map +1 -1
  56. package/dist/locus-info/infoUtils.js +7 -1
  57. package/dist/locus-info/infoUtils.js.map +1 -1
  58. package/dist/locus-info/mediaSharesUtils.js +71 -1
  59. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  60. package/dist/locus-info/parser.js +249 -72
  61. package/dist/locus-info/parser.js.map +1 -1
  62. package/dist/locus-info/selfUtils.js +89 -14
  63. package/dist/locus-info/selfUtils.js.map +1 -1
  64. package/dist/media/index.js +65 -102
  65. package/dist/media/index.js.map +1 -1
  66. package/dist/media/properties.js +73 -124
  67. package/dist/media/properties.js.map +1 -1
  68. package/dist/mediaQualityMetrics/config.js +135 -330
  69. package/dist/mediaQualityMetrics/config.js.map +1 -1
  70. package/dist/meeting/in-meeting-actions.js +86 -2
  71. package/dist/meeting/in-meeting-actions.js.map +1 -1
  72. package/dist/meeting/index.js +4075 -2827
  73. package/dist/meeting/index.js.map +1 -1
  74. package/dist/meeting/locusMediaRequest.js +292 -0
  75. package/dist/meeting/locusMediaRequest.js.map +1 -0
  76. package/dist/meeting/muteState.js +224 -136
  77. package/dist/meeting/muteState.js.map +1 -1
  78. package/dist/meeting/request.js +177 -152
  79. package/dist/meeting/request.js.map +1 -1
  80. package/dist/meeting/util.js +672 -417
  81. package/dist/meeting/util.js.map +1 -1
  82. package/dist/meeting-info/index.js +73 -7
  83. package/dist/meeting-info/index.js.map +1 -1
  84. package/dist/meeting-info/meeting-info-v2.js +192 -51
  85. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  86. package/dist/meeting-info/util.js +1 -1
  87. package/dist/meeting-info/util.js.map +1 -1
  88. package/dist/meeting-info/utilv2.js +36 -36
  89. package/dist/meeting-info/utilv2.js.map +1 -1
  90. package/dist/meetings/collection.js +39 -0
  91. package/dist/meetings/collection.js.map +1 -1
  92. package/dist/meetings/index.js +484 -119
  93. package/dist/meetings/index.js.map +1 -1
  94. package/dist/meetings/meetings.types.js +7 -0
  95. package/dist/meetings/meetings.types.js.map +1 -0
  96. package/dist/meetings/request.js +2 -0
  97. package/dist/meetings/request.js.map +1 -1
  98. package/dist/meetings/util.js +73 -7
  99. package/dist/meetings/util.js.map +1 -1
  100. package/dist/member/index.js +58 -0
  101. package/dist/member/index.js.map +1 -1
  102. package/dist/member/types.js +25 -0
  103. package/dist/member/types.js.map +1 -0
  104. package/dist/member/util.js +132 -25
  105. package/dist/member/util.js.map +1 -1
  106. package/dist/members/collection.js +10 -0
  107. package/dist/members/collection.js.map +1 -1
  108. package/dist/members/index.js +102 -6
  109. package/dist/members/index.js.map +1 -1
  110. package/dist/members/request.js +106 -38
  111. package/dist/members/request.js.map +1 -1
  112. package/dist/members/types.js +15 -0
  113. package/dist/members/types.js.map +1 -0
  114. package/dist/members/util.js +326 -232
  115. package/dist/members/util.js.map +1 -1
  116. package/dist/metrics/constants.js +18 -1
  117. package/dist/metrics/constants.js.map +1 -1
  118. package/dist/metrics/index.js +1 -446
  119. package/dist/metrics/index.js.map +1 -1
  120. package/dist/multistream/mediaRequestManager.js +223 -32
  121. package/dist/multistream/mediaRequestManager.js.map +1 -1
  122. package/dist/multistream/receiveSlot.js +10 -0
  123. package/dist/multistream/receiveSlot.js.map +1 -1
  124. package/dist/multistream/receiveSlotManager.js +39 -36
  125. package/dist/multistream/receiveSlotManager.js.map +1 -1
  126. package/dist/multistream/remoteMedia.js +3 -1
  127. package/dist/multistream/remoteMedia.js.map +1 -1
  128. package/dist/multistream/remoteMediaGroup.js +76 -5
  129. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  130. package/dist/multistream/remoteMediaManager.js +366 -104
  131. package/dist/multistream/remoteMediaManager.js.map +1 -1
  132. package/dist/multistream/sendSlotManager.js +255 -0
  133. package/dist/multistream/sendSlotManager.js.map +1 -0
  134. package/dist/reachability/clusterReachability.js +356 -0
  135. package/dist/reachability/clusterReachability.js.map +1 -0
  136. package/dist/reachability/index.js +263 -390
  137. package/dist/reachability/index.js.map +1 -1
  138. package/dist/reachability/request.js +6 -4
  139. package/dist/reachability/request.js.map +1 -1
  140. package/dist/reachability/util.js +29 -0
  141. package/dist/reachability/util.js.map +1 -0
  142. package/dist/reconnection-manager/index.js +266 -202
  143. package/dist/reconnection-manager/index.js.map +1 -1
  144. package/dist/recording-controller/index.js +21 -2
  145. package/dist/recording-controller/index.js.map +1 -1
  146. package/dist/recording-controller/util.js +9 -8
  147. package/dist/recording-controller/util.js.map +1 -1
  148. package/dist/roap/index.js +51 -28
  149. package/dist/roap/index.js.map +1 -1
  150. package/dist/roap/request.js +48 -64
  151. package/dist/roap/request.js.map +1 -1
  152. package/dist/roap/turnDiscovery.js +220 -70
  153. package/dist/roap/turnDiscovery.js.map +1 -1
  154. package/dist/rtcMetrics/constants.js +12 -0
  155. package/dist/rtcMetrics/constants.js.map +1 -0
  156. package/dist/rtcMetrics/index.js +179 -0
  157. package/dist/rtcMetrics/index.js.map +1 -0
  158. package/dist/statsAnalyzer/index.js +357 -295
  159. package/dist/statsAnalyzer/index.js.map +1 -1
  160. package/dist/statsAnalyzer/mqaUtil.js +296 -156
  161. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  162. package/dist/types/annotation/annotation.types.d.ts +42 -0
  163. package/dist/types/annotation/constants.d.ts +31 -0
  164. package/dist/types/annotation/index.d.ts +117 -0
  165. package/dist/types/breakouts/events.d.ts +8 -0
  166. package/dist/types/breakouts/utils.d.ts +14 -0
  167. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  168. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  169. package/dist/types/common/errors/webex-errors.d.ts +25 -1
  170. package/dist/types/common/logs/request.d.ts +2 -0
  171. package/dist/types/common/queue.d.ts +9 -7
  172. package/dist/types/config.d.ts +2 -7
  173. package/dist/types/constants.d.ts +203 -31
  174. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  175. package/dist/types/controls-options-manager/index.d.ts +17 -1
  176. package/dist/types/controls-options-manager/types.d.ts +43 -0
  177. package/dist/types/controls-options-manager/util.d.ts +1 -7
  178. package/dist/types/index.d.ts +6 -5
  179. package/dist/types/interceptors/index.d.ts +2 -0
  180. package/dist/types/interceptors/locusRetry.d.ts +27 -0
  181. package/dist/types/interpretation/collection.d.ts +5 -0
  182. package/dist/types/interpretation/index.d.ts +5 -0
  183. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  184. package/dist/types/locus-info/index.d.ts +57 -4
  185. package/dist/types/locus-info/parser.d.ts +66 -6
  186. package/dist/types/media/index.d.ts +2 -0
  187. package/dist/types/media/properties.d.ts +34 -49
  188. package/dist/types/mediaQualityMetrics/config.d.ts +99 -223
  189. package/dist/types/meeting/in-meeting-actions.d.ts +86 -2
  190. package/dist/types/meeting/index.d.ts +567 -496
  191. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  192. package/dist/types/meeting/muteState.d.ts +93 -25
  193. package/dist/types/meeting/request.d.ts +64 -43
  194. package/dist/types/meeting/util.d.ts +117 -1
  195. package/dist/types/meeting-info/index.d.ts +13 -1
  196. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  197. package/dist/types/meetings/collection.d.ts +17 -0
  198. package/dist/types/meetings/index.d.ts +113 -21
  199. package/dist/types/meetings/meetings.types.d.ts +4 -0
  200. package/dist/types/member/index.d.ts +14 -0
  201. package/dist/types/member/types.d.ts +32 -0
  202. package/dist/types/members/collection.d.ts +5 -0
  203. package/dist/types/members/index.d.ts +35 -2
  204. package/dist/types/members/request.d.ts +73 -9
  205. package/dist/types/members/types.d.ts +25 -0
  206. package/dist/types/members/util.d.ts +214 -1
  207. package/dist/types/metrics/constants.d.ts +17 -0
  208. package/dist/types/metrics/index.d.ts +4 -111
  209. package/dist/types/multistream/mediaRequestManager.d.ts +72 -3
  210. package/dist/types/multistream/receiveSlot.d.ts +7 -3
  211. package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
  212. package/dist/types/multistream/remoteMedia.d.ts +3 -31
  213. package/dist/types/multistream/remoteMediaGroup.d.ts +2 -9
  214. package/dist/types/multistream/remoteMediaManager.d.ts +62 -2
  215. package/dist/types/multistream/sendSlotManager.d.ts +70 -0
  216. package/dist/types/reachability/clusterReachability.d.ts +109 -0
  217. package/dist/types/reachability/index.d.ts +60 -95
  218. package/dist/types/reachability/request.d.ts +3 -1
  219. package/dist/types/reachability/util.d.ts +8 -0
  220. package/dist/types/reconnection-manager/index.d.ts +19 -0
  221. package/dist/types/recording-controller/index.d.ts +15 -1
  222. package/dist/types/recording-controller/util.d.ts +5 -4
  223. package/dist/types/roap/index.d.ts +2 -1
  224. package/dist/types/roap/request.d.ts +9 -8
  225. package/dist/types/roap/turnDiscovery.d.ts +39 -5
  226. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  227. package/dist/types/rtcMetrics/index.d.ts +61 -0
  228. package/dist/types/statsAnalyzer/index.d.ts +34 -12
  229. package/dist/types/statsAnalyzer/mqaUtil.d.ts +28 -4
  230. package/dist/types/webinar/collection.d.ts +16 -0
  231. package/dist/types/webinar/index.d.ts +5 -0
  232. package/dist/webinar/collection.js +44 -0
  233. package/dist/webinar/collection.js.map +1 -0
  234. package/dist/webinar/index.js +69 -0
  235. package/dist/webinar/index.js.map +1 -0
  236. package/package.json +22 -19
  237. package/src/annotation/annotation.types.ts +50 -0
  238. package/src/annotation/constants.ts +36 -0
  239. package/src/annotation/index.ts +328 -0
  240. package/src/breakouts/README.md +35 -11
  241. package/src/breakouts/breakout.ts +67 -9
  242. package/src/breakouts/events.ts +56 -0
  243. package/src/breakouts/index.ts +558 -59
  244. package/src/breakouts/utils.ts +42 -0
  245. package/src/common/errors/no-meeting-info.ts +24 -0
  246. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  247. package/src/common/errors/webex-errors.ts +44 -2
  248. package/src/common/logs/logger-proxy.ts +1 -1
  249. package/src/common/logs/request.ts +5 -1
  250. package/src/common/queue.ts +22 -8
  251. package/src/config.ts +4 -9
  252. package/src/constants.ts +229 -21
  253. package/src/controls-options-manager/enums.ts +12 -0
  254. package/src/controls-options-manager/index.ts +116 -21
  255. package/src/controls-options-manager/types.ts +59 -0
  256. package/src/controls-options-manager/util.ts +294 -14
  257. package/src/index.ts +44 -0
  258. package/src/interceptors/index.ts +3 -0
  259. package/src/interceptors/locusRetry.ts +67 -0
  260. package/src/interpretation/README.md +60 -0
  261. package/src/interpretation/collection.ts +19 -0
  262. package/src/interpretation/index.ts +349 -0
  263. package/src/interpretation/siLanguage.ts +18 -0
  264. package/src/locus-info/controlsUtils.ts +108 -0
  265. package/src/locus-info/index.ts +417 -59
  266. package/src/locus-info/infoUtils.ts +10 -2
  267. package/src/locus-info/mediaSharesUtils.ts +80 -0
  268. package/src/locus-info/parser.ts +258 -47
  269. package/src/locus-info/selfUtils.ts +81 -5
  270. package/src/media/index.ts +100 -108
  271. package/src/media/properties.ts +88 -117
  272. package/src/mediaQualityMetrics/config.ts +103 -238
  273. package/src/meeting/in-meeting-actions.ts +171 -3
  274. package/src/meeting/index.ts +3411 -2435
  275. package/src/meeting/locusMediaRequest.ts +313 -0
  276. package/src/meeting/muteState.ts +223 -136
  277. package/src/meeting/request.ts +155 -120
  278. package/src/meeting/util.ts +685 -395
  279. package/src/meeting-info/index.ts +81 -8
  280. package/src/meeting-info/meeting-info-v2.ts +170 -14
  281. package/src/meeting-info/util.ts +1 -1
  282. package/src/meeting-info/utilv2.ts +23 -23
  283. package/src/meetings/collection.ts +33 -0
  284. package/src/meetings/index.ts +507 -127
  285. package/src/meetings/meetings.types.ts +12 -0
  286. package/src/meetings/request.ts +2 -0
  287. package/src/meetings/util.ts +81 -12
  288. package/src/member/index.ts +58 -0
  289. package/src/member/types.ts +38 -0
  290. package/src/member/util.ts +141 -25
  291. package/src/members/collection.ts +8 -0
  292. package/src/members/index.ts +134 -8
  293. package/src/members/request.ts +97 -17
  294. package/src/members/types.ts +29 -0
  295. package/src/members/util.ts +333 -240
  296. package/src/metrics/constants.ts +17 -0
  297. package/src/metrics/index.ts +1 -469
  298. package/src/multistream/mediaRequestManager.ts +271 -56
  299. package/src/multistream/receiveSlot.ts +11 -4
  300. package/src/multistream/receiveSlotManager.ts +34 -24
  301. package/src/multistream/remoteMedia.ts +5 -3
  302. package/src/multistream/remoteMediaGroup.ts +78 -0
  303. package/src/multistream/remoteMediaManager.ts +248 -44
  304. package/src/multistream/sendSlotManager.ts +199 -0
  305. package/src/reachability/clusterReachability.ts +320 -0
  306. package/src/reachability/index.ts +229 -346
  307. package/src/reachability/request.ts +8 -4
  308. package/src/reachability/util.ts +24 -0
  309. package/src/reconnection-manager/index.ts +128 -97
  310. package/src/recording-controller/index.ts +20 -3
  311. package/src/recording-controller/util.ts +26 -9
  312. package/src/roap/index.ts +52 -23
  313. package/src/roap/request.ts +48 -67
  314. package/src/roap/turnDiscovery.ts +147 -49
  315. package/src/rtcMetrics/constants.ts +3 -0
  316. package/src/rtcMetrics/index.ts +166 -0
  317. package/src/statsAnalyzer/index.ts +457 -416
  318. package/src/statsAnalyzer/mqaUtil.ts +317 -170
  319. package/src/webinar/collection.ts +31 -0
  320. package/src/webinar/index.ts +62 -0
  321. package/test/integration/spec/converged-space-meetings.js +60 -3
  322. package/test/integration/spec/journey.js +320 -261
  323. package/test/integration/spec/space-meeting.js +76 -3
  324. package/test/unit/spec/annotation/index.ts +418 -0
  325. package/test/unit/spec/breakouts/breakout.ts +118 -28
  326. package/test/unit/spec/breakouts/events.ts +89 -0
  327. package/test/unit/spec/breakouts/index.ts +1349 -114
  328. package/test/unit/spec/breakouts/utils.js +52 -1
  329. package/test/unit/spec/common/queue.js +31 -2
  330. package/test/unit/spec/controls-options-manager/index.js +163 -0
  331. package/test/unit/spec/controls-options-manager/util.js +576 -60
  332. package/test/unit/spec/fixture/locus.js +1 -0
  333. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  334. package/test/unit/spec/interpretation/collection.ts +15 -0
  335. package/test/unit/spec/interpretation/index.ts +625 -0
  336. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  337. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  338. package/test/unit/spec/locus-info/index.js +1363 -37
  339. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  340. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  341. package/test/unit/spec/locus-info/mediaSharesUtils.ts +41 -0
  342. package/test/unit/spec/locus-info/parser.js +116 -35
  343. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  344. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  345. package/test/unit/spec/media/index.ts +173 -81
  346. package/test/unit/spec/media/properties.ts +2 -2
  347. package/test/unit/spec/meeting/in-meeting-actions.ts +85 -3
  348. package/test/unit/spec/meeting/index.js +6821 -2172
  349. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  350. package/test/unit/spec/meeting/muteState.js +402 -212
  351. package/test/unit/spec/meeting/request.js +473 -54
  352. package/test/unit/spec/meeting/utils.js +773 -67
  353. package/test/unit/spec/meeting-info/index.js +300 -0
  354. package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
  355. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  356. package/test/unit/spec/meetings/collection.js +26 -0
  357. package/test/unit/spec/meetings/index.js +1415 -213
  358. package/test/unit/spec/meetings/utils.js +229 -2
  359. package/test/unit/spec/member/index.js +61 -6
  360. package/test/unit/spec/member/util.js +510 -34
  361. package/test/unit/spec/members/index.js +432 -1
  362. package/test/unit/spec/members/request.js +206 -27
  363. package/test/unit/spec/members/utils.js +210 -0
  364. package/test/unit/spec/metrics/index.js +1 -50
  365. package/test/unit/spec/multistream/mediaRequestManager.ts +781 -114
  366. package/test/unit/spec/multistream/receiveSlot.ts +9 -1
  367. package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
  368. package/test/unit/spec/multistream/remoteMedia.ts +2 -0
  369. package/test/unit/spec/multistream/remoteMediaGroup.ts +345 -0
  370. package/test/unit/spec/multistream/remoteMediaManager.ts +525 -0
  371. package/test/unit/spec/multistream/sendSlotManager.ts +274 -0
  372. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  373. package/test/unit/spec/reachability/index.ts +551 -14
  374. package/test/unit/spec/reachability/request.js +3 -1
  375. package/test/unit/spec/reachability/util.ts +40 -0
  376. package/test/unit/spec/reconnection-manager/index.js +171 -11
  377. package/test/unit/spec/recording-controller/index.js +294 -218
  378. package/test/unit/spec/recording-controller/util.js +223 -96
  379. package/test/unit/spec/roap/index.ts +180 -83
  380. package/test/unit/spec/roap/request.ts +100 -62
  381. package/test/unit/spec/roap/turnDiscovery.ts +388 -96
  382. package/test/unit/spec/rtcMetrics/index.ts +122 -0
  383. package/test/unit/spec/stats-analyzer/index.js +1252 -12
  384. package/test/unit/spec/webinar/collection.ts +13 -0
  385. package/test/unit/spec/webinar/index.ts +60 -0
  386. package/test/utils/integrationTestUtils.js +46 -0
  387. package/test/utils/testUtils.js +0 -57
  388. package/test/utils/webex-test-users.js +12 -4
  389. package/dist/metrics/config.js +0 -289
  390. package/dist/metrics/config.js.map +0 -1
  391. package/dist/types/metrics/config.d.ts +0 -169
  392. package/src/index.js +0 -18
  393. package/src/metrics/config.ts +0 -485
@@ -1,13 +1,13 @@
1
1
  /* eslint-disable prefer-destructuring */
2
2
 
3
- import {cloneDeep} from 'lodash';
3
+ import {cloneDeep, isEmpty} from 'lodash';
4
4
  import {ConnectionState} from '@webex/internal-media-core';
5
5
 
6
6
  import EventsScope from '../common/events/events-scope';
7
7
  import {
8
8
  DEFAULT_GET_STATS_FILTER,
9
9
  STATS,
10
- MQA_INTEVAL,
10
+ MQA_INTERVAL,
11
11
  NETWORK_TYPE,
12
12
  MEDIA_DEVICES,
13
13
  _UNKNOWN_,
@@ -18,6 +18,10 @@ import {
18
18
  emptyMqaInterval,
19
19
  emptyVideoReceive,
20
20
  emptyVideoTransmit,
21
+ emptyAudioReceiveStream,
22
+ emptyAudioTransmitStream,
23
+ emptyVideoReceiveStream,
24
+ emptyVideoTransmitStream,
21
25
  } from '../mediaQualityMetrics/config';
22
26
  import LoggerProxy from '../common/logs/logger-proxy';
23
27
 
@@ -27,7 +31,12 @@ import {
27
31
  getAudioReceiverMqa,
28
32
  getVideoSenderMqa,
29
33
  getVideoReceiverMqa,
34
+ getAudioSenderStreamMqa,
35
+ getAudioReceiverStreamMqa,
36
+ getVideoSenderStreamMqa,
37
+ getVideoReceiverStreamMqa,
30
38
  } from './mqaUtil';
39
+ import {ReceiveSlot} from '../multistream/receiveSlot';
31
40
 
32
41
  export const EVENTS = {
33
42
  MEDIA_QUALITY: 'MEDIA_QUALITY',
@@ -53,6 +62,11 @@ const emptyReceiver = {
53
62
  meanRoundTripTime: [],
54
63
  };
55
64
 
65
+ type ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;
66
+ type MediaStatus = {
67
+ actual?: any;
68
+ expected?: any;
69
+ };
56
70
  /**
57
71
  * Stats Analyzer class that will emit events based on detected quality
58
72
  *
@@ -74,17 +88,22 @@ export class StatsAnalyzer extends EventsScope {
74
88
  statsInterval: NodeJS.Timeout;
75
89
  statsResults: any;
76
90
  statsStarted: any;
91
+ successfulCandidatePair: any;
92
+ localIpAddress: string; // Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses
93
+ receiveSlotCallback: ReceiveSlotCallback;
77
94
 
78
95
  /**
79
96
  * Creates a new instance of StatsAnalyzer
80
97
  * @constructor
81
98
  * @public
82
99
  * @param {Object} config SDK Configuration Object
100
+ * @param {Function} receiveSlotCallback Callback used to access receive slots.
83
101
  * @param {Object} networkQualityMonitor class for assessing network characteristics (jitter, packetLoss, latency)
84
102
  * @param {Object} statsResults Default properties for stats
85
103
  */
86
104
  constructor(
87
105
  config: any,
106
+ receiveSlotCallback: ReceiveSlotCallback = () => undefined,
88
107
  networkQualityMonitor: object = {},
89
108
  statsResults: object = defaultStats
90
109
  ) {
@@ -98,6 +117,9 @@ export class StatsAnalyzer extends EventsScope {
98
117
  this.mqaSentCount = -1;
99
118
  this.lastMqaDataSent = {};
100
119
  this.lastEmittedStartStopEvent = {};
120
+ this.receiveSlotCallback = receiveSlotCallback;
121
+ this.successfulCandidatePair = {};
122
+ this.localIpAddress = '';
101
123
  }
102
124
 
103
125
  /**
@@ -128,8 +150,17 @@ export class StatsAnalyzer extends EventsScope {
128
150
  * @memberof StatsAnalyzer
129
151
  * @returns {void}
130
152
  */
131
- public updateMediaStatus(status: object) {
132
- this.meetingMediaStatus = status;
153
+ public updateMediaStatus(status: MediaStatus) {
154
+ this.meetingMediaStatus = {
155
+ actual: {
156
+ ...this.meetingMediaStatus?.actual,
157
+ ...status?.actual,
158
+ },
159
+ expected: {
160
+ ...this.meetingMediaStatus?.expected,
161
+ ...status?.expected,
162
+ },
163
+ };
133
164
  }
134
165
 
135
166
  /**
@@ -142,65 +173,209 @@ export class StatsAnalyzer extends EventsScope {
142
173
  sendMqaData() {
143
174
  const newMqa = cloneDeep(emptyMqaInterval);
144
175
 
176
+ // Fill in empty stats items for lastMqaDataSent
145
177
  Object.keys(this.statsResults).forEach((mediaType) => {
146
- if (mediaType.includes('audio-send') || mediaType.includes('audio-share-send')) {
147
- const audioSender = cloneDeep(emptyAudioTransmit);
178
+ if (!this.lastMqaDataSent[mediaType]) {
179
+ this.lastMqaDataSent[mediaType] = {};
180
+ }
181
+
182
+ if (!this.lastMqaDataSent[mediaType].send && mediaType.includes('-send')) {
183
+ this.lastMqaDataSent[mediaType].send = {};
184
+ }
185
+
186
+ if (!this.lastMqaDataSent[mediaType].recv && mediaType.includes('-recv')) {
187
+ this.lastMqaDataSent[mediaType].recv = {};
188
+ }
189
+ });
190
+
191
+ // Create stats the first level, totals for senders and receivers
192
+ const audioSender = cloneDeep(emptyAudioTransmit);
193
+ const audioShareSender = cloneDeep(emptyAudioTransmit);
194
+ const audioReceiver = cloneDeep(emptyAudioReceive);
195
+ const audioShareReceiver = cloneDeep(emptyAudioReceive);
196
+ const videoSender = cloneDeep(emptyVideoTransmit);
197
+ const videoShareSender = cloneDeep(emptyVideoTransmit);
198
+ const videoReceiver = cloneDeep(emptyVideoReceive);
199
+ const videoShareReceiver = cloneDeep(emptyVideoReceive);
200
+
201
+ getAudioSenderMqa({
202
+ audioSender,
203
+ statsResults: this.statsResults,
204
+ lastMqaDataSent: this.lastMqaDataSent,
205
+ baseMediaType: 'audio-send',
206
+ });
207
+ newMqa.audioTransmit.push(audioSender);
208
+
209
+ getAudioSenderMqa({
210
+ audioSender: audioShareSender,
211
+ statsResults: this.statsResults,
212
+ lastMqaDataSent: this.lastMqaDataSent,
213
+ baseMediaType: 'audio-share-send',
214
+ });
215
+ newMqa.audioTransmit.push(audioShareSender);
216
+
217
+ getAudioReceiverMqa({
218
+ audioReceiver,
219
+ statsResults: this.statsResults,
220
+ lastMqaDataSent: this.lastMqaDataSent,
221
+ baseMediaType: 'audio-recv',
222
+ });
223
+ newMqa.audioReceive.push(audioReceiver);
148
224
 
149
- getAudioSenderMqa({
150
- audioSender,
225
+ getAudioReceiverMqa({
226
+ audioReceiver: audioShareReceiver,
227
+ statsResults: this.statsResults,
228
+ lastMqaDataSent: this.lastMqaDataSent,
229
+ baseMediaType: 'audio-share-recv',
230
+ });
231
+ newMqa.audioReceive.push(audioShareReceiver);
232
+
233
+ getVideoSenderMqa({
234
+ videoSender,
235
+ statsResults: this.statsResults,
236
+ lastMqaDataSent: this.lastMqaDataSent,
237
+ baseMediaType: 'video-send',
238
+ });
239
+ newMqa.videoTransmit.push(videoSender);
240
+
241
+ getVideoSenderMqa({
242
+ videoSender: videoShareSender,
243
+ statsResults: this.statsResults,
244
+ lastMqaDataSent: this.lastMqaDataSent,
245
+ baseMediaType: 'video-share-send',
246
+ });
247
+ newMqa.videoTransmit.push(videoShareSender);
248
+
249
+ getVideoReceiverMqa({
250
+ videoReceiver,
251
+ statsResults: this.statsResults,
252
+ lastMqaDataSent: this.lastMqaDataSent,
253
+ baseMediaType: 'video-recv',
254
+ });
255
+ newMqa.videoReceive.push(videoReceiver);
256
+
257
+ getVideoReceiverMqa({
258
+ videoReceiver: videoShareReceiver,
259
+ statsResults: this.statsResults,
260
+ lastMqaDataSent: this.lastMqaDataSent,
261
+ baseMediaType: 'video-share-recv',
262
+ });
263
+ newMqa.videoReceive.push(videoShareReceiver);
264
+
265
+ // Add stats for individual streams
266
+ Object.keys(this.statsResults).forEach((mediaType) => {
267
+ if (mediaType.includes('audio-send')) {
268
+ const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
269
+
270
+ getAudioSenderStreamMqa({
271
+ audioSenderStream,
151
272
  statsResults: this.statsResults,
152
273
  lastMqaDataSent: this.lastMqaDataSent,
153
274
  mediaType,
154
275
  });
155
- newMqa.audioTransmit.push(audioSender);
156
- } else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {
157
- const audioReceiver = cloneDeep(emptyAudioReceive);
276
+ newMqa.audioTransmit[0].streams.push(audioSenderStream);
158
277
 
159
- getAudioReceiverMqa({
160
- audioReceiver,
278
+ this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
279
+ } else if (mediaType.includes('audio-share-send')) {
280
+ const audioSenderStream = cloneDeep(emptyAudioTransmitStream);
281
+
282
+ getAudioSenderStreamMqa({
283
+ audioSenderStream,
161
284
  statsResults: this.statsResults,
162
285
  lastMqaDataSent: this.lastMqaDataSent,
163
286
  mediaType,
164
287
  });
165
- newMqa.audioReceive.push(audioReceiver);
166
- } else if (mediaType.includes('video-send') || mediaType.includes('video-share-send')) {
167
- const videoSender = cloneDeep(emptyVideoTransmit);
288
+ newMqa.audioTransmit[1].streams.push(audioSenderStream);
289
+
290
+ this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
291
+ } else if (mediaType.includes('audio-recv')) {
292
+ const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
168
293
 
169
- getVideoSenderMqa({
170
- videoSender,
294
+ getAudioReceiverStreamMqa({
295
+ audioReceiverStream,
171
296
  statsResults: this.statsResults,
172
297
  lastMqaDataSent: this.lastMqaDataSent,
173
298
  mediaType,
174
299
  });
175
- newMqa.videoTransmit.push(videoSender);
176
- } else if (mediaType.includes('video-recv') || mediaType.includes('video-share-recv')) {
177
- const videoReceiver = cloneDeep(emptyVideoReceive);
300
+ newMqa.audioReceive[0].streams.push(audioReceiverStream);
301
+
302
+ this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
303
+ } else if (mediaType.includes('audio-share-recv')) {
304
+ const audioReceiverStream = cloneDeep(emptyAudioReceiveStream);
178
305
 
179
- getVideoReceiverMqa({
180
- videoReceiver,
306
+ getAudioReceiverStreamMqa({
307
+ audioReceiverStream,
181
308
  statsResults: this.statsResults,
182
309
  lastMqaDataSent: this.lastMqaDataSent,
183
310
  mediaType,
184
311
  });
185
- newMqa.videoReceive.push(videoReceiver);
312
+ newMqa.audioReceive[1].streams.push(audioReceiverStream);
313
+
314
+ this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
315
+ } else if (mediaType.includes('video-send')) {
316
+ const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
317
+
318
+ getVideoSenderStreamMqa({
319
+ videoSenderStream,
320
+ statsResults: this.statsResults,
321
+ lastMqaDataSent: this.lastMqaDataSent,
322
+ mediaType,
323
+ });
324
+ newMqa.videoTransmit[0].streams.push(videoSenderStream);
325
+
326
+ this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
327
+ } else if (mediaType.includes('video-share-send')) {
328
+ const videoSenderStream = cloneDeep(emptyVideoTransmitStream);
329
+
330
+ getVideoSenderStreamMqa({
331
+ videoSenderStream,
332
+ statsResults: this.statsResults,
333
+ lastMqaDataSent: this.lastMqaDataSent,
334
+ mediaType,
335
+ });
336
+ newMqa.videoTransmit[1].streams.push(videoSenderStream);
337
+
338
+ this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);
339
+ } else if (mediaType.includes('video-recv')) {
340
+ const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
341
+
342
+ getVideoReceiverStreamMqa({
343
+ videoReceiverStream,
344
+ statsResults: this.statsResults,
345
+ lastMqaDataSent: this.lastMqaDataSent,
346
+ mediaType,
347
+ });
348
+ newMqa.videoReceive[0].streams.push(videoReceiverStream);
349
+
350
+ this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
351
+ } else if (mediaType.includes('video-share-recv')) {
352
+ const videoReceiverStream = cloneDeep(emptyVideoReceiveStream);
353
+
354
+ getVideoReceiverStreamMqa({
355
+ videoReceiverStream,
356
+ statsResults: this.statsResults,
357
+ lastMqaDataSent: this.lastMqaDataSent,
358
+ mediaType,
359
+ });
360
+ newMqa.videoReceive[1].streams.push(videoReceiverStream);
361
+
362
+ this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);
186
363
  }
187
364
  });
188
365
 
189
- newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress[0];
366
+ newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress;
190
367
 
191
368
  // Adding peripheral information
192
- newMqa.intervalMetadata.peripherals = [];
193
-
194
369
  newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});
195
370
  if (this.statsResults['audio-send']) {
196
371
  newMqa.intervalMetadata.peripherals.push({
197
- information: this.statsResults['audio-send']?.trackLabel,
372
+ information: this.statsResults['audio-send'].trackLabel || _UNKNOWN_,
198
373
  name: MEDIA_DEVICES.MICROPHONE,
199
374
  });
200
375
  }
201
376
  if (this.statsResults['video-send']) {
202
377
  newMqa.intervalMetadata.peripherals.push({
203
- information: this.statsResults['video-send']?.trackLabel,
378
+ information: this.statsResults['video-send'].trackLabel || _UNKNOWN_,
204
379
  name: MEDIA_DEVICES.CAMERA,
205
380
  });
206
381
  }
@@ -239,6 +414,16 @@ export class StatsAnalyzer extends EventsScope {
239
414
  this.mediaConnection = mediaConnection;
240
415
  }
241
416
 
417
+ /**
418
+ * Returns the local IP address for diagnostics.
419
+ * this is the local IP of the interface used for the current media connection
420
+ * a host can have many local Ip Addresses
421
+ * @returns {string | undefined} The local IP address.
422
+ */
423
+ getLocalIpAddress(): string {
424
+ return this.localIpAddress;
425
+ }
426
+
242
427
  /**
243
428
  * Starts the stats analyzer on interval
244
429
  *
@@ -260,7 +445,7 @@ export class StatsAnalyzer extends EventsScope {
260
445
  this.sendMqaData();
261
446
  this.mqaInterval = setInterval(() => {
262
447
  this.sendMqaData();
263
- }, MQA_INTEVAL);
448
+ }, MQA_INTERVAL);
264
449
  });
265
450
  }
266
451
 
@@ -293,7 +478,6 @@ export class StatsAnalyzer extends EventsScope {
293
478
  this.mediaConnection = null;
294
479
  });
295
480
  }
296
- this.mediaConnection = null;
297
481
 
298
482
  return Promise.resolve();
299
483
  }
@@ -324,26 +508,6 @@ export class StatsAnalyzer extends EventsScope {
324
508
  this.statsResults[type].recv = cloneDeep(emptyReceiver);
325
509
  }
326
510
 
327
- if (!this.statsResults.resolutions[type]) {
328
- this.statsResults.resolutions[type] = {};
329
- }
330
-
331
- if (isSender && !this.statsResults.resolutions[type].send) {
332
- this.statsResults.resolutions[type].send = cloneDeep(emptySender);
333
- } else if (!isSender && !this.statsResults.resolutions[type].recv) {
334
- this.statsResults.resolutions[type].recv = cloneDeep(emptyReceiver);
335
- }
336
-
337
- if (!this.statsResults.internal[type]) {
338
- this.statsResults.internal[type] = {};
339
- }
340
-
341
- if (isSender && !this.statsResults.internal[type].send) {
342
- this.statsResults.internal[type].send = cloneDeep(emptySender);
343
- } else if (!isSender && !this.statsResults.internal[type].recv) {
344
- this.statsResults.internal[type].recv = cloneDeep(emptyReceiver);
345
- }
346
-
347
511
  switch (getStatsResult.type) {
348
512
  case 'outbound-rtp':
349
513
  this.processOutboundRTPResult(getStatsResult, type);
@@ -351,13 +515,9 @@ export class StatsAnalyzer extends EventsScope {
351
515
  case 'inbound-rtp':
352
516
  this.processInboundRTPResult(getStatsResult, type);
353
517
  break;
354
- case 'track':
355
- this.processTrackResult(getStatsResult, type);
356
- break;
357
518
  case 'remote-inbound-rtp':
358
519
  case 'remote-outbound-rtp':
359
- // @ts-ignore
360
- this.compareSentAndReceived(getStatsResult, type, isSender);
520
+ this.compareSentAndReceived(getStatsResult, type);
361
521
  break;
362
522
  case 'remotecandidate':
363
523
  case 'remote-candidate':
@@ -367,7 +527,6 @@ export class StatsAnalyzer extends EventsScope {
367
527
  this.parseCandidate(getStatsResult, type, isSender, false);
368
528
  break;
369
529
  case 'media-source':
370
- // @ts-ignore
371
530
  this.parseAudioSource(getStatsResult, type);
372
531
  break;
373
532
  default:
@@ -386,6 +545,13 @@ export class StatsAnalyzer extends EventsScope {
386
545
  filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {
387
546
  const {types} = DEFAULT_GET_STATS_FILTER;
388
547
 
548
+ // get the successful candidate pair before parsing stats.
549
+ statsItem.report.forEach((report) => {
550
+ if (report.type === 'candidate-pair' && report.state === 'succeeded') {
551
+ this.successfulCandidatePair = report;
552
+ }
553
+ });
554
+
389
555
  statsItem.report.forEach((result) => {
390
556
  if (types.includes(result.type)) {
391
557
  this.parseGetStatsResult(result, type, isSender);
@@ -396,6 +562,12 @@ export class StatsAnalyzer extends EventsScope {
396
562
  this.statsResults[type].direction = statsItem.currentDirection;
397
563
  this.statsResults[type].trackLabel = statsItem.localTrackLabel;
398
564
  this.statsResults[type].csi = statsItem.csi;
565
+ this.extractAndSetLocalIpAddressInfoForDiagnostics(
566
+ this.successfulCandidatePair?.localCandidateId,
567
+ this.statsResults?.candidates
568
+ );
569
+ // reset the successful candidate pair.
570
+ this.successfulCandidatePair = {};
399
571
  }
400
572
  }
401
573
 
@@ -499,43 +671,35 @@ export class StatsAnalyzer extends EventsScope {
499
671
  .filter((key) => key.startsWith(keyPrefix))
500
672
  .reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);
501
673
 
502
- const getCurrentResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
503
- Object.keys(this.statsResults)
504
- .filter((key) => key.startsWith(keyPrefix))
505
- .reduce((prev, cur) => prev + (this.statsResults.resolutions[cur]?.recv[value] || 0), 0);
506
-
507
- const getPreviousResolutionsStatsTotals = (keyPrefix: string, value: string): number =>
508
- Object.keys(this.statsResults)
509
- .filter((key) => key.startsWith(keyPrefix))
510
- .reduce(
511
- (prev, cur) => prev + (this.lastStatsResults.resolutions[cur]?.recv[value] || 0),
512
- 0
513
- );
514
-
515
- if (this.meetingMediaStatus.expected.sendAudio && this.lastStatsResults['audio-send']) {
674
+ // Audio Transmit
675
+ if (this.lastStatsResults['audio-send']) {
516
676
  // compare audio stats sent
517
677
  // NOTE: relies on there being only one sender.
518
678
  const currentStats = this.statsResults['audio-send'].send;
519
679
  const previousStats = this.lastStatsResults['audio-send'].send;
520
680
 
521
681
  if (
522
- currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
682
+ (this.meetingMediaStatus.expected.sendAudio &&
683
+ currentStats.totalPacketsSent === previousStats.totalPacketsSent) ||
523
684
  currentStats.totalPacketsSent === 0
524
685
  ) {
525
686
  LoggerProxy.logger.info(
526
- `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`
687
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,
688
+ currentStats.totalPacketsSent
527
689
  );
528
690
  } else {
529
691
  if (
530
- currentStats.totalAudioEnergy === previousStats.totalAudioEnergy ||
692
+ (this.meetingMediaStatus.expected.sendAudio &&
693
+ currentStats.totalAudioEnergy === previousStats.totalAudioEnergy) ||
531
694
  currentStats.totalAudioEnergy === 0
532
695
  ) {
533
696
  LoggerProxy.logger.info(
534
- `StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`
697
+ `StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,
698
+ currentStats.totalAudioEnergy
535
699
  );
536
700
  }
537
701
 
538
- if (currentStats.audioLevel === 0) {
702
+ if (this.meetingMediaStatus.expected.sendAudio && currentStats.audioLevel === 0) {
539
703
  LoggerProxy.logger.info(
540
704
  `StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`
541
705
  );
@@ -550,64 +714,59 @@ export class StatsAnalyzer extends EventsScope {
550
714
  );
551
715
  }
552
716
 
553
- if (this.meetingMediaStatus.expected.receiveAudio) {
554
- // compare audio stats received
555
- const currentPacketsReceived = getCurrentStatsTotals('audio-recv', 'totalPacketsReceived');
556
- const previousPacketsReceived = getPreviousStatsTotals(
557
- 'audio-recv',
558
- 'totalPacketsReceived'
559
- );
560
- const currentSamplesReceived = getCurrentStatsTotals('audio-recv', 'totalSamplesReceived');
561
- const previousSamplesReceived = getPreviousStatsTotals(
562
- 'audio-recv',
563
- 'totalSamplesReceived'
564
- );
565
-
566
- if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
567
- LoggerProxy.logger.info(
568
- `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`
569
- );
570
- } else if (
571
- currentSamplesReceived === previousSamplesReceived ||
572
- currentSamplesReceived === 0
573
- ) {
574
- LoggerProxy.logger.info(
575
- `StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`
576
- );
577
- }
717
+ // Audio Receive
718
+ const currentAudioPacketsReceived = getCurrentStatsTotals(
719
+ 'audio-recv',
720
+ 'totalPacketsReceived'
721
+ );
722
+ const previousAudioPacketsReceived = getPreviousStatsTotals(
723
+ 'audio-recv',
724
+ 'totalPacketsReceived'
725
+ );
578
726
 
579
- this.emitStartStopEvents('audio', previousPacketsReceived, currentPacketsReceived, false);
580
- }
727
+ this.emitStartStopEvents(
728
+ 'audio',
729
+ previousAudioPacketsReceived,
730
+ currentAudioPacketsReceived,
731
+ false
732
+ );
581
733
 
582
- if (this.meetingMediaStatus.expected.sendVideo && this.lastStatsResults['video-send']) {
734
+ // Video Transmit
735
+ if (this.lastStatsResults['video-send']) {
583
736
  // compare video stats sent
584
737
  const currentStats = this.statsResults['video-send'].send;
585
738
  const previousStats = this.lastStatsResults['video-send'].send;
586
739
 
587
740
  if (
588
- currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
589
- currentStats.totalPacketsSent === 0
741
+ this.meetingMediaStatus.expected.sendVideo &&
742
+ (currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
743
+ currentStats.totalPacketsSent === 0)
590
744
  ) {
591
745
  LoggerProxy.logger.info(
592
- `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`
746
+ `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,
747
+ currentStats.totalPacketsSent
593
748
  );
594
749
  } else {
595
750
  if (
596
- currentStats.framesEncoded === previousStats.framesEncoded ||
597
- currentStats.framesEncoded === 0
751
+ this.meetingMediaStatus.expected.sendVideo &&
752
+ (currentStats.framesEncoded === previousStats.framesEncoded ||
753
+ currentStats.framesEncoded === 0)
598
754
  ) {
599
755
  LoggerProxy.logger.info(
600
- `StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`
756
+ `StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,
757
+ currentStats.framesEncoded
601
758
  );
602
759
  }
603
760
 
604
761
  if (
605
- this.statsResults.resolutions['video-send'].send.framesSent ===
606
- this.lastStatsResults.resolutions['video-send'].send.framesSent ||
607
- this.statsResults.resolutions['video-send'].send.framesSent === 0
762
+ this.meetingMediaStatus.expected.sendVideo &&
763
+ (this.statsResults['video-send'].send.framesSent ===
764
+ this.lastStatsResults['video-send'].send.framesSent ||
765
+ this.statsResults['video-send'].send.framesSent === 0)
608
766
  ) {
609
767
  LoggerProxy.logger.info(
610
- `StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`
768
+ `StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,
769
+ this.statsResults['video-send'].send.framesSent
611
770
  );
612
771
  }
613
772
  }
@@ -615,152 +774,74 @@ export class StatsAnalyzer extends EventsScope {
615
774
  this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);
616
775
  }
617
776
 
618
- if (this.meetingMediaStatus.expected.receiveVideo) {
619
- // compare video stats received
620
- const currentPacketsReceived = getCurrentStatsTotals('video-recv', 'totalPacketsReceived');
621
- const previousPacketsReceived = getPreviousStatsTotals(
622
- 'video-recv',
623
- 'totalPacketsReceived'
624
- );
625
- const currentFramesReceived = getCurrentResolutionsStatsTotals(
626
- 'video-recv',
627
- 'framesReceived'
628
- );
629
- const previousFramesReceived = getPreviousResolutionsStatsTotals(
630
- 'video-recv',
631
- 'framesReceived'
632
- );
633
- const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
634
- const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
635
- const currentFramesDropped = getCurrentResolutionsStatsTotals(
636
- 'video-recv',
637
- 'framesDropped'
638
- );
639
- const previousFramesDropped = getPreviousResolutionsStatsTotals(
640
- 'video-recv',
641
- 'framesDropped'
642
- );
643
-
644
- if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
645
- LoggerProxy.logger.info(
646
- `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`
647
- );
648
- } else {
649
- if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
650
- LoggerProxy.logger.info(
651
- `StatsAnalyzer:index#compareLastStatsResult --> No video frames received`
652
- );
653
- }
654
-
655
- if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
656
- LoggerProxy.logger.info(
657
- `StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`
658
- );
659
- }
777
+ // Video Receive
778
+ const currentVideoFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');
779
+ const previousVideoFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');
660
780
 
661
- if (currentFramesDropped - previousFramesDropped > 10) {
662
- LoggerProxy.logger.info(
663
- `StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`
664
- );
665
- }
666
- }
667
-
668
- this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);
669
- }
781
+ this.emitStartStopEvents(
782
+ 'video',
783
+ previousVideoFramesDecoded,
784
+ currentVideoFramesDecoded,
785
+ false
786
+ );
670
787
 
671
- if (this.meetingMediaStatus.expected.sendShare && this.lastStatsResults['video-share-send']) {
788
+ // Share Transmit
789
+ if (this.lastStatsResults['video-share-send']) {
672
790
  // compare share stats sent
673
791
 
674
792
  const currentStats = this.statsResults['video-share-send'].send;
675
793
  const previousStats = this.lastStatsResults['video-share-send'].send;
676
794
 
677
795
  if (
678
- currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
679
- currentStats.totalPacketsSent === 0
796
+ this.meetingMediaStatus.expected.sendShare &&
797
+ (currentStats.totalPacketsSent === previousStats.totalPacketsSent ||
798
+ currentStats.totalPacketsSent === 0)
680
799
  ) {
681
800
  LoggerProxy.logger.info(
682
- `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`
801
+ `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,
802
+ currentStats.totalPacketsSent
683
803
  );
684
804
  } else {
685
805
  if (
686
- currentStats.framesEncoded === previousStats.framesEncoded ||
687
- currentStats.framesEncoded === 0
806
+ this.meetingMediaStatus.expected.sendShare &&
807
+ (currentStats.framesEncoded === previousStats.framesEncoded ||
808
+ currentStats.framesEncoded === 0)
688
809
  ) {
689
810
  LoggerProxy.logger.info(
690
- `StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`
811
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,
812
+ currentStats.framesEncoded
691
813
  );
692
814
  }
693
815
 
694
816
  if (
695
- this.statsResults.resolutions['video-share-send'].send.framesSent ===
696
- this.lastStatsResults.resolutions['video-share-send'].send.framesSent ||
697
- this.statsResults.resolutions['video-share-send'].send.framesSent === 0
817
+ this.meetingMediaStatus.expected.sendShare &&
818
+ (this.statsResults['video-share-send'].send.framesSent ===
819
+ this.lastStatsResults['video-share-send'].send.framesSent ||
820
+ this.statsResults['video-share-send'].send.framesSent === 0)
698
821
  ) {
699
822
  LoggerProxy.logger.info(
700
- `StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`
823
+ `StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,
824
+ this.statsResults['video-share-send'].send.framesSent
701
825
  );
702
826
  }
703
827
  }
704
- }
705
828
 
706
- if (this.meetingMediaStatus.expected.sendShare) {
707
- // TODO:need to check receive share value
708
- // compare share stats received
709
- const currentPacketsReceived = getCurrentStatsTotals(
710
- 'video-share-recv',
711
- 'totalPacketsReceived'
712
- );
713
- const previousPacketsReceived = getPreviousStatsTotals(
714
- 'video-share-recv',
715
- 'totalPacketsReceived'
716
- );
717
- const currentFramesReceived = getCurrentResolutionsStatsTotals(
718
- 'video-share-recv',
719
- 'framesReceived'
720
- );
721
- const previousFramesReceived = getPreviousResolutionsStatsTotals(
722
- 'video-share-recv',
723
- 'framesReceived'
724
- );
725
- const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
726
- const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');
727
- const currentFramesDropped = getCurrentResolutionsStatsTotals(
728
- 'video-share-recv',
729
- 'framesDropped'
730
- );
731
- const previousFramesDropped = getPreviousResolutionsStatsTotals(
732
- 'video-share-recv',
733
- 'framesDropped'
734
- );
735
-
736
- if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {
737
- LoggerProxy.logger.info(
738
- `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`
739
- );
740
- } else {
741
- if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {
742
- LoggerProxy.logger.info(
743
- `StatsAnalyzer:index#compareLastStatsResult --> No share frames received`
744
- );
745
- }
746
-
747
- if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {
748
- LoggerProxy.logger.info(
749
- `StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`
750
- );
751
- }
829
+ this.emitStartStopEvents('share', previousStats.framesSent, currentStats.framesSent, true);
830
+ }
752
831
 
753
- if (currentFramesDropped - previousFramesDropped > 10) {
754
- LoggerProxy.logger.info(
755
- `StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped`
756
- );
757
- }
758
- }
832
+ // Share receive
833
+ const currentShareFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');
834
+ const previousShareFramesDecoded = getPreviousStatsTotals(
835
+ 'video-share-recv',
836
+ 'framesDecoded'
837
+ );
759
838
 
760
- // we are not calling emitStartStopEvents() for sending or receiving share because sharing is often started and stopped
761
- // in meetings and this.meetingMediaStatus.expected values can be out of sync with the actual packet flow
762
- // so we would send "sharing stopped" events incorrectly
763
- }
839
+ this.emitStartStopEvents(
840
+ 'share',
841
+ previousShareFramesDecoded,
842
+ currentShareFramesDecoded,
843
+ false
844
+ );
764
845
  }
765
846
  }
766
847
 
@@ -851,43 +932,20 @@ export class StatsAnalyzer extends EventsScope {
851
932
  const sendrecvType = STATS.SEND_DIRECTION;
852
933
 
853
934
  if (result.bytesSent) {
854
- let kilobytes = 0;
935
+ const kilobytes = 0;
855
936
 
856
- if (!this.statsResults.internal[mediaType][sendrecvType].prevBytesSent) {
857
- this.statsResults.internal[mediaType][sendrecvType].prevBytesSent = result.bytesSent;
858
- }
859
- if (!this.statsResults.internal[mediaType][sendrecvType].framesEncoded) {
860
- this.statsResults.internal[mediaType][sendrecvType].framesEncoded = result.framesEncoded;
861
- }
862
- if (!this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded) {
863
- this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded =
864
- result.keyFramesEncoded;
937
+ if (result.frameWidth && result.frameHeight) {
938
+ this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
939
+ this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
940
+ this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;
941
+ this.statsResults[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;
865
942
  }
866
943
 
867
- const bytes =
868
- result.bytesSent - this.statsResults.internal[mediaType][sendrecvType].prevBytesSent;
869
-
870
- this.statsResults.internal[mediaType][sendrecvType].prevBytesSent = result.bytesSent;
871
-
872
- kilobytes = bytes / 1024;
873
-
874
944
  this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
875
- this.statsResults[mediaType].bytesSent = kilobytes;
876
-
877
- this.statsResults[mediaType][sendrecvType].framesEncoded =
878
- result.framesEncoded - this.statsResults.internal[mediaType][sendrecvType].framesEncoded;
879
- this.statsResults[mediaType][sendrecvType].keyFramesEncoded =
880
- result.keyFramesEncoded -
881
- this.statsResults.internal[mediaType][sendrecvType].keyFramesEncoded;
882
- this.statsResults.internal[mediaType].outboundRtpId = result.id;
883
-
884
- if (!this.statsResults.internal[mediaType][sendrecvType].packetsSent) {
885
- this.statsResults.internal[mediaType][sendrecvType].packetsSent = result.packetsSent;
886
- }
887
945
 
888
- this.statsResults[mediaType][sendrecvType].packetsSent =
889
- result.packetsSent - this.statsResults.internal[mediaType][sendrecvType].packetsSent;
890
- this.statsResults.internal[mediaType][sendrecvType].packetsSent = result.packetsSent;
946
+ this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;
947
+ this.statsResults[mediaType][sendrecvType].keyFramesEncoded = result.keyFramesEncoded;
948
+ this.statsResults[mediaType][sendrecvType].packetsSent = result.packetsSent;
891
949
 
892
950
  // Data saved to send MQA metrics
893
951
 
@@ -925,75 +983,92 @@ export class StatsAnalyzer extends EventsScope {
925
983
 
926
984
  if (result.bytesReceived) {
927
985
  let kilobytes = 0;
986
+ const receiveSlot = this.receiveSlotCallback(result.ssrc);
987
+ const sourceState = receiveSlot?.sourceState;
988
+ const idAndCsi = receiveSlot
989
+ ? `id: "${receiveSlot.id || ''}"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`
990
+ : '';
928
991
 
929
- if (!this.statsResults.internal[mediaType][sendrecvType].prevBytesReceived) {
930
- this.statsResults.internal[mediaType][sendrecvType].prevBytesReceived =
931
- result.bytesReceived;
932
- }
992
+ const bytes =
993
+ result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;
933
994
 
934
- if (!this.statsResults.internal[mediaType][sendrecvType].pliCount) {
935
- this.statsResults.internal[mediaType][sendrecvType].pliCount = result.pliCount;
936
- }
995
+ kilobytes = bytes / 1024;
996
+ this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
937
997
 
938
- if (!this.statsResults.internal[mediaType][sendrecvType].packetsLost) {
939
- this.statsResults.internal[mediaType][sendrecvType].packetsLost = result.packetsLost;
998
+ let currentPacketsLost =
999
+ result.packetsLost - this.statsResults[mediaType][sendrecvType].totalPacketsLost;
1000
+ if (currentPacketsLost < 0) {
1001
+ currentPacketsLost = 0;
940
1002
  }
1003
+ const packetsReceivedDiff =
1004
+ result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;
1005
+ this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;
941
1006
 
942
- if (!this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived) {
943
- this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived =
944
- result.packetsReceived;
1007
+ if (packetsReceivedDiff === 0) {
1008
+ if (receiveSlot && sourceState === 'live') {
1009
+ LoggerProxy.logger.info(
1010
+ `StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total packets received on slot: `,
1011
+ result.packetsReceived
1012
+ );
1013
+ }
945
1014
  }
946
1015
 
947
- if (!this.statsResults.internal[mediaType][sendrecvType].lastPacketReceivedTimestamp) {
948
- this.statsResults.internal[mediaType][sendrecvType].lastPacketReceivedTimestamp =
949
- result.lastPacketReceivedTimestamp;
950
- }
1016
+ if (mediaType.startsWith('video') || mediaType.startsWith('share')) {
1017
+ const videoFramesReceivedDiff =
1018
+ result.framesReceived - this.statsResults[mediaType][sendrecvType].framesReceived;
951
1019
 
952
- const bytes =
953
- result.bytesReceived -
954
- this.statsResults.internal[mediaType][sendrecvType].prevBytesReceived;
1020
+ if (videoFramesReceivedDiff === 0) {
1021
+ if (receiveSlot && sourceState === 'live') {
1022
+ LoggerProxy.logger.info(
1023
+ `StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames received on slot: `,
1024
+ result.framesReceived
1025
+ );
1026
+ }
1027
+ }
955
1028
 
956
- this.statsResults.internal[mediaType][sendrecvType].prevBytesReceived = result.bytesReceived;
1029
+ const videoFramesDecodedDiff =
1030
+ result.framesDecoded - this.statsResults[mediaType][sendrecvType].framesDecoded;
957
1031
 
958
- kilobytes = bytes / 1024;
959
- this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);
960
- this.statsResults[mediaType].bytesReceived = kilobytes.toFixed(1);
961
-
962
- this.statsResults[mediaType][sendrecvType].pliCount =
963
- result.pliCount - this.statsResults.internal[mediaType][sendrecvType].pliCount;
964
- this.statsResults[mediaType][sendrecvType].currentPacketsLost =
965
- result.packetsLost - this.statsResults.internal[mediaType][sendrecvType].packetsLost;
966
- if (this.statsResults[mediaType][sendrecvType].currentPacketsLost < 0) {
967
- this.statsResults[mediaType][sendrecvType].currentPacketsLost = 0;
968
- }
1032
+ if (videoFramesDecodedDiff === 0) {
1033
+ if (receiveSlot && sourceState === 'live') {
1034
+ LoggerProxy.logger.info(
1035
+ `StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames decoded on slot: `,
1036
+ result.framesDecoded
1037
+ );
1038
+ }
1039
+ }
969
1040
 
970
- this.statsResults[mediaType][sendrecvType].packetsReceived =
971
- result.packetsReceived -
972
- this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived;
973
- this.statsResults.internal[mediaType][sendrecvType].totalPacketsReceived =
974
- result.packetsReceived;
1041
+ const videoFramesDroppedDiff =
1042
+ result.framesDropped - this.statsResults[mediaType][sendrecvType].framesDropped;
975
1043
 
976
- if (this.statsResults[mediaType][sendrecvType].packetsReceived === 0) {
977
- LoggerProxy.logger.info(
978
- `StatsAnalyzer:index#processInboundRTPResult --> No packets received for ${mediaType} `,
979
- this.statsResults[mediaType][sendrecvType].packetsReceived
980
- );
1044
+ if (videoFramesDroppedDiff > 10) {
1045
+ if (receiveSlot && sourceState === 'live') {
1046
+ LoggerProxy.logger.info(
1047
+ `StatsAnalyzer:index#processInboundRTPResult --> Frames dropped for mediaType: ${mediaType}, receive slot ${idAndCsi}. Total frames dropped on slot: `,
1048
+ result.framesDropped
1049
+ );
1050
+ }
1051
+ }
981
1052
  }
982
1053
 
983
1054
  // Check the over all packet Lost ratio
984
1055
  this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =
985
- this.statsResults[mediaType][sendrecvType].currentPacketsLost > 0
986
- ? this.statsResults[mediaType][sendrecvType].currentPacketsLost /
987
- (this.statsResults[mediaType][sendrecvType].packetsReceived +
988
- this.statsResults[mediaType][sendrecvType].currentPacketsLost)
1056
+ currentPacketsLost > 0
1057
+ ? currentPacketsLost / (packetsReceivedDiff + currentPacketsLost)
989
1058
  : 0;
990
1059
  if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {
991
1060
  LoggerProxy.logger.info(
992
- 'StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver ',
1061
+ `StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,
993
1062
  this.statsResults[mediaType][sendrecvType].currentPacketLossRatio
994
1063
  );
995
1064
  }
996
1065
 
1066
+ if (result.frameWidth && result.frameHeight) {
1067
+ this.statsResults[mediaType][sendrecvType].width = result.frameWidth;
1068
+ this.statsResults[mediaType][sendrecvType].height = result.frameHeight;
1069
+ this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;
1070
+ }
1071
+
997
1072
  // TODO: check the packet loss value is negative values here
998
1073
 
999
1074
  if (result.packetsLost) {
@@ -1011,6 +1086,7 @@ export class StatsAnalyzer extends EventsScope {
1011
1086
  this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;
1012
1087
  this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
1013
1088
  this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;
1089
+ this.statsResults[mediaType][sendrecvType].framesDropped = result.framesDropped;
1014
1090
 
1015
1091
  this.statsResults[mediaType][sendrecvType].decoderImplementation =
1016
1092
  result.decoderImplementation;
@@ -1035,6 +1111,48 @@ export class StatsAnalyzer extends EventsScope {
1035
1111
  }
1036
1112
  }
1037
1113
 
1114
+ /**
1115
+ * extracts the local Ip address from the statsResult object by looking at stats results candidates
1116
+ * and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
1117
+ * and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
1118
+ * note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address
1119
+ * for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
1120
+ * @private
1121
+ * @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
1122
+ * @param {Object} candidates - the stats result candidates
1123
+ * @returns {void}
1124
+ */
1125
+ extractAndSetLocalIpAddressInfoForDiagnostics = (
1126
+ successfulCandidatePairId: string,
1127
+ candidates: {[key: string]: Record<string, unknown>}
1128
+ ) => {
1129
+ let newIpAddress = '';
1130
+ if (successfulCandidatePairId && !isEmpty(candidates)) {
1131
+ const localCandidate = candidates[successfulCandidatePairId];
1132
+ if (localCandidate) {
1133
+ if (localCandidate.candidateType === 'host') {
1134
+ // if it's a host candidate, use the address property - it will be the local IP
1135
+ newIpAddress = `${localCandidate.address}`;
1136
+ } else if (localCandidate.candidateType === 'prflx') {
1137
+ // if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)
1138
+ // then look at the relatedAddress - it will be the local
1139
+ //
1140
+ // Firefox doesn't populate the relayProtocol property
1141
+ if (!localCandidate.relayProtocol) {
1142
+ newIpAddress = `${localCandidate.relatedAddress}`;
1143
+ } else {
1144
+ // if it's a peer reflexive candidate and we are using a relay -
1145
+ // in that case the relatedAddress will be the IP of the TURN server (Linus),
1146
+ // so we can only look at the address, but it might be local IP or public IP,
1147
+ // depending on if the user is behind a NAT or not
1148
+ newIpAddress = `${localCandidate.address}`;
1149
+ }
1150
+ }
1151
+ }
1152
+ }
1153
+ this.localIpAddress = newIpAddress;
1154
+ };
1155
+
1038
1156
  /**
1039
1157
  * Processes remote and local candidate result and stores
1040
1158
  * @private
@@ -1049,125 +1167,53 @@ export class StatsAnalyzer extends EventsScope {
1049
1167
  if (!result || !result.id) {
1050
1168
  return;
1051
1169
  }
1052
- const RemoteCandidateType = {};
1053
- const RemoteTransport = {};
1054
- const RemoteIpAddress = {};
1055
- const RemoteNetworkType = {};
1056
-
1057
- if (!result.id) return;
1058
-
1059
- const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;
1060
- const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;
1061
-
1062
- if (!RemoteCandidateType[result.id]) {
1063
- RemoteCandidateType[result.id] = [];
1064
- }
1065
1170
 
1066
- if (!RemoteTransport[result.id]) {
1067
- RemoteTransport[result.id] = [];
1068
- }
1069
-
1070
- if (!RemoteIpAddress[result.id]) {
1071
- RemoteIpAddress[result.id] = [];
1072
- }
1073
- if (!RemoteNetworkType[result.id]) {
1074
- RemoteNetworkType[result.id] = [];
1075
- }
1076
-
1077
- if (
1078
- result.candidateType &&
1079
- RemoteCandidateType[result.id].indexOf(result.candidateType) === -1
1080
- ) {
1081
- RemoteCandidateType[result.id].push(result.candidateType);
1171
+ // We only care about the successful local candidate
1172
+ if (this.successfulCandidatePair?.localCandidateId !== result.id) {
1173
+ return;
1082
1174
  }
1083
1175
 
1084
- if (result.protocol && RemoteTransport[result.id].indexOf(result.protocol) === -1) {
1085
- RemoteTransport[result.id].push(result.protocol.toUpperCase());
1176
+ let transport;
1177
+ if (result.relayProtocol) {
1178
+ transport = result.relayProtocol.toUpperCase();
1179
+ } else if (result.protocol) {
1180
+ transport = result.protocol.toUpperCase();
1086
1181
  }
1087
1182
 
1088
- if (
1089
- result.ip &&
1090
- RemoteIpAddress[result.id].indexOf(`${result.ip}:${result.portNumber}`) === -1
1091
- ) {
1092
- RemoteIpAddress[result.id].push(`${result.ip}`); // TODO: Add ports
1093
- }
1183
+ const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;
1184
+ const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;
1094
1185
 
1095
- if (result.networkType && RemoteNetworkType[result.id].indexOf(result.networkType) === -1) {
1096
- RemoteNetworkType[result.id].push(result.networkType);
1186
+ if (!this.statsResults.candidates) {
1187
+ this.statsResults.candidates = {};
1097
1188
  }
1098
1189
 
1099
- this.statsResults.internal.candidates[result.id] = {
1100
- candidateType: RemoteCandidateType[result.id],
1101
- ipAddress: RemoteIpAddress[result.id],
1190
+ this.statsResults.candidates[result.id] = {
1191
+ candidateType: result.candidateType,
1192
+ ipAddress: result.ip, // TODO: add ports
1193
+ relatedAddress: result.relatedAddress,
1194
+ relatedPort: result.relatedPort,
1195
+ relayProtocol: result.relayProtocol,
1196
+ protocol: result.protocol,
1197
+ address: result.address,
1102
1198
  portNumber: result.port,
1103
- networkType: RemoteNetworkType[result.id],
1199
+ networkType: result.networkType,
1104
1200
  priority: result.priority,
1105
- transport: RemoteTransport[result.id],
1201
+ transport,
1106
1202
  timestamp: result.time,
1107
1203
  id: result.id,
1108
1204
  type: result.type,
1109
1205
  };
1110
1206
 
1111
- this.statsResults.connectionType[ipType].candidateType = RemoteCandidateType[result.id];
1112
- this.statsResults.connectionType[ipType].ipAddress = RemoteIpAddress[result.id];
1207
+ this.statsResults.connectionType[ipType].candidateType = result.candidateType;
1208
+ this.statsResults.connectionType[ipType].ipAddress = result.ipAddress;
1113
1209
 
1114
1210
  this.statsResults.connectionType[ipType].networkType =
1115
- RemoteNetworkType[result.id][0] === NETWORK_TYPE.VPN
1116
- ? NETWORK_TYPE.UNKNOWN
1117
- : RemoteNetworkType[result.id][0];
1118
- this.statsResults.connectionType[ipType].transport = RemoteTransport[result.id];
1211
+ result.networkType === NETWORK_TYPE.VPN ? NETWORK_TYPE.UNKNOWN : result.networkType;
1212
+ this.statsResults.connectionType[ipType].transport = transport;
1119
1213
 
1120
1214
  this.statsResults[type][sendRecvType].totalRoundTripTime = result.totalRoundTripTime;
1121
1215
  };
1122
1216
 
1123
- /**
1124
- * Process Track results
1125
- *
1126
- * @private
1127
- * @param {*} result
1128
- * @param {*} mediaType
1129
- * @returns {void}
1130
- * @memberof StatsAnalyzer
1131
- */
1132
- private processTrackResult(result: any, mediaType: any) {
1133
- if (!result || result.type !== 'track') {
1134
- return;
1135
- }
1136
-
1137
- const sendrecvType =
1138
- result.remoteSource === true ? STATS.RECEIVE_DIRECTION : STATS.SEND_DIRECTION;
1139
-
1140
- if (result.frameWidth && result.frameHeight) {
1141
- this.statsResults.resolutions[mediaType][sendrecvType].width = result.frameWidth;
1142
- this.statsResults.resolutions[mediaType][sendrecvType].height = result.frameHeight;
1143
- this.statsResults.resolutions[mediaType][sendrecvType].framesSent = result.framesSent;
1144
- this.statsResults.resolutions[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;
1145
- }
1146
-
1147
- if (sendrecvType === STATS.RECEIVE_DIRECTION) {
1148
- this.statsResults.resolutions[mediaType][sendrecvType].framesReceived = result.framesReceived;
1149
- this.statsResults.resolutions[mediaType][sendrecvType].framesDecoded = result.framesDecoded;
1150
- this.statsResults.resolutions[mediaType][sendrecvType].framesDropped = result.framesDropped;
1151
- }
1152
-
1153
- if (result.trackIdentifier && !mediaType.includes('audio')) {
1154
- this.statsResults.resolutions[mediaType][sendrecvType].trackIdentifier =
1155
- result.trackIdentifier;
1156
-
1157
- const jitterBufferDelay = result && result.jitterBufferDelay;
1158
- const jitterBufferEmittedCount = result && result.jitterBufferEmittedCount;
1159
-
1160
- this.statsResults.resolutions[mediaType][sendrecvType].avgJitterDelay =
1161
- jitterBufferEmittedCount && +jitterBufferDelay / +jitterBufferEmittedCount;
1162
-
1163
- // Used to calculate the jitter
1164
- this.statsResults.resolutions[mediaType][sendrecvType].jitterBufferDelay =
1165
- result.jitterBufferDelay;
1166
- this.statsResults.resolutions[mediaType][sendrecvType].jitterBufferEmittedCount =
1167
- result.jitterBufferEmittedCount;
1168
- }
1169
- }
1170
-
1171
1217
  /**
1172
1218
  *
1173
1219
  * @private
@@ -1178,20 +1224,15 @@ export class StatsAnalyzer extends EventsScope {
1178
1224
  */
1179
1225
  compareSentAndReceived(result, type) {
1180
1226
  // Don't compare on transceivers without a sender.
1181
- if (!type || !this.statsResults.internal[type].send) {
1227
+ if (!type || !this.statsResults[type].send) {
1182
1228
  return;
1183
1229
  }
1184
1230
 
1185
1231
  const mediaType = type;
1186
1232
 
1187
- if (!this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver) {
1188
- this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
1189
- }
1190
-
1191
1233
  const currentPacketLoss =
1192
- result.packetsLost - this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver;
1234
+ result.packetsLost - this.statsResults[mediaType].send.totalPacketsLostOnReceiver;
1193
1235
 
1194
- this.statsResults.internal[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
1195
1236
  this.statsResults[mediaType].send.packetsLostOnReceiver = currentPacketLoss;
1196
1237
  this.statsResults[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;
1197
1238