diff --git a/docs/configuration.md b/docs/configuration.md index f788729d..a1ae900c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1270,7 +1270,7 @@ editing the `conf` file in a text editor. Use the examples as reference. Remap the requested resolution and FPS to another display mode.
Depending on the [dd_resolution_option](#dd_resolution_option) and - [dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping + [dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping groups are available: - For each of those groups, a list of fields can be configured to perform remapping: + For each of those groups, a list of fields can be configured to perform remapping: If `requested_*` field is left empty, it will match everything.
- If `final_*` field is left empty, the original value will not be remapped and either a requested, manual - or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered + If `final_*` field is left empty, the original value will not be remapped and either a requested, manual + or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered invalid.
- @note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution` + @note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution` field to be considered.} @note{First entry to be matched in the list is the one that will be used.} @tip{`requested_resolution` and `final_resolution` can be omitted for `refresh_rate_only` group.} @@ -1371,6 +1371,32 @@ editing the `conf` file in a text editor. Use the examples as reference. +### minimum_fps_target + + + + + + + + + + + + + + + + + + + +
Description + Sunshine tries to save bandwidth when content on screen is static or a low framerate. Because many clients expect a constant stream of video frames, a certain amount of duplicate frames are sent when this happens. This setting controls the lowest effective framerate a stream can reach. +
Default@code{} + 0 + @endcode
Choices0Use half the stream's FPS as the minimum target.
1-1000Specify your own value. The real minimum may differ from this value.
+ ## Network ### upnp diff --git a/src/config.cpp b/src/config.cpp index c1dba388..17faacb4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -504,7 +504,8 @@ namespace config { {} // wa }, // display_device - 0 // max_bitrate + 0, // max_bitrate + 0 // minimum_fps_target (0 = framerate) }; audio_t audio { @@ -1146,6 +1147,7 @@ namespace config { } int_f(vars, "max_bitrate", video.max_bitrate); + double_between_f(vars, "minimum_fps_target", video.minimum_fps_target, {0.0, 1000.0}); path_f(vars, "pkey", nvhttp.pkey); path_f(vars, "cert", nvhttp.cert); diff --git a/src/config.h b/src/config.h index 2e088ea7..287efaf5 100644 --- a/src/config.h +++ b/src/config.h @@ -141,6 +141,7 @@ namespace config { } dd; int max_bitrate; // Maximum bitrate, sets ceiling in kbps for bitrate requested from client + double minimum_fps_target; ///< Lowest framerate that will be used when streaming. Range 0-1000, 0 = half of client's requested framerate. }; struct audio_t { diff --git a/src/video.cpp b/src/video.cpp index ed263e21..4db957b3 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1890,9 +1890,10 @@ namespace video { } }); - // set minimum frame time based on client-requested target framerate - std::chrono::duration minimum_frame_time {1000.0 / config.framerate}; - BOOST_LOG(info) << "Minimum frame time set to "sv << minimum_frame_time.count() << "ms, based on client-requested target framerate "sv << config.framerate << "."sv; + // set max frame time based on client-requested target framerate. + double minimum_fps_target = (config::video.minimum_fps_target > 0.0) ? config::video.minimum_fps_target : config.framerate; + std::chrono::duration max_frametime {1000.0 / minimum_fps_target}; + BOOST_LOG(info) << "Minimum FPS target set to ~"sv << (minimum_fps_target / 2) << "fps ("sv << max_frametime.count() * 2 << "ms)"sv; auto shutdown_event = mail->event(mail::shutdown); auto packets = mail::man->queue(mail::video_packets); @@ -1943,7 +1944,7 @@ namespace video { // Encode at a minimum FPS to avoid image quality issues with static content if (!requested_idr_frame || images->peek()) { - if (auto img = images->pop(minimum_frame_time)) { + if (auto img = images->pop(max_frametime)) { frame_timestamp = img->frame_timestamp; if (session->convert(*img)) { BOOST_LOG(error) << "Could not convert image"sv; diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 6e766e93..9c1fb5e9 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -179,6 +179,7 @@ "dd_mode_remapping": {"mixed": [], "resolution_only": [], "refresh_rate_only": []}, "dd_wa_hdr_toggle_delay": 0, "max_bitrate": 0, + "minimum_fps_target": 0 }, }, { diff --git a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue index 25b12deb..51b5b8c2 100644 --- a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue +++ b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue @@ -17,6 +17,13 @@ const config = ref(props.config)
{{ $t("config.max_bitrate_desc") }}
+ + +
+ + +
{{ $t("config.minimum_fps_target_desc") }}
+