pub fn normalize_audio_gain_percent(percent: u32) -> u32 { percent.min(MAX_AUDIO_GAIN_PERCENT) } pub fn format_audio_gain_percent(percent: u32) -> String { format!("{}%", normalize_audio_gain_percent(percent)) } pub fn normalize_mic_gain_percent(percent: u32) -> u32 { percent.min(MAX_MIC_GAIN_PERCENT) } pub fn format_mic_gain_percent(percent: u32) -> String { format!("{}%", normalize_mic_gain_percent(percent)) } fn breakout_size_choice( physical_limit: PreviewSourceSize, display_fill: PreviewSourceSize, source: PreviewSourceSize, preset: BreakoutSizePreset, ) -> BreakoutSizeChoice { let physical_width = physical_limit.width.max(1) as i32; let physical_height = physical_limit.height.max(1) as i32; let display_width = display_fill.width.max(1) as i32; let display_height = display_fill.height.max(1) as i32; let (width, height) = match preset { BreakoutSizePreset::P360 => { fit_standard_dimensions(physical_width, physical_height, 640, 360) } BreakoutSizePreset::P540 => { fit_standard_dimensions(physical_width, physical_height, 960, 540) } BreakoutSizePreset::P720 => { fit_standard_dimensions(physical_width, physical_height, 1280, 720) } BreakoutSizePreset::P900 => { fit_standard_dimensions(physical_width, physical_height, 1600, 900) } BreakoutSizePreset::P1080 => { fit_standard_dimensions(physical_width, physical_height, 1920, 1080) } BreakoutSizePreset::P1440 => { fit_standard_dimensions(physical_width, physical_height, 2560, 1440) } BreakoutSizePreset::Source => fit_standard_dimensions( physical_width, physical_height, source.width.max(1) as i32, source.height.max(1) as i32, ), BreakoutSizePreset::FillDisplay => (display_width, display_height), }; BreakoutSizeChoice { preset, width, height, } } fn breakout_size_options( physical_limit: PreviewSourceSize, display_fill: PreviewSourceSize, source: PreviewSourceSize, ) -> Vec { let mut options = Vec::new(); for preset in [ BreakoutSizePreset::Source, BreakoutSizePreset::P360, BreakoutSizePreset::P540, BreakoutSizePreset::P720, BreakoutSizePreset::P900, BreakoutSizePreset::P1080, BreakoutSizePreset::P1440, BreakoutSizePreset::FillDisplay, ] { let choice = breakout_size_choice(physical_limit, display_fill, source, preset); let allow_duplicate_label = matches!( preset, BreakoutSizePreset::Source | BreakoutSizePreset::FillDisplay ); if !allow_duplicate_label && options.iter().any(|existing: &BreakoutSizeChoice| { existing.width == choice.width && existing.height == choice.height }) { continue; } options.push(choice); } options } fn capture_size_choice( _source: PreviewSourceSize, preset: CaptureSizePreset, selected_fps: u32, selected_bitrate_kbit: u32, ) -> CaptureSizeChoice { let preset = normalize_capture_size_preset(preset); let mode = preset.source_mode(); let _ = (selected_fps, selected_bitrate_kbit); let (width, height, fps, max_bitrate_kbit) = ( mode.width as i32, mode.height as i32, mode.fps, estimate_source_bitrate_kbit(mode.width as i32, mode.height as i32, mode.fps), ); CaptureSizeChoice { preset, width, height, fps, max_bitrate_kbit, } } fn estimate_source_bitrate_kbit(width: i32, height: i32, fps: u32) -> u32 { let pixels_per_second = width.max(1) as u64 * height.max(1) as u64 * fps.max(1) as u64; match pixels_per_second { p if p >= 1920_u64 * 1080 * 50 => 18_000, p if p >= 1920_u64 * 1080 * 24 => 12_000, p if p >= 1280_u64 * 720 * 24 => 6_000, _ => 2_500, } } fn capture_size_options(source: PreviewSourceSize) -> Vec { native_eye_source_modes() .iter() .copied() .filter(|mode| mode.width <= source.width && mode.height <= source.height) .map(CaptureSizePreset::from_source_mode) .map(|preset| { let defaults = default_profile_for_preset(source, preset); capture_size_choice(source, preset, defaults.fps, defaults.max_bitrate_kbit) }) .collect() } fn capture_fps_options(source: PreviewSourceSize) -> Vec { vec![CaptureFpsChoice { fps: source.fps.max(1), }] } fn capture_bitrate_options(source: PreviewSourceSize) -> Vec { vec![CaptureBitrateChoice { max_bitrate_kbit: estimate_source_bitrate_kbit( source.width as i32, source.height as i32, source.fps, ), }] } fn default_profile_for_preset( _source: PreviewSourceSize, preset: CaptureSizePreset, ) -> CaptureSizeChoice { let preset = normalize_capture_size_preset(preset); let mode = preset.source_mode(); let (width, height, fps, max_bitrate_kbit) = ( mode.width as i32, mode.height as i32, mode.fps, estimate_source_bitrate_kbit(mode.width as i32, mode.height as i32, mode.fps), ); CaptureSizeChoice { preset, width, height, fps, max_bitrate_kbit, } } fn normalize_capture_size_preset(preset: CaptureSizePreset) -> CaptureSizePreset { match preset { CaptureSizePreset::Vga | CaptureSizePreset::P480 | CaptureSizePreset::P576 => { CaptureSizePreset::P720 } other => other, } } fn fit_standard_dimensions( limit_width: i32, limit_height: i32, wanted_width: i32, wanted_height: i32, ) -> (i32, i32) { let width = wanted_width.min(limit_width).max(2); let height = wanted_height.min(limit_height).max(2); if width == limit_width && height == limit_height { return (width, height); } let width_from_height = round_down_even((height * 16) / 9); if width_from_height <= limit_width { (round_down_even(width_from_height), round_down_even(height)) } else { let height_from_width = round_down_even((width * 9) / 16); (round_down_even(width), round_down_even(height_from_width)) } } fn round_down_even(value: i32) -> i32 { let rounded = value.max(2); rounded - (rounded % 2) } fn normalize_selection(value: Option) -> Option { value.and_then(|v| { let trimmed = v.trim(); if trimmed.is_empty() || trimmed.eq_ignore_ascii_case("auto") { None } else { Some(trimmed.to_string()) } }) } fn keep_or_select_first(selection: &mut Option, values: &[String]) { if selection.is_none() { *selection = values.first().cloned(); } } fn media_status_label(enabled: bool, selected: Option<&str>) -> &str { if !enabled { "off" } else { selected.unwrap_or("none") } } fn normalize_swap_key(value: String) -> String { let trimmed = value.trim(); if trimmed.is_empty() { "off".to_string() } else { trimmed.to_ascii_lowercase() } }