@@ -98,6 +98,52 @@ namespace video {
9898 }
9999 } // namespace
100100
101+ std::chrono::duration<double , std::milli>
102+ minimum_frame_time_for_vrr (int stream_fps, int minimum_fps_target) {
103+ if (minimum_fps_target > 0 ) {
104+ return std::chrono::duration<double , std::milli> { 1000.0 / minimum_fps_target };
105+ }
106+
107+ return std::chrono::duration<double , std::milli> { 2000.0 / std::max (stream_fps, 1 ) };
108+ }
109+
110+ input_activity_boost_policy_t
111+ make_input_activity_boost_policy (const input_activity_boost_config_t &config) {
112+ input_activity_boost_policy_t policy {};
113+ policy.configured =
114+ config.variable_refresh_rate &&
115+ config.enabled &&
116+ config.boost_fps > 0 &&
117+ config.window_ms > 0 ;
118+
119+ if (!policy.configured ) {
120+ return policy;
121+ }
122+
123+ policy.fps = std::min (config.boost_fps , std::max (config.stream_fps , 1 ));
124+ policy.frame_time = std::chrono::duration<double , std::milli> { 1000.0 / policy.fps };
125+ policy.useful = config.minimum_fps_target == 0 || policy.fps > config.minimum_fps_target ;
126+
127+ return policy;
128+ }
129+
130+ std::chrono::duration<double , std::milli>
131+ effective_minimum_frame_time (
132+ const std::chrono::duration<double , std::milli> &base_minimum_frame_time,
133+ const input_activity_boost_policy_t &input_activity_boost_policy,
134+ bool input_boost_active,
135+ int minimum_fps_target) {
136+ if (!input_boost_active || !input_activity_boost_policy.useful ) {
137+ return base_minimum_frame_time;
138+ }
139+
140+ if (minimum_fps_target > 0 ) {
141+ return std::min (base_minimum_frame_time, input_activity_boost_policy.frame_time );
142+ }
143+
144+ return input_activity_boost_policy.frame_time ;
145+ }
146+
101147 void
102148 free_ctx (AVCodecContext *ctx) {
103149 avcodec_free_context (&ctx);
@@ -2687,34 +2733,32 @@ namespace video {
26872733
26882734 // Set the base minimum frame time based on client-requested target framerate or minimum_fps_target.
26892735 // This can be temporarily reduced later if VRR input activity boost is active.
2690- std::chrono::duration< double , std::milli> base_minimum_frame_time ;
2736+ const auto base_minimum_frame_time = minimum_frame_time_for_vrr (config. framerate , config::video. minimum_fps_target ) ;
26912737 if (config::video.minimum_fps_target > 0 ) {
2692- // Use minimum_fps_target if specified
2693- base_minimum_frame_time = std::chrono::duration<double , std::milli> { 1000.0 / config::video.minimum_fps_target };
26942738 BOOST_LOG (info) << " Minimum frame time set to " sv << base_minimum_frame_time.count () << " ms, based on minimum_fps_target " sv << config::video.minimum_fps_target << " fps." sv;
26952739 }
26962740 else {
2697- // Default behavior: about half the stream FPS
2698- base_minimum_frame_time = std::chrono::duration<double , std::milli> { 2000.0 / config.framerate };
26992741 BOOST_LOG (info) << " Minimum frame time set to " sv << base_minimum_frame_time.count () << " ms, based on client-requested target framerate " sv << config.framerate << " ." sv;
27002742 }
27012743
2702- const bool input_activity_boost_enabled =
2703- config::video.variable_refresh_rate &&
2704- config::video.input_activity_boost &&
2705- config::video.input_activity_boost_fps > 0 &&
2706- config::video.input_activity_boost_window_ms > 0 ;
2707-
2708- const auto input_activity_boost_frame_time = input_activity_boost_enabled ?
2709- std::chrono::duration<double , std::milli> { 1000.0 / config::video.input_activity_boost_fps } :
2710- base_minimum_frame_time;
2744+ const auto input_activity_boost_policy = make_input_activity_boost_policy ({
2745+ config::video.variable_refresh_rate ,
2746+ config::video.input_activity_boost ,
2747+ config.framerate ,
2748+ config::video.minimum_fps_target ,
2749+ config::video.input_activity_boost_fps ,
2750+ config::video.input_activity_boost_window_ms ,
2751+ });
27112752 const auto input_activity_boost_window = std::chrono::milliseconds { config::video.input_activity_boost_window_ms };
27122753
2713- if (input_activity_boost_enabled ) {
2754+ if (input_activity_boost_policy. useful ) {
27142755 BOOST_LOG (info) << " Input activity boost enabled: floor=" sv
2715- << config::video. input_activity_boost_fps
2756+ << input_activity_boost_policy. fps
27162757 << " fps, window=" sv << config::video.input_activity_boost_window_ms << " ms" sv;
27172758 }
2759+ else if (input_activity_boost_policy.configured ) {
2760+ BOOST_LOG (info) << " Input activity boost configured but not enabled because it would not raise the current VRR minimum cadence." sv;
2761+ }
27182762
27192763 auto shutdown_event = mail->event <bool >(mail::shutdown);
27202764 auto packets = mail::man->queue <packet_t >(mail::video_packets);
@@ -2806,18 +2850,20 @@ namespace video {
28062850 }
28072851
28082852 consume_input_activity ();
2809- auto input_boost_active = input_activity_boost_enabled && std::chrono::steady_clock::now () < input_boost_until;
2810- auto effective_minimum_frame_time = input_boost_active ?
2811- std::min (base_minimum_frame_time, input_activity_boost_frame_time) :
2812- base_minimum_frame_time;
2853+ auto input_boost_active = input_activity_boost_policy.useful && std::chrono::steady_clock::now () < input_boost_until;
2854+ auto effective_frame_time = effective_minimum_frame_time (
2855+ base_minimum_frame_time,
2856+ input_activity_boost_policy,
2857+ input_boost_active,
2858+ config::video.minimum_fps_target );
28132859
28142860 std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
28152861 bool has_new_frame = false ;
28162862
28172863 // Encode at a minimum FPS to avoid image quality issues with static content
28182864 // When variable_refresh_rate is enabled, only encode when we have a new frame
28192865 if (!requested_idr_frame || images->peek ()) {
2820- if (auto img = pop_image_interruptible (effective_minimum_frame_time, input_activity_boost_enabled && !input_boost_active)) {
2866+ if (auto img = pop_image_interruptible (effective_frame_time, input_activity_boost_policy. useful && !input_boost_active)) {
28212867 frame_timestamp = img->frame_timestamp ;
28222868 if (session->convert (*img)) {
28232869 BOOST_LOG (error) << " Could not convert image" sv;
@@ -2832,7 +2878,7 @@ namespace video {
28322878 }
28332879
28342880 consume_input_activity ();
2835- input_boost_active = input_activity_boost_enabled && std::chrono::steady_clock::now () < input_boost_until;
2881+ input_boost_active = input_activity_boost_policy. useful && std::chrono::steady_clock::now () < input_boost_until;
28362882
28372883 // While streaming check to see if the mouse is present and enable Mouse Keys to force the cursor to appear.
28382884 // Run this BEFORE the VRR early-continue so a KVM switch on a static screen still recovers the cursor
0 commit comments