@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
@@ -7,6 +7,9 @@ import {ConnectionState} from '@webex/internal-media-core';
7
7
  import {StatsAnalyzer, EVENTS} from '../../../../src/statsAnalyzer';
8
8
  import NetworkQualityMonitor from '../../../../src/networkQualityMonitor';
9
9
  import testUtils from '../../../utils/testUtils';
10
+ import {MEDIA_DEVICES, MQA_INTERVAL, _UNKNOWN_} from '@webex/plugin-meetings/src/constants';
11
+ import LoggerProxy from '../../../../src/common/logs/logger-proxy';
12
+ import LoggerConfig from '../../../../src/common/logs/logger-config';
10
13
 
11
14
  const {assert} = chai;
12
15
 
@@ -15,6 +18,186 @@ sinon.assert.expose(chai.assert, {prefix: ''});
15
18
 
16
19
  describe('plugin-meetings', () => {
17
20
  describe('StatsAnalyzer', () => {
21
+ describe('parseStatsResult', () => {
22
+ const sandbox = sinon.createSandbox();
23
+ let statsAnalyzer;
24
+
25
+ const initialConfig = {};
26
+ const defaultStats = {};
27
+
28
+ beforeEach(() => {
29
+ const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
30
+
31
+ statsAnalyzer = new StatsAnalyzer(
32
+ initialConfig,
33
+ () => ({}),
34
+ networkQualityMonitor,
35
+ defaultStats
36
+ );
37
+ });
38
+
39
+ afterEach(() => {
40
+ sandbox.reset();
41
+ });
42
+
43
+ it('should call processOutboundRTPResult', () => {
44
+ const calledSpy = sandbox.spy(statsAnalyzer, 'processOutboundRTPResult');
45
+ statsAnalyzer.parseGetStatsResult({type: 'outbound-rtp'}, 'video-send');
46
+ assert(calledSpy.calledOnce);
47
+ });
48
+
49
+ it('should call processInboundRTPResult', () => {
50
+ const calledSpy = sandbox.spy(statsAnalyzer, 'processInboundRTPResult');
51
+ statsAnalyzer.parseGetStatsResult({type: 'inbound-rtp'}, 'video-recv');
52
+ assert(calledSpy.calledOnce);
53
+ });
54
+
55
+ it('should call compareSentAndReceived', () => {
56
+ const calledSpy = sandbox.spy(statsAnalyzer, 'compareSentAndReceived');
57
+ statsAnalyzer.parseGetStatsResult({type: 'remote-outbound-rtp'}, 'video-send');
58
+ assert(calledSpy.calledOnce);
59
+ });
60
+
61
+ it('should call parseCandidate', () => {
62
+ const calledSpy = sandbox.spy(statsAnalyzer, 'parseCandidate');
63
+ statsAnalyzer.parseGetStatsResult({type: 'local-candidate'}, 'video-send');
64
+ assert(calledSpy.calledOnce);
65
+ });
66
+
67
+ it('processOutboundRTPResult should create the correct stats results', () => {
68
+ // establish the `statsResults` object.
69
+ statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
70
+
71
+ statsAnalyzer.processOutboundRTPResult(
72
+ {
73
+ bytesSent: 50000,
74
+ codecId: 'RTCCodec_1_Outbound_111',
75
+ headerBytesSent: 25000,
76
+ id: 'RTCOutboundRTPAudioStream_123456789',
77
+ kind: 'audio',
78
+ mediaSourceId: 'RTCAudioSource_2',
79
+ mediaType: 'audio',
80
+ nackCount: 1,
81
+ packetsSent: 3600,
82
+ remoteId: 'RTCRemoteInboundRtpAudioStream_123456789',
83
+ retransmittedBytesSent: 100,
84
+ retransmittedPacketsSent: 2,
85
+ ssrc: 123456789,
86
+ targetBitrate: 256000,
87
+ timestamp: 1707341489336,
88
+ trackId: 'RTCMediaStreamTrack_sender_2',
89
+ transportId: 'RTCTransport_0_1',
90
+ type: 'outbound-rtp',
91
+ },
92
+ 'audio-send',
93
+ true
94
+ );
95
+
96
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.headerBytesSent, 25000);
97
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalBytesSent, 50000);
98
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalNackCount, 1);
99
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalPacketsSent, 3600);
100
+ assert.strictEqual(
101
+ statsAnalyzer.statsResults['audio-send'].send.retransmittedPacketsSent,
102
+ 2
103
+ );
104
+ assert.strictEqual(
105
+ statsAnalyzer.statsResults['audio-send'].send.retransmittedBytesSent,
106
+ 100
107
+ );
108
+ });
109
+
110
+ it('processInboundRTPResult should create the correct stats results', () => {
111
+ // establish the `statsResults` object.
112
+ statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-recv-1', false);
113
+
114
+ statsAnalyzer.processInboundRTPResult(
115
+ {
116
+ audioLevel: 0,
117
+ bytesReceived: 509,
118
+ codecId: 'RTCCodec_6_Inbound_111',
119
+ concealedSamples: 200000,
120
+ concealmentEvents: 13,
121
+ fecPacketsDiscarded: 1,
122
+ fecPacketsReceived: 1,
123
+ headerBytesReceived: 250,
124
+ id: 'RTCInboundRTPAudioStream_123456789',
125
+ insertedSamplesForDeceleration: 0,
126
+ jitter: 0.012,
127
+ jitterBufferDelay: 1000,
128
+ jitterBufferEmittedCount: 10000,
129
+ kind: 'audio',
130
+ lastPacketReceivedTimestamp: 1707341488529,
131
+ mediaType: 'audio',
132
+ packetsDiscarded: 0,
133
+ packetsLost: 0,
134
+ packetsReceived: 12,
135
+ remoteId: 'RTCRemoteOutboundRTPAudioStream_123456789',
136
+ removedSamplesForAcceleration: 0,
137
+ silentConcealedSamples: 200000,
138
+ ssrc: 123456789,
139
+ timestamp: 1707341489419,
140
+ totalAudioEnergy: 133,
141
+ totalSamplesDuration: 7,
142
+ totalSamplesReceived: 300000,
143
+ trackId: 'RTCMediaStreamTrack_receiver_76',
144
+ transportId: 'RTCTransport_0_1',
145
+ type: 'inbound-rtp',
146
+ },
147
+ 'audio-recv-1',
148
+ false
149
+ );
150
+
151
+ assert.strictEqual(
152
+ statsAnalyzer.statsResults['audio-recv-1'].recv.totalPacketsReceived,
153
+ 12
154
+ );
155
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsDiscarded, 1);
156
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.fecPacketsReceived, 1);
157
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalBytesReceived, 509);
158
+ assert.strictEqual(
159
+ statsAnalyzer.statsResults['audio-recv-1'].recv.headerBytesReceived,
160
+ 250
161
+ );
162
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.audioLevel, 0);
163
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalAudioEnergy, 133);
164
+ assert.strictEqual(
165
+ statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesReceived,
166
+ 300000
167
+ );
168
+ assert.strictEqual(statsAnalyzer.statsResults['audio-recv-1'].recv.totalSamplesDecoded, 0);
169
+ assert.strictEqual(
170
+ statsAnalyzer.statsResults['audio-recv-1'].recv.concealedSamples,
171
+ 200000
172
+ );
173
+ });
174
+
175
+ it('parseAudioSource should create the correct stats results', () => {
176
+ // establish the `statsResults` object.
177
+ statsAnalyzer.parseGetStatsResult({type: 'none'}, 'audio-send', true);
178
+
179
+ statsAnalyzer.parseAudioSource(
180
+ {
181
+ audioLevel: 0.03,
182
+ echoReturnLoss: -30,
183
+ echoReturnLossEnhancement: 0.17,
184
+ id: 'RTCAudioSource_2',
185
+ kind: 'audio',
186
+ timestamp: 1707341488160.012,
187
+ totalAudioEnergy: 0.001,
188
+ totalSamplesDuration: 4.5,
189
+ trackIdentifier: '2207e5bf-c595-4301-93f7-283994d8143f',
190
+ type: 'media-source',
191
+ },
192
+ 'audio-send',
193
+ true
194
+ );
195
+
196
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.audioLevel, 0.03);
197
+ assert.strictEqual(statsAnalyzer.statsResults['audio-send'].send.totalAudioEnergy, 0.001);
198
+ });
199
+ });
200
+
18
201
  describe('compareSentAndReceived()', () => {
19
202
  let statsAnalyzer;
20
203
  let sandBoxSpy;
@@ -53,7 +236,12 @@ describe('plugin-meetings', () => {
53
236
  beforeEach(() => {
54
237
  const networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
55
238
 
56
- statsAnalyzer = new StatsAnalyzer(initialConfig, networkQualityMonitor, defaultStats);
239
+ statsAnalyzer = new StatsAnalyzer(
240
+ initialConfig,
241
+ () => ({}),
242
+ networkQualityMonitor,
243
+ defaultStats
244
+ );
57
245
 
58
246
  sandBoxSpy = sandbox.spy(
59
247
  statsAnalyzer.networkQualityMonitor,
@@ -84,6 +272,9 @@ describe('plugin-meetings', () => {
84
272
  let pc;
85
273
  let networkQualityMonitor;
86
274
  let statsAnalyzer;
275
+ let mqeData;
276
+ let loggerSpy;
277
+ let receiveSlot;
87
278
 
88
279
  let receivedEventsData = {
89
280
  local: {},
@@ -96,6 +287,8 @@ describe('plugin-meetings', () => {
96
287
 
97
288
  let fakeStats;
98
289
 
290
+ const sandbox = sinon.createSandbox();
291
+
99
292
  const resetReceivedEvents = () => {
100
293
  receivedEventsData = {
101
294
  local: {},
@@ -103,8 +296,15 @@ describe('plugin-meetings', () => {
103
296
  };
104
297
  };
105
298
 
299
+ before(() => {
300
+ LoggerConfig.set({enable: false});
301
+ LoggerProxy.set();
302
+ loggerSpy = sandbox.spy(LoggerProxy.logger, 'info');
303
+ });
304
+
106
305
  beforeEach(() => {
107
306
  clock = sinon.useFakeTimers();
307
+ receiveSlot = undefined;
108
308
 
109
309
  resetReceivedEvents();
110
310
 
@@ -113,11 +313,31 @@ describe('plugin-meetings', () => {
113
313
  audio: {
114
314
  senders: [
115
315
  {
316
+ localTrackLabel: 'fake-microphone',
116
317
  report: [
117
318
  {
118
319
  type: 'outbound-rtp',
119
- packetsSent: 0,
120
320
  bytesSent: 1,
321
+ packetsSent: 0,
322
+ },
323
+ {
324
+ type: 'remote-inbound-rtp',
325
+ packetsLost: 0,
326
+ },
327
+ {
328
+ type: 'candidate-pair',
329
+ state: 'succeeded',
330
+ localCandidateId: 'fake-candidate-id',
331
+ },
332
+ {
333
+ type: 'candidate-pair',
334
+ state: 'failed',
335
+ localCandidateId: 'bad-candidate-id',
336
+ },
337
+ {
338
+ type: 'local-candidate',
339
+ id: 'fake-candidate-id',
340
+ protocol: 'tcp',
121
341
  },
122
342
  ],
123
343
  },
@@ -127,8 +347,29 @@ describe('plugin-meetings', () => {
127
347
  report: [
128
348
  {
129
349
  type: 'inbound-rtp',
130
- packetsReceived: 0,
131
350
  bytesReceived: 1,
351
+ fecPacketsDiscarded: 0,
352
+ fecPacketsReceived: 0,
353
+ packetsLost: 0,
354
+ packetsReceived: 0,
355
+ },
356
+ {
357
+ type: 'remote-outbound-rtp',
358
+ },
359
+ {
360
+ type: 'candidate-pair',
361
+ state: 'succeeded',
362
+ localCandidateId: 'fake-candidate-id',
363
+ },
364
+ {
365
+ type: 'candidate-pair',
366
+ state: 'failed',
367
+ localCandidateId: 'bad-candidate-id',
368
+ },
369
+ {
370
+ type: 'local-candidate',
371
+ id: 'fake-candidate-id',
372
+ protocol: 'tcp',
132
373
  },
133
374
  ],
134
375
  },
@@ -137,11 +378,32 @@ describe('plugin-meetings', () => {
137
378
  video: {
138
379
  senders: [
139
380
  {
381
+ localTrackLabel: 'fake-camera',
140
382
  report: [
141
383
  {
142
384
  type: 'outbound-rtp',
143
- framesSent: 0,
144
385
  bytesSent: 1,
386
+ framesSent: 0,
387
+ packetsSent: 0,
388
+ },
389
+ {
390
+ type: 'remote-inbound-rtp',
391
+ packetsLost: 0,
392
+ },
393
+ {
394
+ type: 'candidate-pair',
395
+ state: 'succeeded',
396
+ localCandidateId: 'fake-candidate-id',
397
+ },
398
+ {
399
+ type: 'candidate-pair',
400
+ state: 'failed',
401
+ localCandidateId: 'bad-candidate-id',
402
+ },
403
+ {
404
+ type: 'local-candidate',
405
+ id: 'fake-candidate-id',
406
+ protocol: 'tcp',
145
407
  },
146
408
  ],
147
409
  },
@@ -151,8 +413,99 @@ describe('plugin-meetings', () => {
151
413
  report: [
152
414
  {
153
415
  type: 'inbound-rtp',
416
+ bytesReceived: 1,
417
+ frameHeight: 720,
418
+ frameWidth: 1280,
154
419
  framesDecoded: 0,
420
+ framesReceived: 0,
421
+ packetsLost: 0,
422
+ packetsReceived: 0,
423
+ },
424
+ {
425
+ type: 'remote-outbound-rtp',
426
+ },
427
+ {
428
+ type: 'candidate-pair',
429
+ state: 'succeeded',
430
+ localCandidateId: 'fake-candidate-id',
431
+ },
432
+ {
433
+ type: 'candidate-pair',
434
+ state: 'failed',
435
+ localCandidateId: 'bad-candidate-id',
436
+ },
437
+ {
438
+ type: 'local-candidate',
439
+ id: 'fake-candidate-id',
440
+ protocol: 'tcp',
441
+ },
442
+ ],
443
+ },
444
+ ],
445
+ },
446
+ share: {
447
+ senders: [
448
+ {
449
+ localTrackLabel: 'fake-share',
450
+ report: [
451
+ {
452
+ type: 'outbound-rtp',
453
+ bytesSent: 1,
454
+ framesSent: 0,
455
+ packetsSent: 0,
456
+ },
457
+ {
458
+ type: 'remote-inbound-rtp',
459
+ packetsLost: 0,
460
+ },
461
+ {
462
+ type: 'candidate-pair',
463
+ state: 'succeeded',
464
+ localCandidateId: 'fake-candidate-id',
465
+ },
466
+ {
467
+ type: 'candidate-pair',
468
+ state: 'failed',
469
+ localCandidateId: 'bad-candidate-id',
470
+ },
471
+ {
472
+ type: 'local-candidate',
473
+ id: 'fake-candidate-id',
474
+ protocol: 'tcp',
475
+ },
476
+ ],
477
+ },
478
+ ],
479
+ receivers: [
480
+ {
481
+ report: [
482
+ {
483
+ type: 'inbound-rtp',
155
484
  bytesReceived: 1,
485
+ frameHeight: 720,
486
+ frameWidth: 1280,
487
+ framesDecoded: 0,
488
+ framesReceived: 0,
489
+ packetsLost: 0,
490
+ packetsReceived: 0,
491
+ },
492
+ {
493
+ type: 'remote-outbound-rtp',
494
+ },
495
+ {
496
+ type: 'candidate-pair',
497
+ state: 'succeeded',
498
+ localCandidateId: 'fake-candidate-id',
499
+ },
500
+ {
501
+ type: 'candidate-pair',
502
+ state: 'failed',
503
+ localCandidateId: 'bad-candidate-id',
504
+ },
505
+ {
506
+ type: 'local-candidate',
507
+ id: 'fake-candidate-id',
508
+ protocol: 'tcp',
156
509
  },
157
510
  ],
158
511
  },
@@ -172,19 +525,19 @@ describe('plugin-meetings', () => {
172
525
  receivers: [fakeStats.video.receivers[0]],
173
526
  },
174
527
  screenShareAudio: {
175
- senders: [],
176
- receivers: [],
528
+ senders: [fakeStats.audio.senders[0]],
529
+ receivers: [fakeStats.audio.receivers[0]],
177
530
  },
178
531
  screenShareVideo: {
179
- senders: [],
180
- receivers: [],
532
+ senders: [fakeStats.share.senders[0]],
533
+ receivers: [fakeStats.share.receivers[0]],
181
534
  },
182
535
  }),
183
536
  };
184
537
 
185
538
  networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
186
539
 
187
- statsAnalyzer = new StatsAnalyzer(initialConfig, networkQualityMonitor);
540
+ statsAnalyzer = new StatsAnalyzer(initialConfig, () => receiveSlot, networkQualityMonitor);
188
541
 
189
542
  statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
190
543
  receivedEventsData.local.started = data;
@@ -198,21 +551,45 @@ describe('plugin-meetings', () => {
198
551
  statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
199
552
  receivedEventsData.remote.stopped = data;
200
553
  });
554
+ statsAnalyzer.on(EVENTS.MEDIA_QUALITY, ({data}) => {
555
+ mqeData = data;
556
+ });
201
557
  });
202
558
 
203
559
  afterEach(() => {
560
+ sandbox.reset();
204
561
  clock.restore();
205
562
  });
206
563
 
207
- const startStatsAnalyzer = async (mediaStatus) => {
564
+ const startStatsAnalyzer = async (mediaStatus, lastEmittedEvents) => {
208
565
  statsAnalyzer.updateMediaStatus(mediaStatus);
209
566
  statsAnalyzer.startAnalyzer(pc);
567
+ statsAnalyzer.lastEmittedStartStopEvent = lastEmittedEvents || {};
210
568
 
211
569
  await testUtils.flushPromises();
212
570
  };
213
571
 
214
- const progressTime = async () => {
215
- await clock.tickAsync(initialConfig.analyzerInterval);
572
+ const mergeProperties = (
573
+ target,
574
+ properties,
575
+ keyValue = 'fake-candidate-id',
576
+ matchKey = 'type',
577
+ matchValue = 'local-candidate'
578
+ ) => {
579
+ for (let key in target) {
580
+ if (target.hasOwnProperty(key)) {
581
+ if (typeof target[key] === 'object') {
582
+ mergeProperties(target[key], properties, keyValue, matchKey, matchValue);
583
+ }
584
+ if (key === 'id' && target[key] === keyValue && target[matchKey] === matchValue) {
585
+ Object.assign(target, properties);
586
+ }
587
+ }
588
+ }
589
+ };
590
+
591
+ const progressTime = async (time = initialConfig.analyzerInterval) => {
592
+ await clock.tickAsync(time);
216
593
  await testUtils.flushPromises();
217
594
  };
218
595
 
@@ -224,6 +601,23 @@ describe('plugin-meetings', () => {
224
601
  assert.deepEqual(receivedEventsData.remote.stopped, expected.remote?.stopped);
225
602
  };
226
603
 
604
+ const checkMqeData = () => {
605
+ for (const data of [
606
+ mqeData.audioTransmit,
607
+ mqeData.audioReceive,
608
+ mqeData.videoTransmit,
609
+ mqeData.videoReceive,
610
+ ]) {
611
+ assert.strictEqual(data.length, 2);
612
+ assert.strictEqual(data[0].common.common.isMain, true);
613
+ assert.strictEqual(data[1].common.common.isMain, false);
614
+ }
615
+
616
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedFrameSize, 3600);
617
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedHeight, 720);
618
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].receivedWidth, 1280);
619
+ };
620
+
227
621
  it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for audio', async () => {
228
622
  await startStatsAnalyzer({expected: {sendAudio: true}});
229
623
 
@@ -264,6 +658,26 @@ describe('plugin-meetings', () => {
264
658
  checkReceivedEvent({expected: {local: {stopped: {type: 'video'}}}});
265
659
  });
266
660
 
661
+ it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for share', async () => {
662
+ await startStatsAnalyzer({expected: {sendShare: true}});
663
+
664
+ // check that we haven't received any events yet
665
+ checkReceivedEvent({expected: {}});
666
+
667
+ // setup a mock to return some values higher the previous ones
668
+ fakeStats.share.senders[0].report[0].framesSent += 1;
669
+
670
+ await progressTime();
671
+
672
+ // check that we got the LOCAL_MEDIA_STARTED event for audio
673
+ checkReceivedEvent({expected: {local: {started: {type: 'share'}}}});
674
+
675
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
676
+ resetReceivedEvents();
677
+ await progressTime();
678
+ checkReceivedEvent({expected: {local: {stopped: {type: 'share'}}}});
679
+ });
680
+
267
681
  it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for audio', async () => {
268
682
  await startStatsAnalyzer({expected: {receiveAudio: true}});
269
683
 
@@ -303,6 +717,832 @@ describe('plugin-meetings', () => {
303
717
 
304
718
  checkReceivedEvent({expected: {remote: {stopped: {type: 'video'}}}});
305
719
  });
720
+
721
+ it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for share', async () => {
722
+ await startStatsAnalyzer({expected: {receiveShare: true}});
723
+
724
+ // check that we haven't received any events yet
725
+ checkReceivedEvent({expected: {}});
726
+
727
+ // setup a mock to return some values higher the previous ones
728
+ fakeStats.share.receivers[0].report[0].framesDecoded += 1;
729
+
730
+ await progressTime();
731
+ // check that we got the REMOTE_MEDIA_STARTED event for video
732
+ checkReceivedEvent({expected: {remote: {started: {type: 'share'}}}});
733
+
734
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
735
+ resetReceivedEvents();
736
+ await progressTime();
737
+
738
+ checkReceivedEvent({expected: {remote: {stopped: {type: 'share'}}}});
739
+ });
740
+
741
+ it('emits the correct MEDIA_QUALITY events', async () => {
742
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
743
+
744
+ await progressTime();
745
+
746
+ // Check that the mqe data has been emitted and is correctly computed.
747
+ checkMqeData();
748
+ });
749
+
750
+ it('emits the correct transportType in MEDIA_QUALITY events', async () => {
751
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
752
+
753
+ await progressTime();
754
+
755
+ assert.strictEqual(mqeData.audioTransmit[0].common.transportType, 'TCP');
756
+ assert.strictEqual(mqeData.videoReceive[0].common.transportType, 'TCP');
757
+ });
758
+
759
+ it('emits the correct transportType in MEDIA_QUALITY events when using a TURN server', async () => {
760
+ fakeStats.audio.senders[0].report[4].relayProtocol = 'tls';
761
+ fakeStats.video.senders[0].report[4].relayProtocol = 'tls';
762
+ fakeStats.audio.receivers[0].report[4].relayProtocol = 'tls';
763
+ fakeStats.video.receivers[0].report[4].relayProtocol = 'tls';
764
+
765
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
766
+
767
+ await progressTime();
768
+
769
+ assert.strictEqual(mqeData.audioTransmit[0].common.transportType, 'TLS');
770
+ assert.strictEqual(mqeData.videoReceive[0].common.transportType, 'TLS');
771
+ });
772
+
773
+ it('emits the correct peripherals in MEDIA_QUALITY events', async () => {
774
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
775
+
776
+ await progressTime();
777
+
778
+ assert.strictEqual(
779
+ mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
780
+ .information,
781
+ 'fake-microphone'
782
+ );
783
+ assert.strictEqual(
784
+ mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
785
+ .information,
786
+ 'fake-camera'
787
+ );
788
+ });
789
+
790
+ it('emits the correct peripherals in MEDIA_QUALITY events when localTrackLabel is undefined', async () => {
791
+ fakeStats.audio.senders[0].localTrackLabel = undefined;
792
+ fakeStats.video.senders[0].localTrackLabel = undefined;
793
+
794
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
795
+
796
+ await progressTime();
797
+
798
+ assert.strictEqual(
799
+ mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.MICROPHONE)
800
+ .information,
801
+ _UNKNOWN_
802
+ );
803
+ assert.strictEqual(
804
+ mqeData.intervalMetadata.peripherals.find((val) => val.name === MEDIA_DEVICES.CAMERA)
805
+ .information,
806
+ _UNKNOWN_
807
+ );
808
+ });
809
+
810
+ it('emits the correct transmittedFrameRate/receivedFrameRate', async () => {
811
+ it('at the start of the stats analyzer', async () => {
812
+ await startStatsAnalyzer();
813
+ assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 0);
814
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 0);
815
+ });
816
+
817
+ it('after frames are sent and received', async () => {
818
+ fakeStats.video.senders[0].report[0].framesSent += 300;
819
+ fakeStats.video.receivers[0].report[0].framesReceived += 300;
820
+ await progressTime(MQA_INTERVAL);
821
+
822
+ // 300 frames in 60 seconds = 5 frames per second
823
+ assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.transmittedFrameRate, 5);
824
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 5);
825
+ });
826
+ });
827
+
828
+ it('emits the correct rtpPackets', async () => {
829
+ it('at the start of the stats analyzer', async () => {
830
+ await startStatsAnalyzer();
831
+ assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 0);
832
+ assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 0);
833
+ assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 0);
834
+ assert.strictEqual(mqeData.audioReceive[0].streams[0].common.rtpPackets, 0);
835
+ assert.strictEqual(mqeData.videoTransmit[0].common.rtpPackets, 0);
836
+ assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 0);
837
+ assert.strictEqual(mqeData.videoReceive[0].common.rtpPackets, 0);
838
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 0);
839
+ });
840
+
841
+ it('after packets are sent', async () => {
842
+ fakeStats.audio.senders[0].report[0].packetsSent += 5;
843
+ fakeStats.video.senders[0].report[0].packetsSent += 5;
844
+ await progressTime(MQA_INTERVAL);
845
+
846
+ assert.strictEqual(mqeData.audioTransmit[0].common.rtpPackets, 5);
847
+ assert.strictEqual(mqeData.audioTransmit[0].streams[0].common.rtpPackets, 5);
848
+ assert.strictEqual(mqeData.videoTransmit[0].common.rtpPackets, 5);
849
+ assert.strictEqual(mqeData.videoTransmit[0].streams[0].common.rtpPackets, 5);
850
+ });
851
+
852
+ it('after packets are received', async () => {
853
+ fakeStats.audio.senders[0].report[0].packetsSent += 10;
854
+ fakeStats.video.senders[0].report[0].packetsSent += 10;
855
+ fakeStats.audio.receivers[0].report[0].packetsReceived += 10;
856
+ fakeStats.video.receivers[0].report[0].packetsReceived += 10;
857
+ await progressTime(MQA_INTERVAL);
858
+
859
+ assert.strictEqual(mqeData.audioReceive[0].common.rtpPackets, 10);
860
+ assert.strictEqual(mqeData.audioReceive[0].streams[0].common.rtpPackets, 10);
861
+ assert.strictEqual(mqeData.videoReceive[0].common.rtpPackets, 10);
862
+ assert.strictEqual(mqeData.videoReceive[0].streams[0].common.rtpPackets, 10);
863
+ });
864
+ });
865
+
866
+ it('emits the correct fecPackets', async () => {
867
+ it('at the start of the stats analyzer', async () => {
868
+ await startStatsAnalyzer();
869
+ assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 0);
870
+ });
871
+
872
+ it('after FEC packets are received', async () => {
873
+ fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 5;
874
+ await progressTime(MQA_INTERVAL);
875
+
876
+ assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 5);
877
+ });
878
+
879
+ it('after FEC packets are received and some FEC packets are discarded', async () => {
880
+ fakeStats.audio.receivers[0].report[0].fecPacketsReceived += 15;
881
+ fakeStats.audio.receivers[0].report[0].fecPacketsDiscarded += 5;
882
+ await progressTime(MQA_INTERVAL);
883
+
884
+ assert.strictEqual(mqeData.audioReceive[0].common.fecPackets, 10);
885
+ });
886
+ });
887
+
888
+ it('emits the correct mediaHopByHopLost/rtpHopByHopLost', async () => {
889
+ it('at the start of the stats analyzer', async () => {
890
+ await startStatsAnalyzer();
891
+ assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 0);
892
+ assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 0);
893
+ assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 0);
894
+ assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 0);
895
+ });
896
+
897
+ it('after packets are lost', async () => {
898
+ fakeStats.audio.receivers[0].report[0].packetsLost += 5;
899
+ fakeStats.video.receivers[0].report[0].packetsLost += 5;
900
+ await progressTime(MQA_INTERVAL);
901
+
902
+ assert.strictEqual(mqeData.audioReceive[0].common.mediaHopByHopLost, 5);
903
+ assert.strictEqual(mqeData.audioReceive[0].common.rtpHopByHopLost, 5);
904
+ assert.strictEqual(mqeData.videoReceive[0].common.mediaHopByHopLost, 5);
905
+ assert.strictEqual(mqeData.videoReceive[0].common.rtpHopByHopLost, 5);
906
+ });
907
+ });
908
+
909
+ it('emits the correct remoteLossRate', async () => {
910
+ it('at the start of the stats analyzer', async () => {
911
+ await startStatsAnalyzer();
912
+ assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
913
+ assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
914
+ });
915
+
916
+ it('after packets are sent', async () => {
917
+ fakeStats.audio.senders[0].report[0].packetsSent += 100;
918
+ fakeStats.video.senders[0].report[0].packetsSent += 100;
919
+ await progressTime(MQA_INTERVAL);
920
+
921
+ assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 0);
922
+ assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 0);
923
+ });
924
+
925
+ it('after packets are sent and some packets are lost', async () => {
926
+ fakeStats.audio.senders[0].report[0].packetsSent += 200;
927
+ fakeStats.audio.senders[0].report[1].packetsLost += 10;
928
+ fakeStats.video.senders[0].report[0].packetsSent += 200;
929
+ fakeStats.video.senders[0].report[1].packetsLost += 10;
930
+ await progressTime(MQA_INTERVAL);
931
+
932
+ assert.strictEqual(mqeData.audioTransmit[0].common.remoteLossRate, 5);
933
+ assert.strictEqual(mqeData.videoTransmit[0].common.remoteLossRate, 5);
934
+ });
935
+ });
936
+
937
+ it('has the correct localIpAddress set when the candidateType is host', async () => {
938
+ await startStatsAnalyzer();
939
+
940
+ await progressTime();
941
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
942
+ mergeProperties(fakeStats, {address: 'test', candidateType: 'host'});
943
+ await progressTime();
944
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test');
945
+ });
946
+
947
+ it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is set', async () => {
948
+ await startStatsAnalyzer();
949
+
950
+ await progressTime();
951
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
952
+ mergeProperties(fakeStats, {
953
+ relayProtocol: 'test',
954
+ address: 'test2',
955
+ candidateType: 'prflx',
956
+ });
957
+ await progressTime();
958
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test2');
959
+ });
960
+
961
+ it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is not set', async () => {
962
+ await startStatsAnalyzer();
963
+
964
+ await progressTime();
965
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
966
+ mergeProperties(fakeStats, {
967
+ relatedAddress: 'relatedAddress',
968
+ address: 'test2',
969
+ candidateType: 'prflx',
970
+ });
971
+ await progressTime();
972
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'relatedAddress');
973
+ });
974
+
975
+ it('has no localIpAddress set when the candidateType is invalid', async () => {
976
+ await startStatsAnalyzer();
977
+
978
+ await progressTime();
979
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
980
+ mergeProperties(fakeStats, {candidateType: 'invalid'});
981
+ await progressTime();
982
+ assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
983
+ });
984
+
985
+ it('logs a message when audio send packets do not increase', async () => {
986
+ await startStatsAnalyzer(
987
+ {expected: {sendAudio: true}},
988
+ {audio: {local: EVENTS.LOCAL_MEDIA_STARTED}}
989
+ );
990
+
991
+ // don't increase the packets when time progresses.
992
+ await progressTime();
993
+
994
+ assert(
995
+ loggerSpy.calledWith(
996
+ 'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
997
+ )
998
+ );
999
+ });
1000
+
1001
+ it('does not log a message when audio send packets increase', async () => {
1002
+ await startStatsAnalyzer(
1003
+ {expected: {sendAudio: true}},
1004
+ {audio: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
1005
+ );
1006
+
1007
+ fakeStats.audio.senders[0].report[0].packetsSent += 5;
1008
+ await progressTime();
1009
+
1010
+ assert(
1011
+ loggerSpy.neverCalledWith(
1012
+ 'StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent'
1013
+ )
1014
+ );
1015
+ });
1016
+
1017
+ it('logs a message when video send packets do not increase', async () => {
1018
+ await startStatsAnalyzer(
1019
+ {expected: {sendVideo: true}},
1020
+ {video: {local: EVENTS.LOCAL_MEDIA_STARTED}}
1021
+ );
1022
+
1023
+ // don't increase the packets when time progresses.
1024
+ await progressTime();
1025
+
1026
+ assert(
1027
+ loggerSpy.calledWith(
1028
+ 'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
1029
+ )
1030
+ );
1031
+ });
1032
+
1033
+ it('does not log a message when video send packets increase', async () => {
1034
+ await startStatsAnalyzer(
1035
+ {expected: {sendVideo: true}},
1036
+ {video: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
1037
+ );
1038
+
1039
+ fakeStats.video.senders[0].report[0].packetsSent += 5;
1040
+ await progressTime();
1041
+
1042
+ assert(
1043
+ loggerSpy.neverCalledWith(
1044
+ 'StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent'
1045
+ )
1046
+ );
1047
+ });
1048
+
1049
+ it('logs a message when share send packets do not increase', async () => {
1050
+ await startStatsAnalyzer(
1051
+ {expected: {sendShare: true}},
1052
+ {share: {local: EVENTS.LOCAL_MEDIA_STARTED}}
1053
+ );
1054
+
1055
+ // don't increase the packets when time progresses.
1056
+ await progressTime();
1057
+
1058
+ assert(
1059
+ loggerSpy.calledWith(
1060
+ 'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
1061
+ )
1062
+ );
1063
+ });
1064
+
1065
+ it('does not log a message when share send packets increase', async () => {
1066
+ await startStatsAnalyzer(
1067
+ {expected: {sendShare: true}},
1068
+ {share: {local: EVENTS.LOCAL_MEDIA_STOPPED}}
1069
+ );
1070
+
1071
+ fakeStats.share.senders[0].report[0].packetsSent += 5;
1072
+ await progressTime();
1073
+
1074
+ assert(
1075
+ loggerSpy.neverCalledWith(
1076
+ 'StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent'
1077
+ )
1078
+ );
1079
+ });
1080
+
1081
+ ['avatar', 'invalid', 'no source', 'bandwidth limited', 'policy violation'].forEach(
1082
+ (sourceState) => {
1083
+ it(`does not log a message when no packets are recieved for a receive slot with sourceState "${sourceState}"`, async () => {
1084
+ receiveSlot = {
1085
+ sourceState,
1086
+ csi: 2,
1087
+ id: '4',
1088
+ };
1089
+
1090
+ await startStatsAnalyzer();
1091
+
1092
+ // don't increase the packets when time progresses.
1093
+ await progressTime();
1094
+
1095
+ assert.neverCalledWith(
1096
+ loggerSpy,
1097
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot id: "4" and csi: 2. Total packets received on slot: ',
1098
+ 0
1099
+ );
1100
+ });
1101
+ }
1102
+ );
1103
+
1104
+ it(`logs a message if no packets are sent`, async () => {
1105
+ receiveSlot = {
1106
+ sourceState: 'live',
1107
+ csi: 2,
1108
+ id: '4',
1109
+ };
1110
+ await startStatsAnalyzer();
1111
+
1112
+ // don't increase the packets when time progresses.
1113
+ await progressTime();
1114
+
1115
+ assert.calledWith(
1116
+ loggerSpy,
1117
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
1118
+ 0
1119
+ );
1120
+
1121
+ assert.calledWith(
1122
+ loggerSpy,
1123
+ 'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
1124
+ 0
1125
+ );
1126
+
1127
+ assert.calledWith(
1128
+ loggerSpy,
1129
+ 'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
1130
+ 0
1131
+ );
1132
+
1133
+ assert.calledWith(
1134
+ loggerSpy,
1135
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
1136
+ 0
1137
+ );
1138
+
1139
+ assert.calledWith(
1140
+ loggerSpy,
1141
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
1142
+ 0
1143
+ );
1144
+
1145
+ assert.calledWith(
1146
+ loggerSpy,
1147
+ 'StatsAnalyzer:index#processInboundRTPResult --> No frames received for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames received on slot: ',
1148
+ 0
1149
+ );
1150
+ assert.calledWith(
1151
+ loggerSpy,
1152
+ 'StatsAnalyzer:index#processInboundRTPResult --> No frames decoded for mediaType: video-share-recv-0, receive slot id: "4" and csi: 2. Total frames decoded on slot: ',
1153
+ 0
1154
+ );
1155
+ assert.calledWith(
1156
+ loggerSpy,
1157
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for mediaType: audio-share-recv-0, receive slot id: "4" and csi: 2. Total packets received on slot: ',
1158
+ 0
1159
+ );
1160
+ });
1161
+
1162
+ it(`does not log a message if receiveSlot is undefined`, async () => {
1163
+ await startStatsAnalyzer();
1164
+
1165
+ // don't increase the packets when time progresses.
1166
+ await progressTime();
1167
+
1168
+ assert.neverCalledWith(
1169
+ loggerSpy,
1170
+ 'StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot "". Total packets received on slot: ',
1171
+ 0
1172
+ );
1173
+ });
1174
+
1175
+ it('has the correct number of senders and receivers (2)', async () => {
1176
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
1177
+
1178
+ await progressTime();
1179
+
1180
+ assert.lengthOf(mqeData.audioTransmit, 2);
1181
+ assert.lengthOf(mqeData.audioReceive, 2);
1182
+ assert.lengthOf(mqeData.videoTransmit, 2);
1183
+ assert.lengthOf(mqeData.videoReceive, 2);
1184
+ });
1185
+
1186
+ it('has one stream per sender/reciever', async () => {
1187
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
1188
+
1189
+ await progressTime();
1190
+
1191
+ assert.deepEqual(mqeData.audioTransmit[0].streams, [
1192
+ {
1193
+ common: {
1194
+ codec: 'opus',
1195
+ csi: [],
1196
+ requestedBitrate: 0,
1197
+ requestedFrames: 0,
1198
+ rtpPackets: 0,
1199
+ ssci: 0,
1200
+ transmittedBitrate: 0.13333333333333333,
1201
+ transmittedFrameRate: 0,
1202
+ },
1203
+ transmittedKeyFrames: 0,
1204
+ requestedKeyFrames: 0,
1205
+ },
1206
+ ]);
1207
+ assert.deepEqual(mqeData.audioTransmit[1].streams, [
1208
+ {
1209
+ common: {
1210
+ codec: 'opus',
1211
+ csi: [],
1212
+ requestedBitrate: 0,
1213
+ requestedFrames: 0,
1214
+ rtpPackets: 0,
1215
+ ssci: 0,
1216
+ transmittedBitrate: 0.13333333333333333,
1217
+ transmittedFrameRate: 0,
1218
+ },
1219
+ transmittedKeyFrames: 0,
1220
+ requestedKeyFrames: 0,
1221
+ },
1222
+ ]);
1223
+ assert.deepEqual(mqeData.audioReceive[0].streams, [
1224
+ {
1225
+ common: {
1226
+ codec: 'opus',
1227
+ concealedFrames: 0,
1228
+ csi: [],
1229
+ maxConcealRunLength: 0,
1230
+ optimalBitrate: 0,
1231
+ optimalFrameRate: 0,
1232
+ receivedBitrate: 0.13333333333333333,
1233
+ receivedFrameRate: 0,
1234
+ renderedFrameRate: 0,
1235
+ requestedBitrate: 0,
1236
+ requestedFrameRate: 0,
1237
+ rtpEndToEndLost: 0,
1238
+ maxRtpJitter: 0,
1239
+ meanRtpJitter: 0,
1240
+ rtpPackets: 0,
1241
+ ssci: 0,
1242
+ rtpJitter: 0,
1243
+ framesDropped: 0,
1244
+ framesReceived: 0,
1245
+ },
1246
+ },
1247
+ ]);
1248
+ assert.deepEqual(mqeData.audioReceive[1].streams, [
1249
+ {
1250
+ common: {
1251
+ codec: 'opus',
1252
+ concealedFrames: 0,
1253
+ csi: [],
1254
+ maxConcealRunLength: 0,
1255
+ optimalBitrate: 0,
1256
+ optimalFrameRate: 0,
1257
+ receivedBitrate: 0.13333333333333333,
1258
+ receivedFrameRate: 0,
1259
+ renderedFrameRate: 0,
1260
+ requestedBitrate: 0,
1261
+ requestedFrameRate: 0,
1262
+ rtpEndToEndLost: 0,
1263
+ maxRtpJitter: 0,
1264
+ meanRtpJitter: 0,
1265
+ rtpPackets: 0,
1266
+ ssci: 0,
1267
+ rtpJitter: 0,
1268
+ framesDropped: 0,
1269
+ framesReceived: 0,
1270
+ },
1271
+ },
1272
+ ]);
1273
+ assert.deepEqual(mqeData.videoTransmit[0].streams, [
1274
+ {
1275
+ common: {
1276
+ codec: 'H264',
1277
+ csi: [],
1278
+ duplicateSsci: 0,
1279
+ requestedBitrate: 0,
1280
+ requestedFrames: 0,
1281
+ rtpPackets: 0,
1282
+ ssci: 0,
1283
+ transmittedBitrate: 0.13333333333333333,
1284
+ transmittedFrameRate: 0,
1285
+ },
1286
+ h264CodecProfile: 'BP',
1287
+ isAvatar: false,
1288
+ isHardwareEncoded: false,
1289
+ localConfigurationChanges: 2,
1290
+ maxFrameQp: 0,
1291
+ maxNoiseLevel: 0,
1292
+ minRegionQp: 0,
1293
+ remoteConfigurationChanges: 0,
1294
+ requestedFrameSize: 0,
1295
+ requestedKeyFrames: 0,
1296
+ transmittedFrameSize: 0,
1297
+ transmittedHeight: 0,
1298
+ transmittedKeyFrames: 0,
1299
+ transmittedKeyFramesClient: 0,
1300
+ transmittedKeyFramesConfigurationChange: 0,
1301
+ transmittedKeyFramesFeedback: 0,
1302
+ transmittedKeyFramesLocalDrop: 0,
1303
+ transmittedKeyFramesOtherLayer: 0,
1304
+ transmittedKeyFramesPeriodic: 0,
1305
+ transmittedKeyFramesSceneChange: 0,
1306
+ transmittedKeyFramesStartup: 0,
1307
+ transmittedKeyFramesUnknown: 0,
1308
+ transmittedWidth: 0,
1309
+ },
1310
+ ]);
1311
+ assert.deepEqual(mqeData.videoTransmit[1].streams, [
1312
+ {
1313
+ common: {
1314
+ codec: 'H264',
1315
+ csi: [],
1316
+ duplicateSsci: 0,
1317
+ requestedBitrate: 0,
1318
+ requestedFrames: 0,
1319
+ rtpPackets: 0,
1320
+ ssci: 0,
1321
+ transmittedBitrate: 0.13333333333333333,
1322
+ transmittedFrameRate: 0,
1323
+ },
1324
+ h264CodecProfile: 'BP',
1325
+ isAvatar: false,
1326
+ isHardwareEncoded: false,
1327
+ localConfigurationChanges: 2,
1328
+ maxFrameQp: 0,
1329
+ maxNoiseLevel: 0,
1330
+ minRegionQp: 0,
1331
+ remoteConfigurationChanges: 0,
1332
+ requestedFrameSize: 0,
1333
+ requestedKeyFrames: 0,
1334
+ transmittedFrameSize: 0,
1335
+ transmittedHeight: 0,
1336
+ transmittedKeyFrames: 0,
1337
+ transmittedKeyFramesClient: 0,
1338
+ transmittedKeyFramesConfigurationChange: 0,
1339
+ transmittedKeyFramesFeedback: 0,
1340
+ transmittedKeyFramesLocalDrop: 0,
1341
+ transmittedKeyFramesOtherLayer: 0,
1342
+ transmittedKeyFramesPeriodic: 0,
1343
+ transmittedKeyFramesSceneChange: 0,
1344
+ transmittedKeyFramesStartup: 0,
1345
+ transmittedKeyFramesUnknown: 0,
1346
+ transmittedWidth: 0,
1347
+ },
1348
+ ]);
1349
+ assert.deepEqual(mqeData.videoReceive[0].streams, [
1350
+ {
1351
+ common: {
1352
+ codec: 'H264',
1353
+ concealedFrames: 0,
1354
+ csi: [],
1355
+ maxConcealRunLength: 0,
1356
+ optimalBitrate: 0,
1357
+ optimalFrameRate: 0,
1358
+ receivedBitrate: 0.13333333333333333,
1359
+ receivedFrameRate: 0,
1360
+ renderedFrameRate: 0,
1361
+ requestedBitrate: 0,
1362
+ requestedFrameRate: 0,
1363
+ rtpEndToEndLost: 0,
1364
+ rtpJitter: 0,
1365
+ rtpPackets: 0,
1366
+ ssci: 0,
1367
+ framesDropped: 0,
1368
+ },
1369
+ h264CodecProfile: 'BP',
1370
+ isActiveSpeaker: true,
1371
+ optimalFrameSize: 0,
1372
+ receivedFrameSize: 3600,
1373
+ receivedHeight: 720,
1374
+ receivedKeyFrames: 0,
1375
+ receivedKeyFramesForRequest: 0,
1376
+ receivedKeyFramesSourceChange: 0,
1377
+ receivedKeyFramesUnknown: 0,
1378
+ receivedWidth: 1280,
1379
+ requestedFrameSize: 0,
1380
+ requestedKeyFrames: 0,
1381
+ },
1382
+ ]);
1383
+ assert.deepEqual(mqeData.videoReceive[1].streams, [
1384
+ {
1385
+ common: {
1386
+ codec: 'H264',
1387
+ concealedFrames: 0,
1388
+ csi: [],
1389
+ maxConcealRunLength: 0,
1390
+ optimalBitrate: 0,
1391
+ optimalFrameRate: 0,
1392
+ receivedBitrate: 0.13333333333333333,
1393
+ receivedFrameRate: 0,
1394
+ renderedFrameRate: 0,
1395
+ requestedBitrate: 0,
1396
+ requestedFrameRate: 0,
1397
+ rtpEndToEndLost: 0,
1398
+ rtpJitter: 0,
1399
+ rtpPackets: 0,
1400
+ ssci: 0,
1401
+ framesDropped: 0,
1402
+ },
1403
+ h264CodecProfile: 'BP',
1404
+ isActiveSpeaker: true,
1405
+ optimalFrameSize: 0,
1406
+ receivedFrameSize: 3600,
1407
+ receivedHeight: 720,
1408
+ receivedKeyFrames: 0,
1409
+ receivedKeyFramesForRequest: 0,
1410
+ receivedKeyFramesSourceChange: 0,
1411
+ receivedKeyFramesUnknown: 0,
1412
+ receivedWidth: 1280,
1413
+ requestedFrameSize: 0,
1414
+ requestedKeyFrames: 0,
1415
+ },
1416
+ ]);
1417
+ });
1418
+
1419
+ it('has three streams for video receivers when three exist', async () => {
1420
+ pc.getTransceiverStats = sinon.stub().resolves({
1421
+ audio: {
1422
+ senders: [fakeStats.audio.senders[0]],
1423
+ receivers: [fakeStats.audio.receivers[0]],
1424
+ },
1425
+ video: {
1426
+ senders: [fakeStats.video.senders[0]],
1427
+ receivers: [
1428
+ fakeStats.video.receivers[0],
1429
+ fakeStats.video.receivers[0],
1430
+ fakeStats.video.receivers[0],
1431
+ ],
1432
+ },
1433
+ screenShareAudio: {
1434
+ senders: [fakeStats.audio.senders[0]],
1435
+ receivers: [fakeStats.audio.receivers[0]],
1436
+ },
1437
+ screenShareVideo: {
1438
+ senders: [fakeStats.video.senders[0]],
1439
+ receivers: [fakeStats.video.receivers[0]],
1440
+ },
1441
+ });
1442
+
1443
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
1444
+
1445
+ await progressTime();
1446
+
1447
+ assert.deepEqual(mqeData.videoReceive[0].streams, [
1448
+ {
1449
+ common: {
1450
+ codec: 'H264',
1451
+ concealedFrames: 0,
1452
+ csi: [],
1453
+ maxConcealRunLength: 0,
1454
+ optimalBitrate: 0,
1455
+ optimalFrameRate: 0,
1456
+ receivedBitrate: 0.13333333333333333,
1457
+ receivedFrameRate: 0,
1458
+ renderedFrameRate: 0,
1459
+ requestedBitrate: 0,
1460
+ requestedFrameRate: 0,
1461
+ rtpEndToEndLost: 0,
1462
+ rtpJitter: 0,
1463
+ rtpPackets: 0,
1464
+ ssci: 0,
1465
+ framesDropped: 0,
1466
+ },
1467
+ h264CodecProfile: 'BP',
1468
+ isActiveSpeaker: true,
1469
+ optimalFrameSize: 0,
1470
+ receivedFrameSize: 3600,
1471
+ receivedHeight: 720,
1472
+ receivedKeyFrames: 0,
1473
+ receivedKeyFramesForRequest: 0,
1474
+ receivedKeyFramesSourceChange: 0,
1475
+ receivedKeyFramesUnknown: 0,
1476
+ receivedWidth: 1280,
1477
+ requestedFrameSize: 0,
1478
+ requestedKeyFrames: 0,
1479
+ },
1480
+ {
1481
+ common: {
1482
+ codec: 'H264',
1483
+ concealedFrames: 0,
1484
+ csi: [],
1485
+ maxConcealRunLength: 0,
1486
+ optimalBitrate: 0,
1487
+ optimalFrameRate: 0,
1488
+ receivedBitrate: 0.13333333333333333,
1489
+ receivedFrameRate: 0,
1490
+ renderedFrameRate: 0,
1491
+ requestedBitrate: 0,
1492
+ requestedFrameRate: 0,
1493
+ rtpEndToEndLost: 0,
1494
+ rtpJitter: 0,
1495
+ rtpPackets: 0,
1496
+ ssci: 0,
1497
+ framesDropped: 0,
1498
+ },
1499
+ h264CodecProfile: 'BP',
1500
+ isActiveSpeaker: true,
1501
+ optimalFrameSize: 0,
1502
+ receivedFrameSize: 3600,
1503
+ receivedHeight: 720,
1504
+ receivedKeyFrames: 0,
1505
+ receivedKeyFramesForRequest: 0,
1506
+ receivedKeyFramesSourceChange: 0,
1507
+ receivedKeyFramesUnknown: 0,
1508
+ receivedWidth: 1280,
1509
+ requestedFrameSize: 0,
1510
+ requestedKeyFrames: 0,
1511
+ },
1512
+ {
1513
+ common: {
1514
+ codec: 'H264',
1515
+ concealedFrames: 0,
1516
+ csi: [],
1517
+ maxConcealRunLength: 0,
1518
+ optimalBitrate: 0,
1519
+ optimalFrameRate: 0,
1520
+ receivedBitrate: 0.13333333333333333,
1521
+ receivedFrameRate: 0,
1522
+ renderedFrameRate: 0,
1523
+ requestedBitrate: 0,
1524
+ requestedFrameRate: 0,
1525
+ rtpEndToEndLost: 0,
1526
+ rtpJitter: 0,
1527
+ rtpPackets: 0,
1528
+ ssci: 0,
1529
+ framesDropped: 0,
1530
+ },
1531
+ h264CodecProfile: 'BP',
1532
+ isActiveSpeaker: true,
1533
+ optimalFrameSize: 0,
1534
+ receivedFrameSize: 3600,
1535
+ receivedHeight: 720,
1536
+ receivedKeyFrames: 0,
1537
+ receivedKeyFramesForRequest: 0,
1538
+ receivedKeyFramesSourceChange: 0,
1539
+ receivedKeyFramesUnknown: 0,
1540
+ receivedWidth: 1280,
1541
+ requestedFrameSize: 0,
1542
+ requestedKeyFrames: 0,
1543
+ },
1544
+ ]);
1545
+ });
306
1546
  });
307
1547
  });
308
1548
  });