Skip to content

Commit 72e7e0f

Browse files
authored
Merge pull request #3736 from emmick4/livekit
feat: advanced media quality settings UI and config-driven defaults
2 parents 94a1d87 + cbc12b0 commit 72e7e0f

20 files changed

Lines changed: 1571 additions & 89 deletions

locales/en/app.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,23 @@
196196
"room_auth_view_ssla_caption": "By clicking \"Join call now\", you agree to our <2>Software and Services License Agreement (SSLA)</2>",
197197
"screenshare_button_label": "Share screen",
198198
"settings": {
199+
"advanced_camera_description": "Configure resolution, framerate, bitrate, and codec for camera video. Changes apply on next call join.",
200+
"advanced_camera_label": "Advanced camera settings",
201+
"advanced_screen_share_description": "Configure resolution, framerate, bitrate, and codec for screen sharing",
202+
"advanced_screen_share_label": "Advanced screen share settings",
203+
"audio_processing_description": "Changes apply on next call join.",
204+
"audio_processing_header": "Audio processing",
199205
"audio_tab": {
200206
"effect_volume_description": "Adjust the volume at which reactions and hand raised effects play.",
201207
"effect_volume_label": "Sound effect volume"
202208
},
209+
"auto_gain_control_label": "Automatic gain control",
203210
"background_blur_header": "Background",
204211
"background_blur_label": "Blur the background of the video",
212+
"bitrate_label": "Bitrate",
205213
"blur_not_supported_by_browser": "(Background blur is not supported by this device.)",
214+
"camera_header": "Camera quality",
215+
"codec_label": "Codec",
206216
"developer_tab_title": "Developer",
207217
"devices": {
208218
"activating": "Activating…",
@@ -218,12 +228,15 @@
218228
"speaker": "Speaker",
219229
"speaker_numbered": "Speaker {{n}}"
220230
},
231+
"echo_cancellation_label": "Echo cancellation",
221232
"feedback_tab_body": "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.",
222233
"feedback_tab_description_label": "Your feedback",
223234
"feedback_tab_h4": "Submit feedback",
224235
"feedback_tab_send_logs_label": "Include debug logs",
225236
"feedback_tab_thank_you": "Thanks, we received your feedback!",
226237
"feedback_tab_title": "Feedback",
238+
"framerate_label": "Framerate",
239+
"noise_suppression_label": "Noise suppression",
227240
"opt_in_description": "<0></0><1></1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.",
228241
"preferences_tab": {
229242
"developer_mode_label": "Developer mode",
@@ -235,7 +248,9 @@
235248
"reactions_show_label": "Show reactions",
236249
"show_hand_raised_timer_description": "Show a timer when a participant raises their hand",
237250
"show_hand_raised_timer_label": "Show hand raise duration"
238-
}
251+
},
252+
"resolution_label": "Resolution",
253+
"screen_share_header": "Screen sharing"
239254
},
240255
"star_rating_input_label_one": "{{count}} star",
241256
"star_rating_input_label_other": "{{count}} stars",

src/Slider.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ interface Props {
3131
max: number;
3232
step: number;
3333
disabled?: boolean;
34+
/**
35+
* Custom formatter for the tooltip label. If not provided, the value is
36+
* displayed as a percentage.
37+
*/
38+
tooltipFormatter?: (value: number) => string;
3439
}
3540

3641
/**
@@ -46,6 +51,7 @@ export const Slider: FC<Props> = ({
4651
max,
4752
step,
4853
disabled,
54+
tooltipFormatter,
4955
}) => {
5056
const onValueChange = useCallback(
5157
([v]: number[]) => onValueChangeProp(v),
@@ -71,7 +77,14 @@ export const Slider: FC<Props> = ({
7177
<Range className={styles.highlight} />
7278
</Track>
7379
{/* Note: This is expected not to be visible on mobile.*/}
74-
<Tooltip placement="top" label={Math.round(value * 100).toString() + "%"}>
80+
<Tooltip
81+
placement="top"
82+
label={
83+
tooltipFormatter
84+
? tooltipFormatter(value)
85+
: Math.round(value * 100).toString() + "%"
86+
}
87+
>
7588
<Thumb className={styles.handle} aria-label={label} />
7689
</Tooltip>
7790
</Root>

src/config/ConfigOptions.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,61 @@ export interface ConfigOptions {
105105
*/
106106
ssla?: string;
107107

108+
/**
109+
* Media quality settings for video and screen sharing.
110+
* These override the hardcoded LiveKit defaults.
111+
*/
112+
media_quality?: {
113+
/**
114+
* Video codec preference. The server must also have the codec enabled.
115+
* @default "vp8"
116+
*/
117+
video_codec?: "vp8" | "vp9" | "h264" | "av1";
118+
119+
/**
120+
* Camera video settings.
121+
*/
122+
video?: {
123+
/** Max resolution height in pixels (e.g. 720, 1080, 1440). @default 720 */
124+
max_resolution?: number;
125+
/** Max bitrate in bits per second. @default 1700000 */
126+
max_bitrate?: number;
127+
/** Max framerate. @default 30 */
128+
max_framerate?: number;
129+
/**
130+
* Simulcast layers as an array of {height, bitrate} objects,
131+
* ordered from lowest to highest quality.
132+
* @default [{height: 180, bitrate: 160000}, {height: 360, bitrate: 450000}]
133+
*/
134+
simulcast_layers?: Array<{
135+
height: number;
136+
bitrate: number;
137+
}>;
138+
};
139+
140+
/**
141+
* Screen share settings.
142+
*/
143+
screen_share?: {
144+
/** Max resolution height in pixels. @default 1080 */
145+
max_resolution?: number;
146+
/** Max bitrate in bits per second. @default 5000000 */
147+
max_bitrate?: number;
148+
/** Max framerate. @default 30 */
149+
max_framerate?: number;
150+
/**
151+
* Simulcast layers for screen sharing as an array of {height, bitrate, framerate} objects,
152+
* ordered from lowest to highest quality. If omitted, LiveKit SDK defaults apply (1 extra
153+
* layer at half resolution).
154+
*/
155+
simulcast_layers?: Array<{
156+
height: number;
157+
bitrate: number;
158+
framerate?: number;
159+
}>;
160+
};
161+
};
162+
108163
media_devices?: {
109164
/**
110165
* Defines whether participants should start with audio enabled by default.
@@ -185,6 +240,24 @@ export interface ConfigOptions {
185240
export interface ResolvedConfigOptions extends ConfigOptions {
186241
sync_disconnect_grace_period_ms: number;
187242
ssla: string;
243+
media_quality: Required<
244+
Pick<NonNullable<ConfigOptions["media_quality"]>, "video_codec">
245+
> & {
246+
video: Required<
247+
Pick<
248+
NonNullable<NonNullable<ConfigOptions["media_quality"]>["video"]>,
249+
"max_resolution" | "max_bitrate" | "max_framerate"
250+
>
251+
>;
252+
screen_share: Required<
253+
Pick<
254+
NonNullable<
255+
NonNullable<ConfigOptions["media_quality"]>["screen_share"]
256+
>,
257+
"max_resolution" | "max_bitrate" | "max_framerate"
258+
>
259+
>;
260+
};
188261
matrix_rtc_session: {
189262
wait_for_key_rotation_ms?: number;
190263
delayed_leave_event_delay_ms: number;
@@ -201,6 +274,19 @@ export const DEFAULT_CONFIG: ResolvedConfigOptions = {
201274
},
202275
sync_disconnect_grace_period_ms: 10000,
203276
ssla: "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
277+
media_quality: {
278+
video_codec: "vp8",
279+
video: {
280+
max_resolution: 720,
281+
max_bitrate: 1_700_000,
282+
max_framerate: 30,
283+
},
284+
screen_share: {
285+
max_resolution: 1080,
286+
max_bitrate: 5_000_000,
287+
max_framerate: 30,
288+
},
289+
},
204290
matrix_rtc_session: {
205291
delayed_leave_event_delay_ms: 10000,
206292
network_error_retry_ms: 1000,

src/initializer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030

3131
import { getUrlParams } from "./UrlParams";
3232
import { Config } from "./config/Config";
33+
import { seedSettingsFromConfig } from "./settings/settings";
3334
import { platform } from "./Platform";
3435
import { isFailure } from "./utils/fetch";
3536
import { initializeWidget } from "./widget";
@@ -237,6 +238,7 @@ export class Initializer {
237238
this.loadStates.config = LoadState.Loading;
238239
Config.init().then(
239240
() => {
241+
seedSettingsFromConfig(Config.get().media_quality);
240242
this.loadStates.config = LoadState.Loaded;
241243
this.initStep(resolve);
242244
},

0 commit comments

Comments
 (0)