@stream-io/video-react-sdk 1.41.0 → 1.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/CHANGELOG.md +30 -6
  2. package/dist/{embedded-BackgroundFilters-folwWrgs.cjs.js → background-filters-DMStbSEg.cjs.js} +97 -71
  3. package/dist/background-filters-DMStbSEg.cjs.js.map +1 -0
  4. package/dist/{background-filters-folwWrgs.cjs.js → embedded-BackgroundFilters-DMStbSEg.cjs.js} +97 -71
  5. package/dist/embedded-BackgroundFilters-DMStbSEg.cjs.js.map +1 -0
  6. package/dist/embedded.cjs.js +2 -2
  7. package/dist/esm/embedded.js +3 -0
  8. package/dist/esm/embedded.js.map +1 -0
  9. package/dist/esm/index.js +92 -0
  10. package/dist/esm/index.js.map +1 -0
  11. package/dist/esm/src/components/Avatar/Avatar.js +14 -0
  12. package/dist/esm/src/components/Avatar/Avatar.js.map +1 -0
  13. package/dist/{background-filters-D2U5UBbH.es.js → esm/src/components/BackgroundFilters/BackgroundFilters.js} +97 -71
  14. package/dist/esm/src/components/BackgroundFilters/BackgroundFilters.js.map +1 -0
  15. package/dist/esm/src/components/BackgroundFilters/BackgroundFiltersProvider.js +33 -0
  16. package/dist/esm/src/components/BackgroundFilters/BackgroundFiltersProvider.js.map +1 -0
  17. package/dist/esm/src/components/Button/CompositeButton.js +29 -0
  18. package/dist/esm/src/components/Button/CompositeButton.js.map +1 -0
  19. package/dist/esm/src/components/Button/IconButton.js +18 -0
  20. package/dist/esm/src/components/Button/IconButton.js.map +1 -0
  21. package/dist/esm/src/components/Button/TextButton.js +8 -0
  22. package/dist/esm/src/components/Button/TextButton.js.map +1 -0
  23. package/dist/esm/src/components/CallControls/AcceptCallButton.js +28 -0
  24. package/dist/esm/src/components/CallControls/AcceptCallButton.js.map +1 -0
  25. package/dist/esm/src/components/CallControls/CallControls.js +21 -0
  26. package/dist/esm/src/components/CallControls/CallControls.js.map +1 -0
  27. package/dist/esm/src/components/CallControls/CallStatsButton.js +18 -0
  28. package/dist/esm/src/components/CallControls/CallStatsButton.js.map +1 -0
  29. package/dist/esm/src/components/CallControls/CancelCallButton.js +78 -0
  30. package/dist/esm/src/components/CallControls/CancelCallButton.js.map +1 -0
  31. package/dist/esm/src/components/CallControls/ReactionsButton.js +61 -0
  32. package/dist/esm/src/components/CallControls/ReactionsButton.js.map +1 -0
  33. package/dist/esm/src/components/CallControls/RecordCallButton.js +64 -0
  34. package/dist/esm/src/components/CallControls/RecordCallButton.js.map +1 -0
  35. package/dist/esm/src/components/CallControls/ScreenShareButton.js +44 -0
  36. package/dist/esm/src/components/CallControls/ScreenShareButton.js.map +1 -0
  37. package/dist/esm/src/components/CallControls/ToggleAudioButton.js +72 -0
  38. package/dist/esm/src/components/CallControls/ToggleAudioButton.js.map +1 -0
  39. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js +27 -0
  40. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js.map +1 -0
  41. package/dist/esm/src/components/CallControls/ToggleVideoButton.js +81 -0
  42. package/dist/esm/src/components/CallControls/ToggleVideoButton.js.map +1 -0
  43. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js +23 -0
  44. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js.map +1 -0
  45. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js +15 -0
  46. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js.map +1 -0
  47. package/dist/esm/src/components/CallParticipantsList/CallParticipantListing.js +7 -0
  48. package/dist/esm/src/components/CallParticipantsList/CallParticipantListing.js.map +1 -0
  49. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js +46 -0
  50. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -0
  51. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js +81 -0
  52. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js.map +1 -0
  53. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js +10 -0
  54. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js.map +1 -0
  55. package/dist/esm/src/components/CallPreview/CallPreview.js +24 -0
  56. package/dist/esm/src/components/CallPreview/CallPreview.js.map +1 -0
  57. package/dist/esm/src/components/CallRecordingList/CallRecordingList.js +12 -0
  58. package/dist/esm/src/components/CallRecordingList/CallRecordingList.js.map +1 -0
  59. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js +12 -0
  60. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js.map +1 -0
  61. package/dist/esm/src/components/CallRecordingList/CallRecordingListItem.js +14 -0
  62. package/dist/esm/src/components/CallRecordingList/CallRecordingListItem.js.map +1 -0
  63. package/dist/esm/src/components/CallRecordingList/EmptyCallRecordingListing.js +8 -0
  64. package/dist/esm/src/components/CallRecordingList/EmptyCallRecordingListing.js.map +1 -0
  65. package/dist/esm/src/components/CallRecordingList/LoadingCallRecordingListing.js +10 -0
  66. package/dist/esm/src/components/CallRecordingList/LoadingCallRecordingListing.js.map +1 -0
  67. package/dist/esm/src/components/CallStats/CallStats.js +180 -0
  68. package/dist/esm/src/components/CallStats/CallStats.js.map +1 -0
  69. package/dist/{latency-chart-Bj5OSYzg.es.js → esm/src/components/CallStats/CallStatsLatencyChart.js} +1 -1
  70. package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js.map +1 -0
  71. package/dist/esm/src/components/DeviceSettings/AudioVolumeIndicator.js +27 -0
  72. package/dist/esm/src/components/DeviceSettings/AudioVolumeIndicator.js.map +1 -0
  73. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js +42 -0
  74. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js.map +1 -0
  75. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js +67 -0
  76. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js.map +1 -0
  77. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js +26 -0
  78. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js.map +1 -0
  79. package/dist/esm/src/components/DeviceSettings/DeviceSelectorVideo.js +15 -0
  80. package/dist/esm/src/components/DeviceSettings/DeviceSelectorVideo.js.map +1 -0
  81. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js +26 -0
  82. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js.map +1 -0
  83. package/dist/esm/src/components/DeviceSettings/DeviceVideoPreviewItem.js +42 -0
  84. package/dist/esm/src/components/DeviceSettings/DeviceVideoPreviewItem.js.map +1 -0
  85. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js +57 -0
  86. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js.map +1 -0
  87. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js +79 -0
  88. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js.map +1 -0
  89. package/dist/esm/src/components/Icon/Icon.js +7 -0
  90. package/dist/esm/src/components/Icon/Icon.js.map +1 -0
  91. package/dist/esm/src/components/LoadingIndicator/LoadingIndicator.js +9 -0
  92. package/dist/esm/src/components/LoadingIndicator/LoadingIndicator.js.map +1 -0
  93. package/dist/esm/src/components/Menu/GenericMenu.js +19 -0
  94. package/dist/esm/src/components/Menu/GenericMenu.js.map +1 -0
  95. package/dist/esm/src/components/Menu/MenuToggle.js +75 -0
  96. package/dist/esm/src/components/Menu/MenuToggle.js.map +1 -0
  97. package/dist/esm/src/components/NoiseCancellation/NoiseCancellationProvider.js +96 -0
  98. package/dist/esm/src/components/NoiseCancellation/NoiseCancellationProvider.js.map +1 -0
  99. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js +23 -0
  100. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js.map +1 -0
  101. package/dist/esm/src/components/Notification/Notification.js +31 -0
  102. package/dist/esm/src/components/Notification/Notification.js.map +1 -0
  103. package/dist/esm/src/components/Notification/PermissionNotification.js +32 -0
  104. package/dist/esm/src/components/Notification/PermissionNotification.js.map +1 -0
  105. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js +23 -0
  106. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js.map +1 -0
  107. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js +14 -0
  108. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js.map +1 -0
  109. package/dist/esm/src/components/Permissions/PermissionRequests.js +99 -0
  110. package/dist/esm/src/components/Permissions/PermissionRequests.js.map +1 -0
  111. package/dist/esm/src/components/Reaction/Reaction.js +32 -0
  112. package/dist/esm/src/components/Reaction/Reaction.js.map +1 -0
  113. package/dist/esm/src/components/RingingCall/RingingCall.js +48 -0
  114. package/dist/esm/src/components/RingingCall/RingingCall.js.map +1 -0
  115. package/dist/esm/src/components/RingingCall/RingingCallControls.js +34 -0
  116. package/dist/esm/src/components/RingingCall/RingingCallControls.js.map +1 -0
  117. package/dist/esm/src/components/Search/SearchInput.js +27 -0
  118. package/dist/esm/src/components/Search/SearchInput.js.map +1 -0
  119. package/dist/esm/src/components/Search/SearchResults.js +15 -0
  120. package/dist/esm/src/components/Search/SearchResults.js.map +1 -0
  121. package/dist/esm/src/components/Search/hooks/useSearch.js +38 -0
  122. package/dist/esm/src/components/Search/hooks/useSearch.js.map +1 -0
  123. package/dist/esm/src/components/StreamTheme/StreamTheme.js +9 -0
  124. package/dist/esm/src/components/StreamTheme/StreamTheme.js.map +1 -0
  125. package/dist/esm/src/components/Tooltip/Tooltip.js +29 -0
  126. package/dist/esm/src/components/Tooltip/Tooltip.js.map +1 -0
  127. package/dist/esm/src/components/Tooltip/WithTooltip.js +15 -0
  128. package/dist/esm/src/components/Tooltip/WithTooltip.js.map +1 -0
  129. package/dist/esm/src/components/Tooltip/hooks/useEnterLeaveHandlers.js +17 -0
  130. package/dist/esm/src/components/Tooltip/hooks/useEnterLeaveHandlers.js.map +1 -0
  131. package/dist/esm/src/components/VideoPreview/VideoPreview.js +37 -0
  132. package/dist/esm/src/components/VideoPreview/VideoPreview.js.map +1 -0
  133. package/dist/esm/src/core/components/Audio/Audio.js +22 -0
  134. package/dist/esm/src/core/components/Audio/Audio.js.map +1 -0
  135. package/dist/esm/src/core/components/Audio/ParticipantsAudio.js +23 -0
  136. package/dist/esm/src/core/components/Audio/ParticipantsAudio.js.map +1 -0
  137. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js +150 -0
  138. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js.map +1 -0
  139. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js +79 -0
  140. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -0
  141. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js +80 -0
  142. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js.map +1 -0
  143. package/dist/esm/src/core/components/CallLayout/PipLayout/Host.js +13 -0
  144. package/dist/esm/src/core/components/CallLayout/PipLayout/Host.js.map +1 -0
  145. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js +57 -0
  146. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js.map +1 -0
  147. package/dist/esm/src/core/components/CallLayout/PipLayout/index.js +8 -0
  148. package/dist/esm/src/core/components/CallLayout/PipLayout/index.js.map +1 -0
  149. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js +103 -0
  150. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js.map +1 -0
  151. package/dist/esm/src/core/components/CallLayout/hooks.js +62 -0
  152. package/dist/esm/src/core/components/CallLayout/hooks.js.map +1 -0
  153. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js +94 -0
  154. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js.map +1 -0
  155. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js +97 -0
  156. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js.map +1 -0
  157. package/dist/esm/src/core/components/ParticipantView/ParticipantView.js +65 -0
  158. package/dist/esm/src/core/components/ParticipantView/ParticipantView.js.map +1 -0
  159. package/dist/esm/src/core/components/ParticipantView/ParticipantViewContext.js +7 -0
  160. package/dist/esm/src/core/components/ParticipantView/ParticipantViewContext.js.map +1 -0
  161. package/dist/esm/src/core/components/StreamCall/StreamCall.js +8 -0
  162. package/dist/esm/src/core/components/StreamCall/StreamCall.js.map +1 -0
  163. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js +11 -0
  164. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js.map +1 -0
  165. package/dist/esm/src/core/components/Video/BaseVideo.js +41 -0
  166. package/dist/esm/src/core/components/Video/BaseVideo.js.map +1 -0
  167. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js +21 -0
  168. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js.map +1 -0
  169. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js +12 -0
  170. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js.map +1 -0
  171. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js +12 -0
  172. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js.map +1 -0
  173. package/dist/esm/src/core/components/Video/Video.js +88 -0
  174. package/dist/esm/src/core/components/Video/Video.js.map +1 -0
  175. package/dist/esm/src/core/hooks/useCalculateHardLimit.js +59 -0
  176. package/dist/esm/src/core/hooks/useCalculateHardLimit.js.map +1 -0
  177. package/dist/esm/src/core/hooks/usePictureInPictureState.js +24 -0
  178. package/dist/esm/src/core/hooks/usePictureInPictureState.js.map +1 -0
  179. package/dist/esm/src/core/hooks/useTrackElementVisibility.js +14 -0
  180. package/dist/esm/src/core/hooks/useTrackElementVisibility.js.map +1 -0
  181. package/dist/esm/src/embedded/EmbeddedClientProvider.js +83 -0
  182. package/dist/esm/src/embedded/EmbeddedClientProvider.js.map +1 -0
  183. package/dist/esm/src/embedded/call/CallControls.js +65 -0
  184. package/dist/esm/src/embedded/call/CallControls.js.map +1 -0
  185. package/dist/esm/src/embedded/call/CallHeader.js +57 -0
  186. package/dist/esm/src/embedded/call/CallHeader.js.map +1 -0
  187. package/dist/esm/src/embedded/call/CallLayout.js +63 -0
  188. package/dist/esm/src/embedded/call/CallLayout.js.map +1 -0
  189. package/dist/esm/src/embedded/call/CallStateRouter.js +87 -0
  190. package/dist/esm/src/embedded/call/CallStateRouter.js.map +1 -0
  191. package/dist/esm/src/embedded/call/EmbeddedCall.js +12 -0
  192. package/dist/esm/src/embedded/call/EmbeddedCall.js.map +1 -0
  193. package/dist/esm/src/embedded/context/ConfigurationContext.js +21 -0
  194. package/dist/esm/src/embedded/context/ConfigurationContext.js.map +1 -0
  195. package/dist/esm/src/embedded/hooks/useCallDuration.js +32 -0
  196. package/dist/esm/src/embedded/hooks/useCallDuration.js.map +1 -0
  197. package/dist/esm/src/embedded/hooks/useEmbeddedClient.js +40 -0
  198. package/dist/esm/src/embedded/hooks/useEmbeddedClient.js.map +1 -0
  199. package/dist/esm/src/embedded/hooks/useInitializeCall.js +39 -0
  200. package/dist/esm/src/embedded/hooks/useInitializeCall.js.map +1 -0
  201. package/dist/esm/src/embedded/hooks/useInitializeVideoClient.js +93 -0
  202. package/dist/esm/src/embedded/hooks/useInitializeVideoClient.js.map +1 -0
  203. package/dist/esm/src/embedded/hooks/useIsLivestreamPaused.js +42 -0
  204. package/dist/esm/src/embedded/hooks/useIsLivestreamPaused.js.map +1 -0
  205. package/dist/esm/src/embedded/hooks/useLayout.js +122 -0
  206. package/dist/esm/src/embedded/hooks/useLayout.js.map +1 -0
  207. package/dist/esm/src/embedded/hooks/useNoiseCancellationLoader.js +48 -0
  208. package/dist/esm/src/embedded/hooks/useNoiseCancellationLoader.js.map +1 -0
  209. package/dist/esm/src/embedded/hooks/useWakeLock.js +34 -0
  210. package/dist/esm/src/embedded/hooks/useWakeLock.js.map +1 -0
  211. package/dist/esm/src/embedded/livestream/EmbeddedLivestream.js +11 -0
  212. package/dist/esm/src/embedded/livestream/EmbeddedLivestream.js.map +1 -0
  213. package/dist/esm/src/embedded/livestream/LivestreamUI.js +17 -0
  214. package/dist/esm/src/embedded/livestream/LivestreamUI.js.map +1 -0
  215. package/dist/esm/src/embedded/livestream/host/HostLayout.js +76 -0
  216. package/dist/esm/src/embedded/livestream/host/HostLayout.js.map +1 -0
  217. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js +110 -0
  218. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js.map +1 -0
  219. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js +74 -0
  220. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js.map +1 -0
  221. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js +109 -0
  222. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js.map +1 -0
  223. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js +105 -0
  224. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js.map +1 -0
  225. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js +65 -0
  226. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js.map +1 -0
  227. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js +34 -0
  228. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js.map +1 -0
  229. package/dist/esm/src/embedded/shared/CallFeedback/CallFeedback.js +35 -0
  230. package/dist/esm/src/embedded/shared/CallFeedback/CallFeedback.js.map +1 -0
  231. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js +18 -0
  232. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js.map +1 -0
  233. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js +37 -0
  234. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js.map +1 -0
  235. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js +34 -0
  236. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js.map +1 -0
  237. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js +56 -0
  238. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js.map +1 -0
  239. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js +42 -0
  240. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js.map +1 -0
  241. package/dist/esm/src/embedded/shared/JoinError/JoinError.js +34 -0
  242. package/dist/esm/src/embedded/shared/JoinError/JoinError.js.map +1 -0
  243. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js +44 -0
  244. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js.map +1 -0
  245. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js +31 -0
  246. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js.map +1 -0
  247. package/dist/esm/src/embedded/shared/Lobby/Lobby.js +45 -0
  248. package/dist/esm/src/embedded/shared/Lobby/Lobby.js.map +1 -0
  249. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js +40 -0
  250. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js.map +1 -0
  251. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js +41 -0
  252. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js.map +1 -0
  253. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js +50 -0
  254. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js.map +1 -0
  255. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js +31 -0
  256. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js.map +1 -0
  257. package/dist/esm/src/hooks/useDeviceList.js +40 -0
  258. package/dist/esm/src/hooks/useDeviceList.js.map +1 -0
  259. package/dist/esm/src/hooks/useDragToScroll.js +116 -0
  260. package/dist/esm/src/hooks/useDragToScroll.js.map +1 -0
  261. package/dist/esm/src/hooks/useFloatingUIPreset.js +34 -0
  262. package/dist/esm/src/hooks/useFloatingUIPreset.js.map +1 -0
  263. package/dist/esm/src/hooks/useLoopbackRecording.js +138 -0
  264. package/dist/esm/src/hooks/useLoopbackRecording.js.map +1 -0
  265. package/dist/esm/src/hooks/useModeration.js +135 -0
  266. package/dist/esm/src/hooks/useModeration.js.map +1 -0
  267. package/dist/esm/src/hooks/usePersistedDevicePreferences.js +16 -0
  268. package/dist/esm/src/hooks/usePersistedDevicePreferences.js.map +1 -0
  269. package/dist/esm/src/hooks/useRequestPermission.js +41 -0
  270. package/dist/esm/src/hooks/useRequestPermission.js.map +1 -0
  271. package/dist/esm/src/hooks/useScrollPosition.js +66 -0
  272. package/dist/esm/src/hooks/useScrollPosition.js.map +1 -0
  273. package/dist/esm/src/translations/en.json.js +177 -0
  274. package/dist/esm/src/translations/en.json.js.map +1 -0
  275. package/dist/esm/src/translations/index.js +6 -0
  276. package/dist/esm/src/translations/index.js.map +1 -0
  277. package/dist/esm/src/utilities/applyElementToRef.js +10 -0
  278. package/dist/esm/src/utilities/applyElementToRef.js.map +1 -0
  279. package/dist/esm/src/utilities/callControlHandler.js +29 -0
  280. package/dist/esm/src/utilities/callControlHandler.js.map +1 -0
  281. package/dist/esm/src/utilities/chunk.js +7 -0
  282. package/dist/esm/src/utilities/chunk.js.map +1 -0
  283. package/dist/esm/src/utilities/filter.js +94 -0
  284. package/dist/esm/src/utilities/filter.js.map +1 -0
  285. package/dist/esm/src/utilities/isComponentType.js +10 -0
  286. package/dist/esm/src/utilities/isComponentType.js.map +1 -0
  287. package/dist/esm/src/utilities/normalizeString.js +11 -0
  288. package/dist/esm/src/utilities/normalizeString.js.map +1 -0
  289. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js +105 -0
  290. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js.map +1 -0
  291. package/dist/index.cjs.js +136 -3
  292. package/dist/index.cjs.js.map +1 -1
  293. package/dist/index.d.ts +1 -1
  294. package/dist/src/hooks/index.d.ts +1 -0
  295. package/dist/src/hooks/useLoopbackRecording.d.ts +23 -0
  296. package/index.ts +4 -0
  297. package/package.json +14 -10
  298. package/src/components/BackgroundFilters/BackgroundFilters.tsx +139 -103
  299. package/src/hooks/index.ts +1 -0
  300. package/src/hooks/useLoopbackRecording.ts +207 -0
  301. package/dist/background-filters-D2U5UBbH.es.js.map +0 -1
  302. package/dist/background-filters-folwWrgs.cjs.js.map +0 -1
  303. package/dist/embedded-BackgroundFilters-D2U5UBbH.es.js +0 -420
  304. package/dist/embedded-BackgroundFilters-D2U5UBbH.es.js.map +0 -1
  305. package/dist/embedded-BackgroundFilters-folwWrgs.cjs.js.map +0 -1
  306. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js +0 -57
  307. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js.map +0 -1
  308. package/dist/embedded.es.js +0 -3536
  309. package/dist/embedded.es.js.map +0 -1
  310. package/dist/index.es.js +0 -3291
  311. package/dist/index.es.js.map +0 -1
  312. package/dist/latency-chart-Bj5OSYzg.es.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export * from './src/core';
4
4
  export * from './src/components';
5
5
  export * from './src/wrappers';
6
6
  export * from './src/translations';
7
- export { useHorizontalScrollPosition, useVerticalScrollPosition, useRequestPermission, usePersistedDevicePreferences, useDeviceList, useModeration, } from './src/hooks';
7
+ export { useHorizontalScrollPosition, useVerticalScrollPosition, useRequestPermission, usePersistedDevicePreferences, useDeviceList, useModeration, useLoopbackRecording, type LoopbackRecordingState, type StartLoopbackRecordingOptions, type UseLoopbackRecordingResult, } from './src/hooks';
8
8
  export { applyFilter, type Filter } from './src/utilities/filter';
@@ -5,3 +5,4 @@ export * from './useRequestPermission';
5
5
  export * from './useDeviceList';
6
6
  export * from './useModeration';
7
7
  export * from './useDragToScroll';
8
+ export * from './useLoopbackRecording';
@@ -0,0 +1,23 @@
1
+ import { type MediaRecordingResult } from '@stream-io/video-client';
2
+ /** Lifecycle phase of a loopback recording. */
3
+ export type LoopbackRecordingState = 'idle' | 'awaiting-streams' | 'recording';
4
+ export type StartLoopbackRecordingOptions = {
5
+ /** Whether to include loopback video. Audio is always recorded. */
6
+ includeVideo?: boolean;
7
+ /** Maximum recording duration in milliseconds. */
8
+ maxDurationMs?: number;
9
+ };
10
+ export type UseLoopbackRecordingResult = {
11
+ /** Starts recording and resolves with the recorded blob. */
12
+ startRecording: (options?: StartLoopbackRecordingOptions) => Promise<MediaRecordingResult | null>;
13
+ /** Stops the current recording or stream wait. */
14
+ stopRecording: () => Promise<void>;
15
+ /** Lifecycle phase of the recording. */
16
+ recordingState: LoopbackRecordingState;
17
+ /** SFU-echoed video stream on the local participant. */
18
+ loopbackVideoStream?: MediaStream;
19
+ /** SFU-echoed audio stream on the local participant. */
20
+ loopbackAudioStream?: MediaStream;
21
+ };
22
+ /** Records SFU loopback streams for pre-call testing. */
23
+ export declare const useLoopbackRecording: () => UseLoopbackRecordingResult;
package/index.ts CHANGED
@@ -15,6 +15,10 @@ export {
15
15
  usePersistedDevicePreferences,
16
16
  useDeviceList,
17
17
  useModeration,
18
+ useLoopbackRecording,
19
+ type LoopbackRecordingState,
20
+ type StartLoopbackRecordingOptions,
21
+ type UseLoopbackRecordingResult,
18
22
  } from './src/hooks';
19
23
  export { applyFilter, type Filter } from './src/utilities/filter';
20
24
 
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "1.41.0",
3
+ "version": "1.43.0",
4
4
  "main": "./dist/index.cjs.js",
5
- "module": "./dist/index.es.js",
5
+ "module": "./dist/esm/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
- "import": "./dist/index.es.js",
10
- "require": "./dist/index.cjs.js",
11
- "types": "./dist/index.d.ts"
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/esm/index.js",
11
+ "require": "./dist/index.cjs.js"
12
12
  },
13
13
  "./embedded": {
14
14
  "types": "./dist/embedded.d.ts",
15
- "import": "./dist/embedded.es.js",
15
+ "import": "./dist/esm/embedded.js",
16
16
  "require": "./dist/embedded.cjs.js"
17
17
  },
18
18
  "./dist/css/*": "./dist/css/*"
@@ -41,13 +41,17 @@
41
41
  "CHANGELOG.md"
42
42
  ],
43
43
  "sideEffects": [
44
- "*.css"
44
+ "*.css",
45
+ "./dist/esm/index.js",
46
+ "./dist/esm/embedded.js",
47
+ "./dist/index.cjs.js",
48
+ "./dist/embedded.cjs.js"
45
49
  ],
46
50
  "dependencies": {
47
51
  "@floating-ui/react": "^0.27.20",
48
- "@stream-io/video-client": "1.58.0",
49
- "@stream-io/video-filters-web": "0.8.6",
50
- "@stream-io/video-react-bindings": "1.20.0",
52
+ "@stream-io/video-client": "1.59.1",
53
+ "@stream-io/video-filters-web": "0.10.0",
54
+ "@stream-io/video-react-bindings": "1.20.2",
51
55
  "chart.js": "^4.5.1",
52
56
  "clsx": "^2.1.1",
53
57
  "react-chartjs-2": "^5.3.1"
@@ -12,6 +12,7 @@ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
12
12
  import { Call, disposeOfMediaStream } from '@stream-io/video-client';
13
13
  import {
14
14
  BackgroundBlurLevel,
15
+ BackgroundEffectOptions,
15
16
  createRenderer,
16
17
  isMediaPipePlatformSupported,
17
18
  isPlatformSupported,
@@ -431,8 +432,10 @@ const BackgroundFilters = (props: {
431
432
  >(undefined);
432
433
  handleStatsRef.current = onStats;
433
434
 
435
+ const filterActive = !!backgroundFilter;
436
+
434
437
  useEffect(() => {
435
- if (!call || !backgroundFilter) return;
438
+ if (!call || !filterActive) return;
436
439
 
437
440
  setIsLoading(true);
438
441
  const { unregister, registered } = call.camera.registerFilter((ms) => {
@@ -449,7 +452,7 @@ const BackgroundFilters = (props: {
449
452
  return () => {
450
453
  unregister().catch((err) => console.warn(`Can't unregister filter`, err));
451
454
  };
452
- }, [call, start, backgroundFilter, setIsLoading]);
455
+ }, [call, start, filterActive, setIsLoading]);
453
456
 
454
457
  return children;
455
458
  };
@@ -469,6 +472,16 @@ const useRenderer = (
469
472
  segmentationOptions,
470
473
  } = api;
471
474
 
475
+ const optionsRef = useRef<BackgroundEffectOptions>({});
476
+ optionsRef.current = {
477
+ backgroundFilter,
478
+ backgroundBlurLevel,
479
+ backgroundImage,
480
+ segmentationOptions,
481
+ };
482
+
483
+ const processorRef = useRef<VirtualBackground | undefined>(undefined);
484
+
472
485
  const videoRef = useRef<HTMLVideoElement>(null);
473
486
  const canvasRef = useRef<HTMLCanvasElement>(null);
474
487
  const bgImageRef = useRef<HTMLImageElement>(null);
@@ -479,7 +492,7 @@ const useRenderer = (
479
492
  },
480
493
  );
481
494
 
482
- const start = useCallback(
495
+ const startMediaPipe = useCallback(
483
496
  (
484
497
  ms: MediaStream,
485
498
  onError?: (error: any) => void,
@@ -487,6 +500,65 @@ const useRenderer = (
487
500
  ) => {
488
501
  let outputStream: MediaStream | undefined;
489
502
  let processor: VirtualBackground | undefined;
503
+
504
+ const output = new Promise<MediaStream>((resolve, reject) => {
505
+ const options = optionsRef.current;
506
+ if (!options.backgroundFilter) {
507
+ reject(new Error('No filter specified'));
508
+ return;
509
+ }
510
+
511
+ const [track] = ms.getVideoTracks();
512
+ if (!track) {
513
+ reject(new Error('No video tracks in input media stream'));
514
+ return;
515
+ }
516
+
517
+ call?.tracer.trace('backgroundFilters.enable', {
518
+ ...options,
519
+ engine: FilterEngine.MEDIA_PIPE,
520
+ modelFilePath,
521
+ });
522
+
523
+ const trackSettings = track.getSettings();
524
+ flushSync(() =>
525
+ setVideoSize({
526
+ width: trackSettings.width ?? 0,
527
+ height: trackSettings.height ?? 0,
528
+ }),
529
+ );
530
+
531
+ processor = new VirtualBackground(
532
+ track,
533
+ { ...options, basePath, modelPath: modelFilePath },
534
+ { onError, onStats },
535
+ );
536
+ processorRef.current = processor;
537
+ processor
538
+ .start()
539
+ .then((processedTrack) => {
540
+ outputStream = new MediaStream([processedTrack]);
541
+ resolve(outputStream);
542
+ })
543
+ .catch(reject);
544
+ });
545
+
546
+ return {
547
+ output,
548
+ stop: () => {
549
+ call?.tracer.trace('backgroundFilters.disable', null);
550
+ processorRef.current = undefined;
551
+ processor?.stop();
552
+ if (outputStream) disposeOfMediaStream(outputStream);
553
+ },
554
+ };
555
+ },
556
+ [call?.tracer, modelFilePath, basePath],
557
+ );
558
+
559
+ const startTf = useCallback(
560
+ (ms: MediaStream, onError?: (error: any) => void) => {
561
+ let outputStream: MediaStream | undefined;
490
562
  let renderer: Renderer | undefined;
491
563
 
492
564
  const output = new Promise<MediaStream>((resolve, reject) => {
@@ -500,117 +572,63 @@ const useRenderer = (
500
572
  const bgImageEl = bgImageRef.current;
501
573
 
502
574
  const [track] = ms.getVideoTracks();
503
-
504
575
  if (!track) {
505
576
  reject(new Error('No video tracks in input media stream'));
506
577
  return;
507
578
  }
508
579
 
509
- if (engine === FilterEngine.MEDIA_PIPE) {
510
- call?.tracer.trace('backgroundFilters.enable', {
511
- backgroundFilter,
512
- backgroundBlurLevel,
513
- backgroundImage,
514
- engine,
515
- modelFilePath,
516
- });
517
-
518
- if (!videoEl) {
519
- reject(new Error('Renderer started before elements are ready'));
520
- return;
521
- }
522
-
523
- const trackSettings = track.getSettings();
524
- flushSync(() =>
525
- setVideoSize({
526
- width: trackSettings.width ?? 0,
527
- height: trackSettings.height ?? 0,
528
- }),
529
- );
530
-
531
- processor = new VirtualBackground(
532
- track,
533
- {
534
- basePath: basePath,
535
- modelPath: modelFilePath,
580
+ if (!videoEl || !canvasEl || (backgroundImage && !bgImageEl)) {
581
+ reject(new Error('Renderer started before elements are ready'));
582
+ return;
583
+ }
584
+
585
+ videoEl.srcObject = ms;
586
+ videoEl.play().then(
587
+ () => {
588
+ const trackSettings = track.getSettings();
589
+ flushSync(() =>
590
+ setVideoSize({
591
+ width: trackSettings.width ?? 0,
592
+ height: trackSettings.height ?? 0,
593
+ }),
594
+ );
595
+ call?.tracer.trace('backgroundFilters.enable', {
596
+ backgroundFilter,
536
597
  backgroundBlurLevel,
537
598
  backgroundImage,
538
- backgroundFilter,
539
- segmentationOptions,
540
- },
541
- { onError, onStats },
542
- );
543
- processor
544
- .start()
545
- .then((processedTrack) => {
546
- outputStream = new MediaStream([processedTrack]);
547
- resolve(outputStream);
548
- })
549
- .catch((error) => {
550
- reject(error);
599
+ engine: FilterEngine.TF,
551
600
  });
552
601
 
553
- return;
554
- }
602
+ if (!tfLite) {
603
+ reject(new Error('TensorFlow Lite not loaded'));
604
+ return;
605
+ }
555
606
 
556
- if (engine === FilterEngine.TF) {
557
- if (!videoEl || !canvasEl || (backgroundImage && !bgImageEl)) {
558
- reject(new Error('Renderer started before elements are ready'));
559
- return;
560
- }
561
-
562
- videoEl.srcObject = ms;
563
- videoEl.play().then(
564
- () => {
565
- const trackSettings = track.getSettings();
566
- flushSync(() =>
567
- setVideoSize({
568
- width: trackSettings.width ?? 0,
569
- height: trackSettings.height ?? 0,
570
- }),
571
- );
572
- call?.tracer.trace('backgroundFilters.enable', {
607
+ renderer = createRenderer(
608
+ tfLite,
609
+ videoEl,
610
+ canvasEl,
611
+ {
573
612
  backgroundFilter,
574
613
  backgroundBlurLevel,
575
- backgroundImage,
576
- engine,
577
- });
578
-
579
- if (!tfLite) {
580
- reject(new Error('TensorFlow Lite not loaded'));
581
- return;
582
- }
583
-
584
- renderer = createRenderer(
585
- tfLite,
586
- videoEl,
587
- canvasEl,
588
- {
589
- backgroundFilter,
590
- backgroundBlurLevel,
591
- backgroundImage: bgImageEl ?? undefined,
592
- },
593
- onError,
594
- );
595
- outputStream = canvasEl.captureStream();
596
-
597
- resolve(outputStream);
598
- },
599
- () => {
600
- reject(new Error('Could not play the source video stream'));
601
- },
602
- );
603
- return;
604
- }
605
-
606
- reject(new Error('No supported engine available'));
614
+ backgroundImage: bgImageEl ?? undefined,
615
+ },
616
+ onError,
617
+ );
618
+ outputStream = canvasEl.captureStream();
619
+
620
+ resolve(outputStream);
621
+ },
622
+ () => {
623
+ reject(new Error('Could not play the source video stream'));
624
+ },
625
+ );
607
626
  });
608
627
 
609
628
  return {
610
629
  output,
611
630
  stop: () => {
612
631
  call?.tracer.trace('backgroundFilters.disable', null);
613
- processor?.stop();
614
632
  renderer?.dispose();
615
633
  if (videoRef.current) videoRef.current.srcObject = null;
616
634
  if (outputStream) disposeOfMediaStream(outputStream);
@@ -618,18 +636,36 @@ const useRenderer = (
618
636
  };
619
637
  },
620
638
  [
621
- backgroundBlurLevel,
622
- backgroundFilter,
623
- backgroundImage,
624
639
  call?.tracer,
625
640
  tfLite,
626
- engine,
627
- modelFilePath,
628
- basePath,
629
- segmentationOptions,
641
+ backgroundFilter,
642
+ backgroundBlurLevel,
643
+ backgroundImage,
630
644
  ],
631
645
  );
632
646
 
647
+ const start = engine === FilterEngine.TF ? startTf : startMediaPipe;
648
+
649
+ useEffect(() => {
650
+ if (!backgroundFilter) return;
651
+
652
+ processorRef.current
653
+ ?.updateOptions({
654
+ backgroundFilter,
655
+ backgroundBlurLevel,
656
+ backgroundImage,
657
+ segmentationOptions,
658
+ })
659
+ .catch((err) =>
660
+ console.warn(`[filters] Can't apply the background options`, err),
661
+ );
662
+ }, [
663
+ backgroundFilter,
664
+ backgroundBlurLevel,
665
+ backgroundImage,
666
+ segmentationOptions,
667
+ ]);
668
+
633
669
  const children = (
634
670
  <div className="str-video__background-filters">
635
671
  <video
@@ -5,3 +5,4 @@ export * from './useRequestPermission';
5
5
  export * from './useDeviceList';
6
6
  export * from './useModeration';
7
7
  export * from './useDragToScroll';
8
+ export * from './useLoopbackRecording';
@@ -0,0 +1,207 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ CallingState,
4
+ DEFAULT_LOOPBACK_RECORDING_DURATION_MS,
5
+ LOOPBACK_STREAMS_WAIT_TIMEOUT_MS,
6
+ MediaStreamRecorder,
7
+ clampLoopbackRecordingDuration,
8
+ getLoopbackStreams,
9
+ type MediaRecordingResult,
10
+ videoLoggerSystem,
11
+ waitForLoopbackStreams,
12
+ enableLoopbackAudio,
13
+ type LoopbackStreams,
14
+ } from '@stream-io/video-client';
15
+ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
16
+
17
+ /** Lifecycle phase of a loopback recording. */
18
+ export type LoopbackRecordingState = 'idle' | 'awaiting-streams' | 'recording';
19
+
20
+ const attachHiddenMediaSink = (stream: MediaStream): (() => void) => {
21
+ const logger = videoLoggerSystem.getLogger('useLoopbackRecording');
22
+ const element = document.createElement('video');
23
+ element.muted = true;
24
+ element.autoplay = true;
25
+ element.playsInline = true;
26
+ element.srcObject = stream;
27
+
28
+ element.play().catch((error) => {
29
+ logger.warn('sink element failed to play; recording may be empty', error);
30
+ });
31
+
32
+ return () => {
33
+ element.pause();
34
+ element.srcObject = null;
35
+ };
36
+ };
37
+
38
+ export type StartLoopbackRecordingOptions = {
39
+ /** Whether to include loopback video. Audio is always recorded. */
40
+ includeVideo?: boolean;
41
+ /** Maximum recording duration in milliseconds. */
42
+ maxDurationMs?: number;
43
+ };
44
+
45
+ export type UseLoopbackRecordingResult = {
46
+ /** Starts recording and resolves with the recorded blob. */
47
+ startRecording: (
48
+ options?: StartLoopbackRecordingOptions,
49
+ ) => Promise<MediaRecordingResult | null>;
50
+ /** Stops the current recording or stream wait. */
51
+ stopRecording: () => Promise<void>;
52
+ /** Lifecycle phase of the recording. */
53
+ recordingState: LoopbackRecordingState;
54
+ /** SFU-echoed video stream on the local participant. */
55
+ loopbackVideoStream?: MediaStream;
56
+ /** SFU-echoed audio stream on the local participant. */
57
+ loopbackAudioStream?: MediaStream;
58
+ };
59
+
60
+ /** Records SFU loopback streams for pre-call testing. */
61
+ export const useLoopbackRecording = (): UseLoopbackRecordingResult => {
62
+ const call = useCall();
63
+ const { useCallCallingState, useParticipantCount, useLocalParticipant } =
64
+ useCallStateHooks();
65
+ const callingState = useCallCallingState();
66
+ const participantCount = useParticipantCount();
67
+ const localParticipant = useLocalParticipant();
68
+
69
+ const [recordingState, setRecordingState] =
70
+ useState<LoopbackRecordingState>('idle');
71
+
72
+ const recordingStateRef = useRef<LoopbackRecordingState>('idle');
73
+ const isMountedRef = useRef(true);
74
+ const awaitAbortRef = useRef<AbortController | null>(null);
75
+ const stopRef = useRef<(() => void) | null>(null);
76
+ const runRef = useRef<Promise<unknown> | null>(null);
77
+
78
+ const loopbackStreams = useMemo<LoopbackStreams>(
79
+ () => getLoopbackStreams(call, localParticipant),
80
+ [call, localParticipant],
81
+ );
82
+
83
+ const updateState = useCallback((next: LoopbackRecordingState) => {
84
+ recordingStateRef.current = next;
85
+ if (isMountedRef.current) {
86
+ setRecordingState(next);
87
+ }
88
+ }, []);
89
+
90
+ const stopRecording = useCallback(async (): Promise<void> => {
91
+ const current = recordingStateRef.current;
92
+ if (current === 'idle') return;
93
+
94
+ if (current === 'awaiting-streams') {
95
+ videoLoggerSystem
96
+ .getLogger('useLoopbackRecording')
97
+ .debug('aborting the awaiting-streams wait');
98
+ awaitAbortRef.current?.abort();
99
+ return;
100
+ }
101
+
102
+ stopRef.current?.();
103
+ await runRef.current;
104
+ }, []);
105
+
106
+ const startRecording = useCallback(
107
+ async ({
108
+ includeVideo = true,
109
+ maxDurationMs = DEFAULT_LOOPBACK_RECORDING_DURATION_MS,
110
+ }: StartLoopbackRecordingOptions = {}): Promise<MediaRecordingResult | null> => {
111
+ const logger = videoLoggerSystem.getLogger('useLoopbackRecording');
112
+ if (!call) return null;
113
+
114
+ if (recordingStateRef.current !== 'idle') {
115
+ logger.warn('a recording is already running');
116
+ return null;
117
+ }
118
+
119
+ if (call.state.participantCount > 1) {
120
+ logger.warn('cannot start recording with other participants present');
121
+ return null;
122
+ }
123
+
124
+ const run = async (): Promise<MediaRecordingResult | null> => {
125
+ awaitAbortRef.current = new AbortController();
126
+ updateState('awaiting-streams');
127
+
128
+ try {
129
+ const tracks = await waitForLoopbackStreams(call, {
130
+ includeVideo,
131
+ signal: awaitAbortRef.current.signal,
132
+ timeoutMs: LOOPBACK_STREAMS_WAIT_TIMEOUT_MS,
133
+ });
134
+
135
+ if (tracks === null) {
136
+ logger.debug('loopback stream wait was aborted');
137
+ return null;
138
+ }
139
+
140
+ updateState('recording');
141
+
142
+ const restoreAudio = enableLoopbackAudio(tracks);
143
+ const stream = new MediaStream(
144
+ [tracks.audioTrack, tracks.videoTrack].filter(
145
+ (track): track is MediaStreamTrack => !!track,
146
+ ),
147
+ );
148
+ const detachSink = attachHiddenMediaSink(stream);
149
+ const recorder = new MediaStreamRecorder(stream, {
150
+ maxDurationMs: clampLoopbackRecordingDuration(maxDurationMs),
151
+ });
152
+ stopRef.current = recorder.stop;
153
+ try {
154
+ return await recorder.start();
155
+ } finally {
156
+ detachSink();
157
+ restoreAudio();
158
+ }
159
+ } catch (err) {
160
+ logger.error('loopback recording failed', err);
161
+ throw err;
162
+ } finally {
163
+ awaitAbortRef.current = null;
164
+ stopRef.current = null;
165
+ updateState('idle');
166
+ }
167
+ };
168
+
169
+ const promise = run();
170
+ runRef.current = promise.catch(() => undefined);
171
+ return promise;
172
+ },
173
+ [call, updateState],
174
+ );
175
+
176
+ useEffect(() => {
177
+ if (
178
+ callingState === CallingState.LEFT ||
179
+ callingState === CallingState.IDLE
180
+ ) {
181
+ stopRecording().catch(() => {});
182
+ }
183
+ }, [callingState, stopRecording]);
184
+
185
+ useEffect(() => {
186
+ if (recordingState !== 'idle' && participantCount > 1) {
187
+ stopRecording().catch(() => {});
188
+ }
189
+ }, [participantCount, recordingState, stopRecording]);
190
+
191
+ useEffect(() => {
192
+ isMountedRef.current = true;
193
+ return () => {
194
+ isMountedRef.current = false;
195
+ awaitAbortRef.current?.abort('the component unmounted');
196
+ stopRef.current?.();
197
+ };
198
+ }, []);
199
+
200
+ return {
201
+ startRecording,
202
+ stopRecording,
203
+ recordingState,
204
+ loopbackVideoStream: loopbackStreams.loopbackVideoStream,
205
+ loopbackAudioStream: loopbackStreams.loopbackAudioStream,
206
+ };
207
+ };