Skip to content

Comments

feat: advanced media quality settings UI and config-driven defaults#3736

Open
emmick4 wants to merge 7 commits intoelement-hq:livekitfrom
emmick4:livekit
Open

feat: advanced media quality settings UI and config-driven defaults#3736
emmick4 wants to merge 7 commits intoelement-hq:livekitfrom
emmick4:livekit

Conversation

@emmick4
Copy link

@emmick4 emmick4 commented Feb 11, 2026

Why

Element Call currently hardcodes video/audio parameters with no way for users to tune them. Server admins also have no mechanism to set org-wide defaults. Several community requests have asked for these controls.

What

This PR adds three new sections to the Settings modal:

  • Camera quality (Video tab): resolution, framerate, bitrate, and codec selection behind an "Advanced camera settings" toggle. Applied at room join.
  • Screen sharing (Video tab): same controls for screen share. Applied when toggling screen share mid-call.
  • Audio processing (Audio tab): individual checkboxes for echo cancellation, noise suppression, and automatic gain control.

All sliders now display their current value inline (e.g. "Framerate: 30 fps").

A new media_quality section in config.json lets admins set defaults for video codec, resolution, bitrate, framerate, and simulcast layers. These seed user settings only when the user hasn't explicitly set a preference in localStorage, so user choices always win.

The static defaultLiveKitOptions export is replaced by buildLiveKitOptions() / getLiveKitOptions() which read from config at runtime.

Testing strategy

  1. Check out the branch and run yarn && yarn build
  2. Run yarn test — 441 tests should pass
  3. Start the dev server with yarn dev:full
  4. Open Settings > Video tab:
    • Enable "Advanced camera settings", pick a non-default codec (e.g. VP9) and resolution (e.g. 1080p), then join a call
    • Open chrome://webrtc-internals, find the outbound video track, confirm the codec and resolution match your selection
    • Enable "Advanced screen share settings", start a screen share, confirm encoding in webrtc-internals
  5. Open Settings > Audio tab:
    • Uncheck "Noise suppression", rejoin the call
    • In webrtc-internals, check the audio track constraints
  6. Test config-driven defaults:
    • Add a media_quality block to config.json (e.g. {"video_codec": "vp9"})
    • Clear localStorage, reload — settings should reflect the config values
    • Change a setting in the UI, reload — the user override should persist

Note: happy to make any adjustments or split this PR up, haven't done much OSS contributing and just wanted to have these features on my own server. it also lays the scaffolding for more configurable audio and setting quality limits by the server admin
Screenshot 2026-02-11 125500

Add a `media_quality` section to config.json that allows self-hosters
to configure video codec, resolution, bitrate, framerate, and simulcast
layers for both camera and screen sharing.

This addresses the long-standing request in element-hq#249 for configurable media
quality settings. The LiveKit SDK already supports all of these options;
this change exposes them through the existing config system.

New config.json fields:
- media_quality.video_codec: preferred codec (vp8/vp9/h264/av1)
- media_quality.video: camera resolution, bitrate, framerate, simulcast layers
- media_quality.screen_share: screen share resolution, bitrate, framerate,
  simulcast layers (enables 3+ layer simulcast for screen sharing)

All fields are optional and fall back to the existing defaults (VP8,
720p camera, 1080p screen share) when not specified.

Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
Adds a "Screen sharing" section to Settings > Video with controls for:
- Resolution (576p to 4K)
- Framerate (5-60 fps slider)
- Bitrate (0.5-15 Mbps slider)
- Codec (VP8/VP9/H.264/AV1)

Gated behind an "Advanced screen share settings" toggle. When enabled,
settings are passed to LiveKit's setScreenShareEnabled as both capture
constraints and publish options. When disabled, falls back to
config.json media_quality defaults.

Settings are persisted in localStorage via the existing Setting<T>
system. The Slider component is extended with a tooltipFormatter prop
for custom tooltip display.

Inspired by pirosuki's advanced-screen-share-settings branch, but
reimplemented cleanly: settings are read directly in LocalMember.ts
(no signature changes), the existing Slider is extended (no component
duplication), and proper form components are used throughout.

Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
InputField only supports input/textarea, not select. Passing option
children caused React to crash rendering children inside a void input.

Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
…d defaults

- Add camera video quality controls (resolution/framerate/bitrate/codec)
  to Settings > Video, mirroring the screen share settings UI
- Add audio processing toggles (echo cancellation, noise suppression,
  auto gain control) to Settings > Audio, replacing URL-param-only controls
- Display raw values inline on all sliders (framerate, bitrate, volume)
- Add config-seeded defaults: config.json media_quality values now seed
  Setting defaults for users who haven't explicitly set preferences
- Camera settings are applied when joining a call via ConnectionFactory

Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
Allows Element Call to be served from a subdirectory (e.g. /widgets/element-call/
in Element Web) without breaking dynamic imports for locales, workers, and other
assets that were previously using absolute paths.

Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
@emmick4 emmick4 requested a review from a team as a code owner February 11, 2026 18:56
@emmick4 emmick4 requested a review from toger5 February 11, 2026 18:56
@CLAassistant
Copy link

CLAassistant commented Feb 11, 2026

CLA assistant check
All committers have signed the CLA.

@zone-zero
Copy link

This would be great. Element calls are almost completely useless due to incredibly poor quality.

@toger5
Copy link
Contributor

toger5 commented Feb 19, 2026

Thank you for the contribution!
We discussed this PR internally since Element Call is currently in a UX/design cleanup/overhaul project.
We wanted to make sure that this PR does not collide with the design changes.
We will comment on this within this week.

Copy link
Member

@robintown robintown left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, thanks for the thought you put into this change, it really shows! Having these options available for admins & power users is generally quite a welcome addition.

Unfortunately, you caught us while we're still wavering about the fate of Element Call's settings dialog - it might end up being merged with Element Web's existing voice+video settings tab, for instance. And also, while I would love to have advanced settings for frame rate and resolution controls regardless, the team sees the codec and bitrate settings in particular as "unknown territory" - encrypted H.264 simply didn't work yet with some mobile devices in @fkwp's testing, for instance. For the sake of our own ability to support users, we'd prefer to keep those settings rather hidden.

So, the compromise: We can accept this change today as long as the settings UI is safely tucked away in the developer settings tab. Hopefully that's not too out of the way for a motivated user while also communicating the experimental nature of the settings. I do hope we can make a proper home for frame rate and resolution eventually. 🙂

Comment on lines +88 to +92
/**
* Media quality settings for video and screen sharing.
* These override the hardcoded LiveKit defaults.
*/
media_quality?: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems all of these config options (with the exception of screen_share.simulcast_layers) have default values that they imply when unset. In that case, could you set the defaults by adding them to DEFAULT_CONFIG and ResolvedConfigOptions later in this file, rather than in buildPublishOptions?

I like that the ConfigOptions file alone has authority over what the defaults are, that way.

Comment on lines +151 to +161
/**
* Get LiveKit options, reading from the loaded Config singleton.
* Falls back to defaults if Config is not yet initialized.
*/
export function getLiveKitOptions(): RoomOptions {
try {
return buildLiveKitOptions(Config.get().media_quality);
} catch {
return buildLiveKitOptions();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have the app crash if we try to get LiveKit options before the config is available, otherwise we could easily break this feature without realizing. What's maybe not obvious is that the majority of the app already blocks on a loading screen until the config is loaded, so we can reasonably require it in ConnectionFactory for instance.

If that breaks some tests, mockConfig can help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR-Feature Release note category. A PR that introduces a new user facing feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants