✨(frontend) add video resolution selector for publishing control
Introduce select option allowing users to set maximum publishing resolution that instantly changes video track resolution for other participants. Essential for low bandwidth networks and follows common patterns across major videoconferencing solutions. Users can optimize their video quality based on network conditions without leaving the call.
This commit is contained in:
committed by
aleb_the_flash
parent
fd90d0b830
commit
803c94a80c
@@ -7,7 +7,14 @@ import { HStack } from '@/styled-system/jsx'
|
||||
import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices'
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { createLocalVideoTrack, LocalVideoTrack } from 'livekit-client'
|
||||
import {
|
||||
createLocalVideoTrack,
|
||||
LocalVideoTrack,
|
||||
Track,
|
||||
VideoPresets,
|
||||
} from 'livekit-client'
|
||||
import { BackgroundProcessorFactory } from '@/features/rooms/livekit/components/blur'
|
||||
import { VideoResolution } from '@/stores/userChoices'
|
||||
|
||||
type RowWrapperProps = {
|
||||
heading: string
|
||||
@@ -57,8 +64,9 @@ export const VideoTab = ({ id }: VideoTabProps) => {
|
||||
const { localParticipant } = useRoomContext()
|
||||
|
||||
const {
|
||||
userChoices: { videoDeviceId },
|
||||
userChoices: { videoDeviceId, processorSerialized, videoPublishResolution },
|
||||
saveVideoInputDeviceId,
|
||||
saveVideoPublishResolution,
|
||||
} = usePersistentUserChoices()
|
||||
const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(
|
||||
null
|
||||
@@ -88,6 +96,22 @@ export const VideoTab = ({ id }: VideoTabProps) => {
|
||||
isDisabled: true,
|
||||
}
|
||||
|
||||
const handleVideoResolutionChange = async (key: 'h720' | 'h360' | 'h180') => {
|
||||
const videoPublication = localParticipant.getTrackPublication(
|
||||
Track.Source.Camera
|
||||
)
|
||||
const videoTrack = videoPublication?.track
|
||||
if (videoTrack) {
|
||||
saveVideoPublishResolution(key)
|
||||
await videoTrack.restartTrack({
|
||||
resolution: VideoPresets[key].resolution,
|
||||
deviceId: { exact: videoDeviceId },
|
||||
processor:
|
||||
BackgroundProcessorFactory.deserializeProcessor(processorSerialized),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let videoTrack: LocalVideoTrack | null = null
|
||||
|
||||
@@ -165,6 +189,54 @@ export const VideoTab = ({ id }: VideoTabProps) => {
|
||||
)}
|
||||
</div>
|
||||
</RowWrapper>
|
||||
<H lvl={2}>{t('video.resolution.heading')}</H>
|
||||
<HStack
|
||||
gap={0}
|
||||
style={{
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: '1 1 215px',
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
type="select"
|
||||
label={t('video.resolution.publish.label')}
|
||||
items={[
|
||||
{
|
||||
value: 'h720',
|
||||
label: `${t('video.resolution.publish.items.high')} (720p)`,
|
||||
},
|
||||
{
|
||||
value: 'h360',
|
||||
label: `${t('video.resolution.publish.items.medium')} (360p)`,
|
||||
},
|
||||
{
|
||||
value: 'h180',
|
||||
label: `${t('video.resolution.publish.items.low')} (180p)`,
|
||||
},
|
||||
]}
|
||||
selectedKey={videoPublishResolution}
|
||||
onSelectionChange={async (key) => {
|
||||
await handleVideoResolutionChange(key as VideoResolution)
|
||||
}}
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '10rem',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
paddingLeft: '1.5rem',
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</TabPanel>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
"disabled": "Videovorschau deaktiviert"
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"heading": "Auflösung",
|
||||
"publish": {
|
||||
"label": "Wählen Sie Ihre maximale Sendeauflösung",
|
||||
"items": {
|
||||
"high": "Hohe Auflösung",
|
||||
"medium": "Standardauflösung",
|
||||
"low": "Niedrige Auflösung"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsRequired": "Berechtigungen erforderlich"
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
"disabled": "Video preview disabled"
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"heading": "Resolution",
|
||||
"publish": {
|
||||
"label": "Select your sending resolution (maximum)",
|
||||
"items": {
|
||||
"high": "High definition",
|
||||
"medium": "Standard definition",
|
||||
"low": "Low definition"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsRequired": "Permissions required"
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
"disabled": "Aperçu vidéo désactivé"
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"heading": "Résolution",
|
||||
"publish": {
|
||||
"label": "Sélectionner votre résolution d'envoi (maximum)",
|
||||
"items": {
|
||||
"high": "Haute définition",
|
||||
"medium": "Définition standard",
|
||||
"low": "Basse définition"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsRequired": "Autorisations nécessaires"
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
"disabled": "Videovoorbeeld uitgeschakeld"
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"heading": "Resolutie",
|
||||
"publish": {
|
||||
"label": "Selecteer uw verzendresolutie (maximum)",
|
||||
"items": {
|
||||
"high": "Hoge definitie",
|
||||
"medium": "Standaarddefinitie",
|
||||
"low": "Lage definitie"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsRequired": "Machtigingen vereist"
|
||||
},
|
||||
"notifications": {
|
||||
|
||||
Reference in New Issue
Block a user