Build / build_full_element_call (push) Canceled after 0s
Build / build_embedded_element_call (push) Canceled after 0s
Build / build_sdk_element_call (push) Canceled after 0s
Build / Build Storybook (push) Canceled after 0s
Build & publish embedded packages for releases / Versioning (push) Canceled after 0s
Test / Run unit tests (push) Canceled after 0s
Test / Run end-to-end tests (push) Canceled after 0s
Upload translation files to Localazy / upload (push) Canceled after 0s
GitHub Actions Security Analysis with zizmor 🌈 / Run zizmor 🌈 (push) Canceled after 0s
Build / deploy_develop (push) Canceled after 0s
Build / docker_for_develop (push) Canceled after 0s
Build & publish embedded packages for releases / build_element_call (push) Canceled after 0s
Build & publish embedded packages for releases / Publish tarball (push) Canceled after 0s
Build & publish embedded packages for releases / Publish NPM (push) Canceled after 0s
Build & publish embedded packages for releases / Publish Android AAR (push) Canceled after 0s
Build & publish embedded packages for releases / Publish SwiftPM Library (push) Canceled after 0s
Build & publish embedded packages for releases / Update release notes (push) Canceled after 0s
Build Element Call / Build Element Call (push) Canceled after 0s
Live-verified via matrix-rtc-sfu negotiation logs (2026-07-28): a real VP9 publish attempt got "falling back to alternative video codec" - enabledPublishCodecs was [VP8, H264, H265], no VP9/AV1. The client's backupCodec masked this (silent regression to VP8, no visible failure), but offering VP9/AV1 in the dropdown was misleading - selecting them has no real effect right now. Trimmed the dropdown to VP8/H264/H265 (added h265 as a real option - livekit-client's own VideoCodec type already supports it). Also fixed screenShareCodec's default, which was "vp9" (meaning every user who enabled the toggle without touching the codec dropdown was silently requesting an unsupported codec) - now "vp8" to match camera's default.
146 lines
4.9 KiB
TypeScript
146 lines
4.9 KiB
TypeScript
/*
|
|
Copyright 2022-2024 New Vector Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
import { type FC, type ReactNode, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import { FieldRow, InputField } from "../input/Input";
|
|
import { type Setting, useSetting, type VideoCodec } from "./settings";
|
|
import settingsStyles from "./SettingsModal.module.css";
|
|
import { Slider } from "../Slider";
|
|
|
|
export const MediaQualitySettings: FC<{
|
|
id: string;
|
|
header: string;
|
|
toggleLabel: string;
|
|
description: string;
|
|
toggleSetting: Setting<boolean>;
|
|
resolutionSetting: Setting<string>;
|
|
framerateSetting: Setting<number>;
|
|
bitrateSetting: Setting<number>;
|
|
codecSetting: Setting<VideoCodec>;
|
|
resolutionOptions: { value: string; label: string }[];
|
|
bitrateRange: { min: number; max: number; step: number };
|
|
}> = ({
|
|
id,
|
|
header,
|
|
toggleLabel,
|
|
description,
|
|
toggleSetting,
|
|
resolutionSetting,
|
|
framerateSetting,
|
|
bitrateSetting,
|
|
codecSetting,
|
|
resolutionOptions,
|
|
bitrateRange,
|
|
}): ReactNode => {
|
|
const { t } = useTranslation();
|
|
const [advancedEnabled, setAdvancedEnabled] = useSetting(toggleSetting);
|
|
const [resolution, setResolution] = useSetting(resolutionSetting);
|
|
const [framerate, setFramerate] = useSetting(framerateSetting);
|
|
const [framerateRaw, setFramerateRaw] = useState(framerate);
|
|
const [bitrate, setBitrate] = useSetting(bitrateSetting);
|
|
const [bitrateRaw, setBitrateRaw] = useState(bitrate);
|
|
const [codec, setCodec] = useSetting(codecSetting);
|
|
|
|
return (
|
|
<>
|
|
<h4>{header}</h4>
|
|
<FieldRow>
|
|
<InputField
|
|
id={`${id}Toggle`}
|
|
label={toggleLabel}
|
|
description={description}
|
|
type="checkbox"
|
|
checked={advancedEnabled}
|
|
onChange={(e): void => setAdvancedEnabled(e.target.checked)}
|
|
/>
|
|
</FieldRow>
|
|
{advancedEnabled && (
|
|
<>
|
|
<div className={settingsStyles.volumeSlider}>
|
|
<label htmlFor={`${id}Resolution`}>
|
|
{t("settings.resolution_label", "Resolution")}
|
|
</label>
|
|
<select
|
|
id={`${id}Resolution`}
|
|
value={resolution}
|
|
onChange={(e): void => setResolution(e.target.value)}
|
|
>
|
|
{resolutionOptions.map((opt) => (
|
|
<option key={opt.value} value={opt.value}>
|
|
{opt.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div className={settingsStyles.volumeSlider}>
|
|
<label>
|
|
{t("settings.framerate_label", "Framerate")}
|
|
{": "}
|
|
<span className={settingsStyles.settingValue}>
|
|
{framerateRaw} fps
|
|
</span>
|
|
</label>
|
|
<Slider
|
|
label={t("settings.framerate_label", "Framerate")}
|
|
value={framerateRaw}
|
|
onValueChange={setFramerateRaw}
|
|
onValueCommit={setFramerate}
|
|
min={5}
|
|
max={60}
|
|
step={5}
|
|
tooltipFormatter={(v): string => `${v} fps`}
|
|
/>
|
|
</div>
|
|
<div className={settingsStyles.volumeSlider}>
|
|
<label>
|
|
{t("settings.bitrate_label", "Bitrate")}
|
|
{": "}
|
|
<span className={settingsStyles.settingValue}>
|
|
{(bitrateRaw / 1_000_000).toFixed(1)} Mbps
|
|
</span>
|
|
</label>
|
|
<Slider
|
|
label={t("settings.bitrate_label", "Bitrate")}
|
|
value={bitrateRaw}
|
|
onValueChange={setBitrateRaw}
|
|
onValueCommit={setBitrate}
|
|
min={bitrateRange.min}
|
|
max={bitrateRange.max}
|
|
step={bitrateRange.step}
|
|
tooltipFormatter={(v): string =>
|
|
`${(v / 1_000_000).toFixed(1)} Mbps`
|
|
}
|
|
/>
|
|
</div>
|
|
<div className={settingsStyles.volumeSlider}>
|
|
<label htmlFor={`${id}Codec`}>
|
|
{t("settings.codec_label", "Codec")}
|
|
</label>
|
|
{/* Only list codecs the SFU actually accepts right now (confirmed
|
|
live via matrix-rtc-sfu negotiation logs, 2026-07-28 -
|
|
enabledPublishCodecs was [VP8, H264, H265]). VP9/AV1 are not
|
|
currently negotiated by the SFU - selecting them silently
|
|
falls back to VP8 via the client's backupCodec, so leaving
|
|
them out avoids offering a choice that has no real effect. */}
|
|
<select
|
|
id={`${id}Codec`}
|
|
value={codec}
|
|
onChange={(e): void => setCodec(e.target.value as VideoCodec)}
|
|
>
|
|
<option value="vp8">VP8</option>
|
|
<option value="h264">H.264</option>
|
|
<option value="h265">H.265</option>
|
|
</select>
|
|
</div>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
};
|