Description
My team is creating an app that has a couple of different views:
- Default View (has pagination for the 8 smaller videos)
- 1 main video: 720p
- 8 smaller videos: 180p
- Gallery View (has full pagination)
- 9 videos: 360p
During some testing, some users reported in the Default View that the the main video quality would be inconsistent. For some, the quality of the main video would be clear and then drop immediately, and it would only be fixed after switching between views a few times or by paginating through the smaller videos. Some also reported the main video quality would start poor and then clear up by itself after a little bit by itself. There were also some that reported no issues with video quality.
We’re not sure how to actually diagnose this because we’re not seeing any real warnings or errors about downgrading the quality when inspecting the console from the Zoom SDK.
The component uses a single video-player-container and maps the visible participants and returns video-player with assigned refs. When a user switches views, we update the CSS we’re using for the overall container. When we attach, we are setting the desired video quality. We are also updating them as the user lists update when needed.
Which Web Video SDK version?
At the time of testing, 2.2.12. We have updated to 2.3.0 since then.
Code Snippets
This is some code from our current implementation. The first code block is from our Zoom context and the second code block is from the component where we’re using the video-player-container and video-player elements.
const videoPlayerListRef = useRef<Record<string, VideoPlayer | undefined>>({})
const getVideoPlayerElement = (userId: number) => {
return videoPlayerListRef.current[userId.toString()]
}
const handleVideoPlayer = useCallback(
async ({
action,
userId,
quality = VideoQuality.Video_360P,
}: {
action: 'Start' | 'Stop'
userId: number
quality?: VideoQuality
}) => {
if (connectedWithStream) {
if (action === 'Start') {
try {
await mediaStream.attachVideo(
userId,
quality,
getVideoPlayerElement(userId)
)
} catch (error) {
Logger.error('Error attaching video', {
userId,
error,
})
}
} else {
try {
await mediaStream.detachVideo(userId)
} catch (error) {
Logger.error('Error detaching video', {
userId,
error,
})
}
}
}
},
[connectedWithStream, mediaStream]
)
useEffect(() => {
zoomClient.on(
'peer-video-state-change',
(payload: Parameters<typeof event_peer_video_state_change>[0]) => {
void handleVideoPlayer(payload)
}
)
return () => {
zoomClient.off(
'peer-video-state-change',
(payload: Parameters<typeof event_peer_video_state_change>[0]) => {
void handleVideoPlayer(payload)
}
)
}
}, [handleVideoPlayer, zoomClient])
useEffect(() => {
const startVideoPlayer = (participant: Participant) => {
const attachment = getVideoPlayerElement(participant.userId)
if (!attachment) return
const quality = isDefaultView
? participant.displayName === selectedSessionView?.device_name
? VideoQuality.Video_720P
: VideoQuality.Video_180P
: VideoQuality.Video_360P
void handleVideoPlayer({
action: 'Start',
userId: participant.userId,
quality,
})
}
const previousParticipants = previousVisibleParticipants ?? []
const removedParticipants = previousParticipants.filter(
(previousParticipant) =>
!visibleParticipants.some(
(visibleParticipant) =>
visibleParticipant.userId === previousParticipant.userId
)
)
removedParticipants.forEach((participant) => {
void handleVideoPlayer({ action: 'Stop', userId: participant.userId })
})
const addedParticipants = visibleParticipants.filter(
(visibleParticipant) =>
!previousParticipants.some(
(previousParticipant) =>
previousParticipant.userId === visibleParticipant.userId
)
)
const existingParticipants = visibleParticipants.filter(
(visibleParticipant) =>
!addedParticipants.some(
(addedParticipant) =>
addedParticipant.userId === visibleParticipant.userId
)
)
existingParticipants.forEach(startVideoPlayer)
addedParticipants.forEach(startVideoPlayer)
}, [
visibleParticipants,
previousVisibleParticipants,
handleVideoPlayer,
getVideoPlayerElement,
isDefaultView,
selectedSessionView?.device_name,
])