From 8b4a2f39e0b18d64ea8132ea8990995a9aeb6fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 30 Jul 2024 10:20:18 -0300 Subject: [PATCH] fix(webcam): turn useGetStats hook into a service method to avoid function re-instantiation --- .../connection-status/component.jsx | 5 +- .../connection-status/modal/container.jsx | 4 -- .../components/connection-status/service.js | 15 +++--- .../components/video-provider/container.tsx | 6 +-- .../components/video-provider/hooks/index.ts | 52 ------------------- .../ui/components/video-provider/service.ts | 45 +++++++++++++++- 6 files changed, 56 insertions(+), 71 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/component.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/component.jsx index 9e9be29fbe..7f89298201 100755 --- a/bigbluebutton-html5/imports/ui/components/connection-status/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/connection-status/component.jsx @@ -3,7 +3,6 @@ import { useMutation } from '@apollo/client'; import { UPDATE_CONNECTION_ALIVE_AT } from './mutations'; import { getStatus, handleAudioStatsEvent, startMonitoringNetwork } from '/imports/ui/components/connection-status/service'; import connectionStatus from '../../core/graphql/singletons/connectionStatus'; -import { useGetStats } from '../video-provider/hooks'; import getBaseUrl from '/imports/ui/core/utils/getBaseUrl'; @@ -14,8 +13,6 @@ const ConnectionStatus = () => { const [updateConnectionAliveAtM] = useMutation(UPDATE_CONNECTION_ALIVE_AT); - const getVideoStreamsStats = useGetStats(); - const handleUpdateConnectionAliveAt = () => { const startTime = performance.now(); fetch( @@ -66,7 +63,7 @@ const ConnectionStatus = () => { if (STATS_ENABLED) { window.addEventListener('audiostats', handleAudioStatsEvent); - startMonitoringNetwork(getVideoStreamsStats); + startMonitoringNetwork(); } return () => { diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/modal/container.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/modal/container.jsx index 954d1b0d56..d6f0a08b28 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/modal/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/connection-status/modal/container.jsx @@ -3,7 +3,6 @@ import { CONNECTION_STATUS_REPORT_SUBSCRIPTION } from '../queries'; import Service from '../service'; import Component from './component'; import useCurrentUser from '/imports/ui/core/hooks/useCurrentUser'; -import { useGetStats } from '../../video-provider/hooks'; import useDeduplicatedSubscription from '/imports/ui/core/hooks/useDeduplicatedSubscription'; import { useReactiveVar } from '@apollo/client'; import connectionStatus from '/imports/ui/core/graphql/singletons/connectionStatus'; @@ -16,14 +15,11 @@ const ConnectionStatusContainer = (props) => { const newtworkData = useReactiveVar(connectionStatus.getNetworkDataVar()); - const getVideoStreamsStats = useGetStats(); - return ( ); diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/service.js b/bigbluebutton-html5/imports/ui/components/connection-status/service.js index ceaf3517b5..0b7cd89c88 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/service.js +++ b/bigbluebutton-html5/imports/ui/components/connection-status/service.js @@ -5,6 +5,7 @@ import Session from '/imports/ui/services/storage/in-memory'; import { notify } from '/imports/ui/services/notification'; import AudioService from '/imports/ui/components/audio/service'; import ScreenshareService from '/imports/ui/components/screenshare/service'; +import VideoService from '/imports/ui/components/video-provider/service'; import connectionStatus from '../../core/graphql/singletons/connectionStatus'; const intlMessages = defineMessages({ @@ -210,8 +211,8 @@ const getAudioData = async () => { * @returns An Object containing video data for all video peers and screenshare * peer */ -const getVideoData = async (getVideoStreamsStats) => { - const camerasData = await getVideoStreamsStats() || {}; +const getVideoData = async () => { + const camerasData = await VideoService.getStats() || {}; const screenshareData = await ScreenshareService.getStats() || {}; @@ -226,10 +227,10 @@ const getVideoData = async (getVideoStreamsStats) => { * For audio, this will get information about the mic/listen-only stream. * @returns An Object containing all this data. */ -const getNetworkData = async (getVideoStreamsStats) => { +const getNetworkData = async () => { const audio = await getAudioData(); - const video = await getVideoData(getVideoStreamsStats); + const video = await getVideoData(); const user = { time: new Date(), @@ -401,11 +402,11 @@ export function getStatus(levels, value) { * Start monitoring the network data. * @return {Promise} A Promise that resolves when process started. */ -export async function startMonitoringNetwork(getVideoStreamsStats) { - let previousData = await getNetworkData(getVideoStreamsStats); +export async function startMonitoringNetwork() { + let previousData = await getNetworkData(); setInterval(async () => { - const data = await getNetworkData(getVideoStreamsStats); + const data = await getNetworkData(); const { outbound: audioCurrentUploadRate, diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/container.tsx b/bigbluebutton-html5/imports/ui/components/video-provider/container.tsx index ffae9e6ece..9a879fe4c4 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/container.tsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/container.tsx @@ -20,7 +20,6 @@ import VideoService from './service'; import { Output } from '/imports/ui/components/layout/layoutTypes'; import { VideoItem } from './types'; import { debounce } from '/imports/utils/debounce'; -import WebRtcPeer from '/imports/ui/services/webrtc-base/peer'; import useSettings from '/imports/ui/services/settings/hooks/useSettings'; import { SETTINGS } from '/imports/ui/services/settings/enums'; import { useStorageKey } from '/imports/ui/services/storage/hooks'; @@ -64,7 +63,7 @@ const VideoProviderContainer: React.FC = (props) => VideoService.applyCameraProfile, CAMERA_QUALITY_THR_DEBOUNCE, { leading: false, trailing: true }, - ); + ) as typeof VideoService.applyCameraProfile; const { data: currentMeeting } = useMeeting((m) => ({ usersPolicies: m.usersPolicies, @@ -89,6 +88,7 @@ const VideoProviderContainer: React.FC = (props) => totalNumberOfStreams, totalNumberOfOtherStreams, } = useVideoStreams(); + VideoService.updateActivePeers(streams); let usersVideo: VideoItem[] = streams; @@ -167,7 +167,7 @@ const VideoProviderContainer: React.FC = (props) => exitVideo={exitVideo} lockUser={lockUser} stopVideo={stopVideo} - applyCameraProfile={applyCameraProfile as (peer: WebRtcPeer, profileId: string) => void} + applyCameraProfile={applyCameraProfile} myRole={myRole} /> ); diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/hooks/index.ts b/bigbluebutton-html5/imports/ui/components/video-provider/hooks/index.ts index 841124e31b..0069503194 100644 --- a/bigbluebutton-html5/imports/ui/components/video-provider/hooks/index.ts +++ b/bigbluebutton-html5/imports/ui/components/video-provider/hooks/index.ts @@ -52,11 +52,6 @@ import ConnectionStatus from '/imports/ui/core/graphql/singletons/connectionStat import { VIDEO_TYPES } from '/imports/ui/components/video-provider/enums'; import createUseSubscription from '/imports/ui/core/hooks/createUseSubscription'; -const FILTER_VIDEO_STATS = [ - 'outbound-rtp', - 'inbound-rtp', -]; - const useVideoStreamsSubscription = createUseSubscription( VIDEO_STREAMS_SUBSCRIPTION, {}, @@ -532,53 +527,6 @@ export const useStopVideo = () => { }, [cameraBroadcastStop]); }; -export const useActivePeers = () => { - const videoData = useVideoStreams(); - - if (!videoData) return null; - - const { streams: activeVideoStreams } = videoData; - - if (!activeVideoStreams) return null; - - const activePeers: Record = {}; - - activeVideoStreams.forEach((stream) => { - if (videoService.getWebRtcPeersRef()[stream.stream]) { - activePeers[stream.stream] = videoService.getWebRtcPeersRef()[stream.stream].peerConnection; - } - }); - - return activePeers; -}; - -export const useGetStats = () => { - const peers = useActivePeers(); - - return useCallback(async () => { - if (!peers) return null; - - const stats: Record = {}; - - await Promise.all( - Object.keys(peers).map(async (peerId) => { - const peerStats = await peers[peerId].getStats(); - - const videoStats: Record = {}; - - peerStats.forEach((stat) => { - if (FILTER_VIDEO_STATS.includes(stat.type)) { - videoStats[stat.type] = stat; - } - }); - stats[peerId] = videoStats; - }), - ); - - return stats; - }, [peers]); -}; - export const useShouldRenderPaginationToggle = () => { const myPageSize = useMyPageSize(); const { diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/service.ts b/bigbluebutton-html5/imports/ui/components/video-provider/service.ts index b629cd114f..fa6b9fe38e 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/service.ts +++ b/bigbluebutton-html5/imports/ui/components/video-provider/service.ts @@ -18,11 +18,16 @@ import WebRtcPeer from '/imports/ui/services/webrtc-base/peer'; import { Constraints2 } from '/imports/ui/Types/meetingClientSettings'; import MediaStreamUtils from '/imports/utils/media-stream-utils'; import Session from '/imports/ui/services/storage/in-memory'; -import type { Stream } from './types'; +import type { Stream, StreamItem } from './types'; import { VIDEO_TYPES } from './enums'; const TOKEN = '_'; +const FILTER_VIDEO_STATS = [ + 'outbound-rtp', + 'inbound-rtp', +]; + class VideoService { public isMobile: boolean; @@ -40,6 +45,8 @@ class VideoService { private deviceId: string | null = null; + private activePeers: Record; + private readonly clientSessionUUID: string; constructor() { @@ -60,6 +67,7 @@ class VideoService { } this.webRtcPeersRef = {}; + this.activePeers = {}; } static fetchNumberOfDevices(devices: MediaDeviceInfo[]) { @@ -474,6 +482,39 @@ class VideoService { getPrefix() { return `${Auth.userID}${TOKEN}${this.clientSessionUUID}`; } + + updateActivePeers(streams: StreamItem[]) { + const activePeers: Record = {}; + + streams.forEach((vs) => { + if (this.webRtcPeersRef[vs.stream]) { + activePeers[vs.stream] = this.webRtcPeersRef[vs.stream].peerConnection; + } + }); + + this.activePeers = activePeers; + } + + async getStats() { + const stats: Record = {}; + + await Promise.all( + Object.keys(this.activePeers).map(async (peerId) => { + const peerStats = await this.activePeers[peerId].getStats(); + + const videoStats: Record = {}; + + peerStats.forEach((stat) => { + if (FILTER_VIDEO_STATS.includes(stat.type)) { + videoStats[stat.type] = stat; + } + }); + stats[peerId] = videoStats; + }), + ); + + return stats; + } } const videoService = new VideoService(); @@ -516,4 +557,6 @@ export default { getRoleViewer: VideoService.getRoleViewer, getPrefix: videoService.getPrefix.bind(videoService), isPinEnabled: VideoService.isPinEnabled, + updateActivePeers: (streams: StreamItem[]) => videoService.updateActivePeers(streams), + getStats: () => videoService.getStats(), };