mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
Clean up: deduplicate settings UI, fix backupCodec, remove dead export
Signed-off-by: Ryan Emmick <ryanemmick4@gmail.com>
This commit is contained in:
@@ -108,7 +108,10 @@ function buildPublishOptions(
|
|||||||
stopMicTrackOnMute: false,
|
stopMicTrackOnMute: false,
|
||||||
videoCodec: codec,
|
videoCodec: codec,
|
||||||
videoEncoding,
|
videoEncoding,
|
||||||
backupCodec: { codec: "vp8", encoding: videoEncoding },
|
backupCodec: {
|
||||||
|
codec: "vp8",
|
||||||
|
encoding: VideoPresets.h720.encoding,
|
||||||
|
},
|
||||||
} as TrackPublishDefaults;
|
} as TrackPublishDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +159,3 @@ export function getLiveKitOptions(): RoomOptions {
|
|||||||
return buildLiveKitOptions();
|
return buildLiveKitOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep backward-compatible export for existing consumers
|
|
||||||
export const defaultLiveKitOptions: RoomOptions = buildLiveKitOptions();
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { iosDeviceMenu$ } from "../state/MediaDevices";
|
|||||||
import { useMediaDevices } from "../MediaDevicesContext";
|
import { useMediaDevices } from "../MediaDevicesContext";
|
||||||
import { widget } from "../widget";
|
import { widget } from "../widget";
|
||||||
import {
|
import {
|
||||||
|
type Setting,
|
||||||
useSetting,
|
useSetting,
|
||||||
soundEffectVolume as soundEffectVolumeSetting,
|
soundEffectVolume as soundEffectVolumeSetting,
|
||||||
backgroundBlur as backgroundBlurSetting,
|
backgroundBlur as backgroundBlurSetting,
|
||||||
@@ -112,33 +113,47 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScreenShareSettings: React.FC = (): ReactNode => {
|
const MediaQualitySettings: React.FC<{
|
||||||
const [advancedEnabled, setAdvancedEnabled] = useSetting(
|
id: string;
|
||||||
advancedScreenShareSetting,
|
header: string;
|
||||||
);
|
toggleLabel: string;
|
||||||
const [resolution, setResolution] = useSetting(
|
description: string;
|
||||||
screenShareResolutionSetting,
|
toggleSetting: Setting<boolean>;
|
||||||
);
|
resolutionSetting: Setting<string>;
|
||||||
const [framerate, setFramerate] = useSetting(screenShareFramerateSetting);
|
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 [advancedEnabled, setAdvancedEnabled] = useSetting(toggleSetting);
|
||||||
|
const [resolution, setResolution] = useSetting(resolutionSetting);
|
||||||
|
const [framerate, setFramerate] = useSetting(framerateSetting);
|
||||||
const [framerateRaw, setFramerateRaw] = useState(framerate);
|
const [framerateRaw, setFramerateRaw] = useState(framerate);
|
||||||
const [bitrate, setBitrate] = useSetting(screenShareBitrateSetting);
|
const [bitrate, setBitrate] = useSetting(bitrateSetting);
|
||||||
const [bitrateRaw, setBitrateRaw] = useState(bitrate);
|
const [bitrateRaw, setBitrateRaw] = useState(bitrate);
|
||||||
const [codec, setCodec] = useSetting(screenShareCodecSetting);
|
const [codec, setCodec] = useSetting(codecSetting);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4>{t("settings.screen_share_header", "Screen sharing")}</h4>
|
<h4>{header}</h4>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<InputField
|
<InputField
|
||||||
id="advancedScreenShare"
|
id={`${id}Toggle`}
|
||||||
label={t(
|
label={toggleLabel}
|
||||||
"settings.advanced_screen_share_label",
|
description={description}
|
||||||
"Advanced screen share settings",
|
|
||||||
)}
|
|
||||||
description={t(
|
|
||||||
"settings.advanced_screen_share_description",
|
|
||||||
"Configure resolution, framerate, bitrate, and codec for screen sharing",
|
|
||||||
)}
|
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={advancedEnabled}
|
checked={advancedEnabled}
|
||||||
onChange={(e): void => setAdvancedEnabled(e.target.checked)}
|
onChange={(e): void => setAdvancedEnabled(e.target.checked)}
|
||||||
@@ -147,29 +162,29 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
{advancedEnabled && (
|
{advancedEnabled && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.volumeSlider}>
|
<div className={styles.volumeSlider}>
|
||||||
<label htmlFor="screenShareResolution">
|
<label htmlFor={`${id}Resolution`}>
|
||||||
{t("settings.screen_share_resolution_label", "Resolution")}
|
{t("settings.resolution_label", "Resolution")}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="screenShareResolution"
|
id={`${id}Resolution`}
|
||||||
value={resolution}
|
value={resolution}
|
||||||
onChange={(e): void => setResolution(e.target.value)}
|
onChange={(e): void => setResolution(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="1024x576">576p</option>
|
{resolutionOptions.map((opt) => (
|
||||||
<option value="1280x720">720p</option>
|
<option key={opt.value} value={opt.value}>
|
||||||
<option value="1920x1080">1080p</option>
|
{opt.label}
|
||||||
<option value="2560x1440">1440p</option>
|
</option>
|
||||||
<option value="3840x2160">4K</option>
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.volumeSlider}>
|
<div className={styles.volumeSlider}>
|
||||||
<label>
|
<label>
|
||||||
{t("settings.screen_share_framerate_label", "Framerate")}
|
{t("settings.framerate_label", "Framerate")}
|
||||||
{": "}
|
{": "}
|
||||||
<span className={styles.settingValue}>{framerateRaw} fps</span>
|
<span className={styles.settingValue}>{framerateRaw} fps</span>
|
||||||
</label>
|
</label>
|
||||||
<Slider
|
<Slider
|
||||||
label={t("settings.screen_share_framerate_label", "Framerate")}
|
label={t("settings.framerate_label", "Framerate")}
|
||||||
value={framerateRaw}
|
value={framerateRaw}
|
||||||
onValueChange={setFramerateRaw}
|
onValueChange={setFramerateRaw}
|
||||||
onValueCommit={setFramerate}
|
onValueCommit={setFramerate}
|
||||||
@@ -181,142 +196,31 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.volumeSlider}>
|
<div className={styles.volumeSlider}>
|
||||||
<label>
|
<label>
|
||||||
{t("settings.screen_share_bitrate_label", "Bitrate")}
|
{t("settings.bitrate_label", "Bitrate")}
|
||||||
{": "}
|
{": "}
|
||||||
<span className={styles.settingValue}>
|
<span className={styles.settingValue}>
|
||||||
{(bitrateRaw / 1_000_000).toFixed(1)} Mbps
|
{(bitrateRaw / 1_000_000).toFixed(1)} Mbps
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Slider
|
<Slider
|
||||||
label={t("settings.screen_share_bitrate_label", "Bitrate")}
|
label={t("settings.bitrate_label", "Bitrate")}
|
||||||
value={bitrateRaw}
|
value={bitrateRaw}
|
||||||
onValueChange={setBitrateRaw}
|
onValueChange={setBitrateRaw}
|
||||||
onValueCommit={setBitrate}
|
onValueCommit={setBitrate}
|
||||||
min={500_000}
|
min={bitrateRange.min}
|
||||||
max={15_000_000}
|
max={bitrateRange.max}
|
||||||
step={500_000}
|
step={bitrateRange.step}
|
||||||
tooltipFormatter={(v): string =>
|
tooltipFormatter={(v): string =>
|
||||||
`${(v / 1_000_000).toFixed(1)} Mbps`
|
`${(v / 1_000_000).toFixed(1)} Mbps`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.volumeSlider}>
|
<div className={styles.volumeSlider}>
|
||||||
<label htmlFor="screenShareCodec">
|
<label htmlFor={`${id}Codec`}>
|
||||||
{t("settings.screen_share_codec_label", "Codec")}
|
{t("settings.codec_label", "Codec")}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="screenShareCodec"
|
id={`${id}Codec`}
|
||||||
value={codec}
|
|
||||||
onChange={(e): void =>
|
|
||||||
setCodec(e.target.value as VideoCodec)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="vp8">VP8</option>
|
|
||||||
<option value="vp9">VP9</option>
|
|
||||||
<option value="h264">H.264</option>
|
|
||||||
<option value="av1">AV1</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const CameraSettings: React.FC = (): ReactNode => {
|
|
||||||
const [advancedEnabled, setAdvancedEnabled] = useSetting(
|
|
||||||
advancedCameraSetting,
|
|
||||||
);
|
|
||||||
const [resolution, setResolution] = useSetting(cameraResolutionSetting);
|
|
||||||
const [framerate, setFramerate] = useSetting(cameraFramerateSetting);
|
|
||||||
const [framerateRaw, setFramerateRaw] = useState(framerate);
|
|
||||||
const [bitrate, setBitrate] = useSetting(cameraBitrateSetting);
|
|
||||||
const [bitrateRaw, setBitrateRaw] = useState(bitrate);
|
|
||||||
const [codec, setCodec] = useSetting(cameraCodecSetting);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h4>{t("settings.camera_header", "Camera quality")}</h4>
|
|
||||||
<FieldRow>
|
|
||||||
<InputField
|
|
||||||
id="advancedCamera"
|
|
||||||
label={t(
|
|
||||||
"settings.advanced_camera_label",
|
|
||||||
"Advanced camera settings",
|
|
||||||
)}
|
|
||||||
description={t(
|
|
||||||
"settings.advanced_camera_description",
|
|
||||||
"Configure resolution, framerate, bitrate, and codec for camera video. Changes apply on next call join.",
|
|
||||||
)}
|
|
||||||
type="checkbox"
|
|
||||||
checked={advancedEnabled}
|
|
||||||
onChange={(e): void => setAdvancedEnabled(e.target.checked)}
|
|
||||||
/>
|
|
||||||
</FieldRow>
|
|
||||||
{advancedEnabled && (
|
|
||||||
<>
|
|
||||||
<div className={styles.volumeSlider}>
|
|
||||||
<label htmlFor="cameraResolution">
|
|
||||||
{t("settings.camera_resolution_label", "Resolution")}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="cameraResolution"
|
|
||||||
value={resolution}
|
|
||||||
onChange={(e): void => setResolution(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="640x360">360p</option>
|
|
||||||
<option value="960x540">540p</option>
|
|
||||||
<option value="1280x720">720p</option>
|
|
||||||
<option value="1920x1080">1080p</option>
|
|
||||||
<option value="2560x1440">1440p</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className={styles.volumeSlider}>
|
|
||||||
<label>
|
|
||||||
{t("settings.camera_framerate_label", "Framerate")}
|
|
||||||
{": "}
|
|
||||||
<span className={styles.settingValue}>
|
|
||||||
{framerateRaw} fps
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<Slider
|
|
||||||
label={t("settings.camera_framerate_label", "Framerate")}
|
|
||||||
value={framerateRaw}
|
|
||||||
onValueChange={setFramerateRaw}
|
|
||||||
onValueCommit={setFramerate}
|
|
||||||
min={5}
|
|
||||||
max={60}
|
|
||||||
step={5}
|
|
||||||
tooltipFormatter={(v): string => `${v} fps`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.volumeSlider}>
|
|
||||||
<label>
|
|
||||||
{t("settings.camera_bitrate_label", "Bitrate")}
|
|
||||||
{": "}
|
|
||||||
<span className={styles.settingValue}>
|
|
||||||
{(bitrateRaw / 1_000_000).toFixed(1)} Mbps
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<Slider
|
|
||||||
label={t("settings.camera_bitrate_label", "Bitrate")}
|
|
||||||
value={bitrateRaw}
|
|
||||||
onValueChange={setBitrateRaw}
|
|
||||||
onValueCommit={setBitrate}
|
|
||||||
min={200_000}
|
|
||||||
max={8_000_000}
|
|
||||||
step={100_000}
|
|
||||||
tooltipFormatter={(v): string =>
|
|
||||||
`${(v / 1_000_000).toFixed(1)} Mbps`
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.volumeSlider}>
|
|
||||||
<label htmlFor="cameraCodec">
|
|
||||||
{t("settings.camera_codec_label", "Codec")}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="cameraCodec"
|
|
||||||
value={codec}
|
value={codec}
|
||||||
onChange={(e): void =>
|
onChange={(e): void =>
|
||||||
setCodec(e.target.value as VideoCodec)
|
setCodec(e.target.value as VideoCodec)
|
||||||
@@ -488,9 +392,45 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
<Separator />
|
<Separator />
|
||||||
<BlurCheckbox />
|
<BlurCheckbox />
|
||||||
<Separator />
|
<Separator />
|
||||||
<CameraSettings />
|
<MediaQualitySettings
|
||||||
|
id="camera"
|
||||||
|
header={t("settings.camera_header", "Camera quality")}
|
||||||
|
toggleLabel={t("settings.advanced_camera_label", "Advanced camera settings")}
|
||||||
|
description={t("settings.advanced_camera_description", "Configure resolution, framerate, bitrate, and codec for camera video. Changes apply on next call join.")}
|
||||||
|
toggleSetting={advancedCameraSetting}
|
||||||
|
resolutionSetting={cameraResolutionSetting}
|
||||||
|
framerateSetting={cameraFramerateSetting}
|
||||||
|
bitrateSetting={cameraBitrateSetting}
|
||||||
|
codecSetting={cameraCodecSetting}
|
||||||
|
resolutionOptions={[
|
||||||
|
{ value: "640x360", label: "360p" },
|
||||||
|
{ value: "960x540", label: "540p" },
|
||||||
|
{ value: "1280x720", label: "720p" },
|
||||||
|
{ value: "1920x1080", label: "1080p" },
|
||||||
|
{ value: "2560x1440", label: "1440p" },
|
||||||
|
]}
|
||||||
|
bitrateRange={{ min: 200_000, max: 8_000_000, step: 100_000 }}
|
||||||
|
/>
|
||||||
<Separator />
|
<Separator />
|
||||||
<ScreenShareSettings />
|
<MediaQualitySettings
|
||||||
|
id="screenShare"
|
||||||
|
header={t("settings.screen_share_header", "Screen sharing")}
|
||||||
|
toggleLabel={t("settings.advanced_screen_share_label", "Advanced screen share settings")}
|
||||||
|
description={t("settings.advanced_screen_share_description", "Configure resolution, framerate, bitrate, and codec for screen sharing")}
|
||||||
|
toggleSetting={advancedScreenShareSetting}
|
||||||
|
resolutionSetting={screenShareResolutionSetting}
|
||||||
|
framerateSetting={screenShareFramerateSetting}
|
||||||
|
bitrateSetting={screenShareBitrateSetting}
|
||||||
|
codecSetting={screenShareCodecSetting}
|
||||||
|
resolutionOptions={[
|
||||||
|
{ value: "1024x576", label: "576p" },
|
||||||
|
{ value: "1280x720", label: "720p" },
|
||||||
|
{ value: "1920x1080", label: "1080p" },
|
||||||
|
{ value: "2560x1440", label: "1440p" },
|
||||||
|
{ value: "3840x2160", label: "4K" },
|
||||||
|
]}
|
||||||
|
bitrateRange={{ min: 500_000, max: 15_000_000, step: 500_000 }}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -166,6 +166,14 @@ export const customLivekitUrl = new Setting<string | null>(
|
|||||||
|
|
||||||
export type VideoCodec = "vp8" | "vp9" | "h264" | "av1";
|
export type VideoCodec = "vp8" | "vp9" | "h264" | "av1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a "WIDTHxHEIGHT" resolution string into numeric width and height.
|
||||||
|
*/
|
||||||
|
export function parseResolution(res: string): { width: number; height: number } {
|
||||||
|
const [w, h] = res.split("x").map(Number);
|
||||||
|
return { width: w, height: h };
|
||||||
|
}
|
||||||
|
|
||||||
export const advancedScreenShare = new Setting<boolean>(
|
export const advancedScreenShare = new Setting<boolean>(
|
||||||
"advanced-screen-share",
|
"advanced-screen-share",
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import {
|
|||||||
screenShareFramerate,
|
screenShareFramerate,
|
||||||
screenShareBitrate,
|
screenShareBitrate,
|
||||||
screenShareCodec,
|
screenShareCodec,
|
||||||
|
parseResolution,
|
||||||
} from "../../../settings/settings.ts";
|
} from "../../../settings/settings.ts";
|
||||||
import { Config } from "../../../config/Config.ts";
|
import { Config } from "../../../config/Config.ts";
|
||||||
import {
|
import {
|
||||||
@@ -688,9 +689,7 @@ export const createLocalMembership$ = ({
|
|||||||
|
|
||||||
if (advancedScreenShare.getValue()) {
|
if (advancedScreenShare.getValue()) {
|
||||||
// User has advanced screen share settings enabled
|
// User has advanced screen share settings enabled
|
||||||
const resParts = screenShareResolution.getValue().split("x");
|
const { width, height } = parseResolution(screenShareResolution.getValue());
|
||||||
const width = Number(resParts[0]);
|
|
||||||
const height = Number(resParts[1]);
|
|
||||||
const fps = screenShareFramerate.getValue();
|
const fps = screenShareFramerate.getValue();
|
||||||
const bps = screenShareBitrate.getValue();
|
const bps = screenShareBitrate.getValue();
|
||||||
const codec = screenShareCodec.getValue();
|
const codec = screenShareCodec.getValue();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
cameraFramerate,
|
cameraFramerate,
|
||||||
cameraBitrate,
|
cameraBitrate,
|
||||||
cameraCodec,
|
cameraCodec,
|
||||||
|
parseResolution,
|
||||||
echoCancellationSetting,
|
echoCancellationSetting,
|
||||||
noiseSuppressionSetting,
|
noiseSuppressionSetting,
|
||||||
autoGainControlSetting,
|
autoGainControlSetting,
|
||||||
@@ -150,9 +151,7 @@ function generateRoomOption({
|
|||||||
let publishDefaults = liveKitOptions.publishDefaults;
|
let publishDefaults = liveKitOptions.publishDefaults;
|
||||||
|
|
||||||
if (advancedCamera.getValue()) {
|
if (advancedCamera.getValue()) {
|
||||||
const resParts = cameraResolution.getValue().split("x");
|
const { width, height } = parseResolution(cameraResolution.getValue());
|
||||||
const width = Number(resParts[0]);
|
|
||||||
const height = Number(resParts[1]);
|
|
||||||
const fps = cameraFramerate.getValue();
|
const fps = cameraFramerate.getValue();
|
||||||
const bps = cameraBitrate.getValue();
|
const bps = cameraBitrate.getValue();
|
||||||
const codec = cameraCodec.getValue();
|
const codec = cameraCodec.getValue();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { constant } from "../../Behavior";
|
|||||||
import {
|
import {
|
||||||
echoCancellationSetting,
|
echoCancellationSetting,
|
||||||
noiseSuppressionSetting,
|
noiseSuppressionSetting,
|
||||||
|
autoGainControlSetting,
|
||||||
} from "../../../settings/settings.ts";
|
} from "../../../settings/settings.ts";
|
||||||
|
|
||||||
// At the top of your test file, after imports
|
// At the top of your test file, after imports
|
||||||
@@ -105,9 +106,13 @@ describe("ECConnectionFactory - ControlledAudioDevice", () => {
|
|||||||
test.each([{ controlled: true }, { controlled: false }])(
|
test.each([{ controlled: true }, { controlled: false }])(
|
||||||
"it sets controlledAudioDevice=$controlled then uses deviceId accordingly",
|
"it sets controlledAudioDevice=$controlled then uses deviceId accordingly",
|
||||||
({ controlled }) => {
|
({ controlled }) => {
|
||||||
// test("it sets echoCancellation and noiseSuppression based on constructor parameters", () => {
|
|
||||||
const RoomConstructor = vi.mocked(LivekitRoom);
|
const RoomConstructor = vi.mocked(LivekitRoom);
|
||||||
|
|
||||||
|
// Explicitly set audio settings so the test doesn't depend on defaults
|
||||||
|
echoCancellationSetting.setValue(true);
|
||||||
|
noiseSuppressionSetting.setValue(true);
|
||||||
|
autoGainControlSetting.setValue(true);
|
||||||
|
|
||||||
const ecConnectionFactory = new ECConnectionFactory(
|
const ecConnectionFactory = new ECConnectionFactory(
|
||||||
mockClient,
|
mockClient,
|
||||||
"!roomid:example.org",
|
"!roomid:example.org",
|
||||||
|
|||||||
Reference in New Issue
Block a user