Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3453,7 +3453,7 @@
"s_onboarding_51085de55c": "You're set.",
"s_onboarding_b8e418c4eb": "Your preferences will be saved when you finish setup. You can revisit them in Settings.",
"s_onboarding_113143c308": "macOS ONLY",
"s_onboarding_23a5a729cc": "%1 / 200 Mbps",
"s_onboarding_23a5a729cc": "%1 / 100 Mbps",
"s_onboarding_6b89662ed2": "%1 Mbps",
"s_onboarding_e9037adc0f": "%1 Mbps maximum",
"s_onboarding_a77247ed92": "%1 is a beta. We will say more about that right after you sign in.",
Expand Down
35 changes: 30 additions & 5 deletions native/opennow-core/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,13 +789,13 @@ fn clamp_integer(

fn clamp_bitrate_mbps(values: &mut Map<String, Value>) {
// 0.22 Mbps is 220 kbps. Whole numbers stay integers so existing settings
// and the 10–200 Mbps slider keep their previous JSON shape.
// and the bitrate slider keep their previous JSON shape.
let raw = values
.get("maxBitrateMbps")
.and_then(Value::as_f64)
.filter(|value| value.is_finite())
.unwrap_or(75.0);
let value = (raw.clamp(0.22, 200.0) * 100.0).round() / 100.0;
let value = (raw.clamp(0.22, 100.0) * 100.0).round() / 100.0;
let stored = if (value - value.round()).abs() < 1e-9 {
Value::from(value.round() as i64)
} else {
Expand Down Expand Up @@ -2417,6 +2417,31 @@ mod tests {
fs::remove_dir_all(directory).unwrap();
}

#[test]
fn legacy_bitrate_above_limit_is_clamped_on_load_and_preserved_on_save() {
let directory = tempfile::tempdir().unwrap();
fs::write(
directory.path().join("settings.json"),
json!({
"maxBitrateMbps": 200,
"onboardingCompleted": true,
"qtConsoleModePolicyVersion": 1,
"legacySetting": "retained"
})
.to_string(),
)
.unwrap();

let mut store = SettingsStore::load(Some(directory.path().to_path_buf())).unwrap();
assert_eq!(store.all()["maxBitrateMbps"], json!(100));
assert_eq!(store.set("maxBitrateMbps", json!(100)).unwrap(), json!(100));
let persisted: Value =
serde_json::from_slice(&fs::read(directory.path().join("settings.json")).unwrap())
.unwrap();
assert_eq!(persisted["maxBitrateMbps"], json!(100));
assert_eq!(persisted["legacySetting"], json!("retained"));
}

#[test]
fn persists_and_normalizes_settings() {
let unique = SystemTime::now()
Expand Down Expand Up @@ -2481,13 +2506,13 @@ mod tests {
assert_eq!(store.set("fps", json!(999)).unwrap(), json!(360));
assert_eq!(store.set("fps", json!(360)).unwrap(), json!(360));
assert_eq!(store.set("fps", json!(240)).unwrap(), json!(240));
assert_eq!(store.set("maxBitrateMbps", json!(200)).unwrap(), json!(200));
assert_eq!(store.set("maxBitrateMbps", json!(100)).unwrap(), json!(100));
let low_bitrate = store.set("maxBitrateMbps", json!(0.22)).unwrap();
assert!((low_bitrate.as_f64().unwrap() - 0.22).abs() < 0.001);
let clamped_bitrate = store.set("maxBitrateMbps", json!(0.1)).unwrap();
assert!((clamped_bitrate.as_f64().unwrap() - 0.22).abs() < 0.001);
assert_eq!(store.set("maxBitrateMbps", json!(27)).unwrap(), json!(27));
assert_eq!(store.set("maxBitrateMbps", json!(200)).unwrap(), json!(200));
assert_eq!(store.set("maxBitrateMbps", json!(200)).unwrap(), json!(100));
assert_eq!(
store.set("launchInConsoleMode", json!(false)).unwrap(),
json!(false)
Expand All @@ -2510,7 +2535,7 @@ mod tests {
);
let loaded = SettingsStore::load(Some(directory.clone())).unwrap();
assert_eq!(loaded.all()["fps"], json!(240));
assert_eq!(loaded.all()["maxBitrateMbps"], json!(200));
assert_eq!(loaded.all()["maxBitrateMbps"], json!(100));
assert_eq!(loaded.all()["saveBandwidth"], json!(true));
assert_eq!(loaded.all()["launchInConsoleMode"], json!(false));
assert_eq!(loaded.all()["reducedMotion"], json!(true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const CONTROL_PING_EXPIRY: Duration = Duration::from_secs(5);
const CONTROL_IO_TIMEOUT: Duration = Duration::from_millis(100);
const MAX_REQUEST_RESPONSE_BYTES: usize = 4 * 1024 * 1024;
const MAX_CONTROL_RESPONSE_BYTES: usize = 64 * 1024;
const MAX_STREAM_BITRATE_MBPS: u64 = 200;
const MAX_STREAM_BITRATE_MBPS: u64 = 100;
// GeForce NOW 2.0.87.131 reports video[0].timeoutLengthMs=8000 and
// video[0].sendFrameTimeoutMs=7000. Waiting sixty seconds left a dead Mjolnir media leg on screen
// while audio/control remained alive; use the official receiver timeout so the existing bounded
Expand Down Expand Up @@ -2755,7 +2755,7 @@ mod tests {
}

#[test]
fn owned_announce_preserves_the_configured_200_mbps_ceiling() {
fn owned_announce_clamps_legacy_200_mbps_request_to_100_mbps() {
let mut value = context();
value.settings["maxBitrateMbps"] = json!(200);
let sdp = build_announce(
Expand All @@ -2776,9 +2776,9 @@ mod tests {
qos_timings_v5: false,
},
);
assert!(sdp.contains("a=x-nv-video[0].initialBitrateKbps:200000"));
assert!(sdp.contains("a=x-nv-video[0].initialPeakBitrateKbps:200000"));
assert!(sdp.contains("a=x-nv-vqos[0].bw.maximumBitrateKbps:200000"));
assert!(sdp.contains("a=x-nv-video[0].initialBitrateKbps:100000"));
assert!(sdp.contains("a=x-nv-video[0].initialPeakBitrateKbps:100000"));
assert!(sdp.contains("a=x-nv-vqos[0].bw.maximumBitrateKbps:100000"));
assert!(sdp.contains("a=x-nv-vqos[0].bw.minimumBitrateKbps:1000"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ Column {
BitrateSlider {
objectName: "onboardingBitrate"; accessibleName: qsTr("Bitrate")
trackWidth: Math.min(DesktopTokens.px(220), Math.max(DesktopTokens.px(100), bitrateRow.width - DesktopTokens.px(470)))
from: 0.22; to: 200; stepSize: 0.01; suffix: qsTr(" Mbps")
from: 0.22; to: 100; stepSize: 0.01; suffix: qsTr(" Mbps")
value: Number(root.settings.maxBitrateMbps ?? 75)
onMoved: value => root.store.setOnboardingSetting("maxBitrateMbps", Math.round(value * 100) / 100)
}
Expand Down Expand Up @@ -322,11 +322,11 @@ Column {
RowLayout {
width: parent.width
Copy { text: qsTr("Bitrate limit"); color: Theme.label; font.pixelSize: DesktopTokens.px(13); font.weight: Font.ExtraBold; Layout.fillWidth: true }
Copy { text: qsTr("%1 / 200 Mbps").arg(root.settings.maxBitrateMbps ?? 75); font.family: Theme.monoFont; font.pixelSize: DesktopTokens.px(11) }
Copy { text: qsTr("%1 / 100 Mbps").arg(root.settings.maxBitrateMbps ?? 75); font.family: Theme.monoFont; font.pixelSize: DesktopTokens.px(11) }
}
Rectangle {
width: parent.width; height: DesktopTokens.px(4); radius: height / 2; color: DesktopTokens.raised
Rectangle { width: parent.width * Math.max(0, Math.min(1, Number(root.settings.maxBitrateMbps ?? 75) / 200)); height: parent.height; radius: height / 2; color: root.mint }
Rectangle { width: parent.width * Math.max(0, Math.min(1, Number(root.settings.maxBitrateMbps ?? 75) / 100)); height: parent.height; radius: height / 2; color: root.mint }
}
Copy { width: parent.width; text: qsTr("Requested maximum, not a network test. Actual quality depends on your membership, device and connection.") }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Column {
DesktopSettingsRow {
width: parent.width; paperStyle: true; glyph: "wave"; title: qsTr("Bitrate"); description: qsTr("Maximum requested bitrate")
DesktopSettingsSlider {
from: 0.22; to: 200; stepSize: 0.01; decimals: 2
from: 0.22; to: 100; stepSize: 0.01; decimals: 2
value: Number(page.settingsScreen.valueSetting("maxBitrateMbps",75)); suffix: " Mbps"
onCommitted: value => page.settingsScreen.setSetting("maxBitrateMbps", Math.round(value * 100) / 100)
}
Expand Down
4 changes: 2 additions & 2 deletions opennow-qt/qml/overlays/QuickSettingsOverlay.qml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ FocusScope {
Accessible.role: Accessible.Pane
Accessible.name: qsTr("Quick settings")

readonly property var bitrates: [25, 50, 75, 100, 150, 200]
readonly property var bitrates: [25, 50, 75, 100]
readonly property var controllers: ControllerInput.controllers || []
readonly property string tier: String(ShellStore.subscription
&& ShellStore.subscription.membershipTier || qsTr("Ready"))
Expand Down Expand Up @@ -234,7 +234,7 @@ FocusScope {
title: qsTr("Max bitrate")
value: Number(ShellStore.settings.maxBitrateMbps || 75) + " Mbps"
sliderVisible: true
sliderProgress: Number(ShellStore.settings.maxBitrateMbps || 75) / 200
sliderProgress: Number(ShellStore.settings.maxBitrateMbps || 75) / 100
KeyNavigation.up: regionRow; KeyNavigation.down: statsRow
onTriggered: root.nextBitrate()
}
Expand Down
2 changes: 1 addition & 1 deletion opennow-qt/qml/screens/SettingsScreen.qml
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ FocusScope {
choice("Fallback codec", "Used when the preferred codec isn't offered by the rig", "fallbackCodec", ["auto","h264","h265"], ["Auto","H.264","H.265"], "dropdown", disabledCodecs),
descriptorChoice(qsTr("Color quality"), ShellStore.settingsOwnerState.colorDescription, "colorQuality", ShellStore.settingsOwnerState.colorQualityItems),
{t:qsTr("HDR"), d:hdrDescription, v:Boolean(settings.enableHdr) ? qsTr("On") : qsTr("Off"), key:"enableHdr", values:[false,true], labels:[qsTr("Off"),qsTr("On")], control:"segments", selectedIndex:Boolean(settings.enableHdr) ? 1 : 0, disabledValues:(hdrAvailable && hdrTierOk) ? [] : [true]},
{t:"Max bitrate", d:"Maximum requested stream bitrate", v:Number(settings.maxBitrateMbps || 75) + " Mbps", key:"maxBitrateMbps", values:[0.22,1,5,10,25,50,75,100,150,200], labels:["0.22 Mbps","1 Mbps","5 Mbps","10 Mbps","25 Mbps","50 Mbps","75 Mbps","100 Mbps","150 Mbps","200 Mbps"], control:"slider", sliderPercent:Number(settings.maxBitrateMbps || 75) / 200},
{t:"Max bitrate", d:"Maximum requested stream bitrate", v:Number(settings.maxBitrateMbps || 75) + " Mbps", key:"maxBitrateMbps", values:[0.22,1,5,10,25,50,75,100], labels:["0.22 Mbps","1 Mbps","5 Mbps","10 Mbps","25 Mbps","50 Mbps","75 Mbps","100 Mbps"], control:"slider", sliderPercent:Number(settings.maxBitrateMbps || 75) / 100},
toggle(qsTr("Save bandwidth"), qsTr("Lets the server trade resolution and image quality for a steadier frame rate when your connection cannot sustain the selected profile. Off requests no dynamic adjustment. Applies to new sessions."), "saveBandwidth"),
{t:qsTr("Frame generation (Experimental)"), d:qsTr("Targets 120 displayed FPS from a 60 FPS stream. Requires a fast GPU and 120 Hz display; adds latency and artifacts."), v:frameGeneration ? qsTr("2×") : qsTr("Off"), key:"frameGeneration", values:["off","2x"], labels:[qsTr("Off"),qsTr("2×")], control:"segments", selectedIndex:frameGeneration ? 1 : 0},
choice(qsTr("Upscaling"), Qt.platform.os === "osx"
Expand Down
Loading