diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 8c0881e545f..9ef12c097e5 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -378,6 +378,23 @@ dwmflush dwmflush = enabled +fps_min +^^^^^^^ + +**Description** + The minimum framerate Sunshine will attempt to maintain. Increasing this value slightly may help when streaming + mostly static content. + + .. Warning:: Higher values will consume more bandwidth. + +**Default** + ``10`` + +**Example** + .. code-block:: text + + fps_min = 10 + Audio ----- diff --git a/src/config.cpp b/src/config.cpp index 71b90df841f..f9077fed7c6 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -314,7 +314,8 @@ video_t video { 0, // hevc_mode - 1, // min_threads + 10, // min_fps + 1, // min_threads { "superfast"s, // preset "zerolatency"s, // tune diff --git a/src/config.h b/src/config.h index 2a4a638ad93..d8cf1488d0b 100644 --- a/src/config.h +++ b/src/config.h @@ -15,6 +15,7 @@ struct video_t { int hevc_mode; + int min_fps; // Minimum framerate int min_threads; // Minimum number of threads/slices for CPU encoding struct { std::string sw_preset; diff --git a/src/video.cpp b/src/video.cpp index 08d77ac9ff0..c028a77bead 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1281,6 +1281,26 @@ void encode_run( const encoder_t &encoder, void *channel_data) { + // get minimum fps from config + auto min_fps = config::video.min_fps; + auto minimum_frame_time = std::chrono::milliseconds(1000 / 10); // default value + + // check if min_fps is an integer + if(min_fps != (int)min_fps) { + BOOST_LOG(warning) << "Minimum FPS must be an integer, update your config. Falling back to 10 FPS."sv; + } + else { + minimum_frame_time = std::chrono::milliseconds(1000 / config::video.min_fps); // use config value if it is an integer + } + + // check if minimum frame time is between 1 and 240 fps + if(minimum_frame_time < std::chrono::milliseconds(1000 / 240)) { + minimum_frame_time = std::chrono::milliseconds(1000 / 240); + } + else if(minimum_frame_time > std::chrono::milliseconds(1000 / 1)) { // 1 fps, showing equation just for clarity... maybe should have a min fps a little higher than 1? + minimum_frame_time = std::chrono::milliseconds(1000 / 1); + } + auto session = make_session(disp.get(), encoder, config, disp->width, disp->height, std::move(hwdevice)); if(!session) { return; @@ -1311,9 +1331,9 @@ void encode_run( idr_events->pop(); } - // Encode at a minimum of 10 FPS to avoid image quality issues with static content + // Encode at a minimum of FPS to avoid image quality issues with static content if(!frame->key_frame || images->peek()) { - if(auto img = images->pop(100ms)) { + if(auto img = images->pop(minimum_frame_time)) { if(session->device->convert(*img)) { BOOST_LOG(error) << "Could not convert image"sv; return; diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 3e56413aaf7..2f943f93ef7 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -502,6 +502,22 @@