From 22000245bccd4cda8bfebe6ea54a74163ccc5830 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:06:26 +0000 Subject: [PATCH 1/5] Initial plan From 8c8e6deb185af1462e0d4920434230695c307520 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:10:44 +0000 Subject: [PATCH 2/5] Increase H264/H265 bitrate multipliers to improve image quality - H265: Increase base multiplier from 1.5x to 2.0x (33% more bitrate) - H264: Increase base multiplier from 2.0x to 2.5x (25% more bitrate) - Also adjusted high-bitrate decay factors proportionally - At 1080p Balanced quality: H265 now uses ~2777 kbps (was 2084) - Fixes issue where H265 had worse quality than VP8 software codec Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com> --- libs/scrap/src/common/hwcodec.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 17eda7f3c..f3d28d953 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -264,17 +264,21 @@ impl HwRamEncoder { 5.0 } } else if h264 { + // Increased base multiplier from 2.0 to 2.5 to improve image quality + // while maintaining H264's compression efficiency + if base > threshold { + 1.0 + 1.5 / (1.0 + (base - threshold) * decay_rate) + } else { + 2.5 + } + } else { + // H265: Increased base multiplier from 1.5 to 2.0 to fix poor image quality + // H265 should be more efficient than H264, but needs sufficient bitrate if base > threshold { 1.0 + 1.0 / (1.0 + (base - threshold) * decay_rate) } else { 2.0 } - } else { - if base > threshold { - 1.0 + 0.5 / (1.0 + (base - threshold) * decay_rate) - } else { - 1.5 - } }; (base * factor) as u32 } From eacfdaed61b9d0ee5f7dd58c3d25406bafc871eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:11:54 +0000 Subject: [PATCH 3/5] Add unit tests for H264/H265 bitrate calculations - Test validates correct bitrate values at different quality levels - Verifies H265 balanced quality uses ~2777 kbps (33% increase from old value) - Verifies H264 balanced quality uses ~3472 kbps (25% increase from old value) - Tests resolution scaling and quality level differences Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com> --- libs/scrap/src/common/hwcodec.rs | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f3d28d953..dffe1fc06 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -765,3 +765,53 @@ pub fn start_check_process() { std::thread::spawn(f); }); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_h264_h265_bitrate_calculation() { + // Test with 1920x1080 resolution (base bitrate = 2073 kbps) + let width = 1920; + let height = 1080; + + // Test with BR_BALANCED (0.67) - default quality setting + let balanced_ratio = 0.67; + let h264_balanced = HwRamEncoder::calc_bitrate(width, height, balanced_ratio, true); + let h265_balanced = HwRamEncoder::calc_bitrate(width, height, balanced_ratio, false); + + // H265 should get ~2777 kbps with new multiplier (was ~2084 with old 1.5x) + assert!(h265_balanced >= 2700 && h265_balanced <= 2850, + "H265 balanced bitrate should be ~2777 kbps, got {} kbps", h265_balanced); + + // H264 should get ~3472 kbps with new multiplier (was ~2778 with old 2.0x) + assert!(h264_balanced >= 3400 && h264_balanced <= 3550, + "H264 balanced bitrate should be ~3472 kbps, got {} kbps", h264_balanced); + + // H264 should have higher bitrate than H265 at same quality + assert!(h264_balanced > h265_balanced, + "H264 should have higher bitrate than H265"); + + // Test with BR_BEST (1.5) - best quality setting + let best_ratio = 1.5; + let h265_best = HwRamEncoder::calc_bitrate(width, height, best_ratio, false); + + // At best quality, should use significantly more bitrate + assert!(h265_best > h265_balanced * 1.5, + "Best quality should use >50% more bitrate than balanced"); + + // Test with BR_SPEED (0.5) - low quality setting + let speed_ratio = 0.5; + let h265_speed = HwRamEncoder::calc_bitrate(width, height, speed_ratio, false); + + // At speed quality, should use less bitrate + assert!(h265_speed < h265_balanced, + "Speed quality should use less bitrate than balanced"); + + // Verify bitrate scales proportionally with resolution + let hd_bitrate = HwRamEncoder::calc_bitrate(1280, 720, balanced_ratio, false); + assert!(hd_bitrate < h265_balanced, + "720p should use less bitrate than 1080p"); + } +} From b3957febe1501dab05fc8be1ee956fc4d694f2b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:14:02 +0000 Subject: [PATCH 4/5] Fix test compilation issues from code review - Fix type error: Cast u32 to f64 for float multiplication - Clarify comment: Change 'base bitrate' to 'base_bitrate() returns' for accuracy Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com> --- libs/scrap/src/common/hwcodec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index dffe1fc06..67c2121ec 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -772,7 +772,7 @@ mod tests { #[test] fn test_h264_h265_bitrate_calculation() { - // Test with 1920x1080 resolution (base bitrate = 2073 kbps) + // Test with 1920x1080 resolution (base_bitrate() returns 2073 kbps for 1080p) let width = 1920; let height = 1080; @@ -797,8 +797,8 @@ mod tests { let best_ratio = 1.5; let h265_best = HwRamEncoder::calc_bitrate(width, height, best_ratio, false); - // At best quality, should use significantly more bitrate - assert!(h265_best > h265_balanced * 1.5, + // At best quality, should use significantly more bitrate (>50% more) + assert!((h265_best as f64) > (h265_balanced as f64 * 1.5), "Best quality should use >50% more bitrate than balanced"); // Test with BR_SPEED (0.5) - low quality setting From de11e95598194a9b3c09f7caf0ba06a70158f744 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 15:32:01 +0000 Subject: [PATCH 5/5] Add hardware-specific diagnostics for H265 encoder quality monitoring - Log detailed encoder info at creation (name, bitrate, resolution, GPU signature) - Add runtime frame size monitoring to detect quality issues - Track average frame size vs expected and log warnings if ratio is abnormal (<0.3 or >3.0) - Log quality changes with before/after bitrate values - Add documentation about hardware-specific quality variations - Reset statistics on quality changes to ensure accurate measurements This helps diagnose hardware-specific H265 quality issues where some GPUs produce poor quality while others work fine. Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com> --- libs/scrap/src/common/hwcodec.rs | 106 +++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 67c2121ec..6c5d41ad9 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -55,6 +55,10 @@ pub struct HwRamEncoder { pub pixfmt: AVPixelFormat, bitrate: u32, //kbs config: HwRamEncoderConfig, + // Frame statistics for quality monitoring + frame_count: u64, + total_frame_size: u64, + last_quality_log: std::time::Instant, } impl EncoderApi for HwRamEncoder { @@ -94,13 +98,35 @@ impl EncoderApi for HwRamEncoder { } }; match Encoder::new(ctx.clone()) { - Ok(encoder) => Ok(HwRamEncoder { - encoder, - format, - pixfmt: ctx.pixfmt, - bitrate, - config, - }), + Ok(encoder) => { + // Log detailed encoder information for diagnostics + log::info!( + "Hardware encoder created successfully: name='{}', format={:?}, resolution={}x{}, bitrate={} kbps, fps={}, gop={}, rate_control={:?}", + config.name, + format, + config.width, + config.height, + bitrate, + DEFAULT_FPS, + gop, + rc + ); + // Log GPU signature for hardware-specific issue tracking + let gpu_sig = hwcodec::common::get_gpu_signature(); + if !gpu_sig.is_empty() { + log::info!("GPU signature: {}", gpu_sig); + } + Ok(HwRamEncoder { + encoder, + format, + pixfmt: ctx.pixfmt, + bitrate, + config, + frame_count: 0, + total_frame_size: 0, + last_quality_log: std::time::Instant::now(), + }) + } Err(_) => Err(anyhow!(format!("Failed to create encoder"))), } } @@ -171,6 +197,7 @@ impl EncoderApi for HwRamEncoder { } fn set_quality(&mut self, ratio: f32) -> ResultType<()> { + let old_bitrate = self.bitrate; let mut bitrate = Self::bitrate( &self.config.name, self.config.width, @@ -181,6 +208,22 @@ impl EncoderApi for HwRamEncoder { bitrate = Self::check_bitrate_range(&self.config, bitrate); self.encoder.set_bitrate(bitrate as _).ok(); self.bitrate = bitrate; + + // Log quality changes for hardware-specific diagnostics + if old_bitrate != bitrate { + log::info!( + "Hardware encoder quality changed: encoder='{}', ratio={:.2}, bitrate {} -> {} kbps", + self.config.name, + ratio, + old_bitrate, + bitrate + ); + } + + // Reset statistics on quality change + self.frame_count = 0; + self.total_frame_size = 0; + self.last_quality_log = std::time::Instant::now(); } self.config.quality = ratio; Ok(()) @@ -234,6 +277,43 @@ impl HwRamEncoder { Ok(v) => { let mut data = Vec::::new(); data.append(v); + + // Monitor encoding quality by tracking frame sizes + if !data.is_empty() { + self.frame_count += data.len() as u64; + let frame_sizes: u64 = data.iter().map(|f| f.data.len() as u64).sum(); + self.total_frame_size += frame_sizes; + + // Log quality statistics every 300 frames (10 seconds at 30fps) + if self.frame_count % 300 == 0 && self.last_quality_log.elapsed().as_secs() >= 10 { + let avg_frame_size = self.total_frame_size / self.frame_count; + let expected_frame_size = (self.bitrate as u64 * 1000) / (8 * DEFAULT_FPS as u64); + + // Log if actual frame size is significantly different from expected + let ratio = avg_frame_size as f64 / expected_frame_size as f64; + if ratio < 0.3 || ratio > 3.0 { + log::warn!( + "Hardware encoder quality issue detected: encoder='{}', avg_frame_size={} bytes, expected={} bytes, ratio={:.2}, bitrate={} kbps", + self.config.name, + avg_frame_size, + expected_frame_size, + ratio, + self.bitrate + ); + } else { + log::debug!( + "Hardware encoder stats: encoder='{}', frames={}, avg_size={} bytes, expected={} bytes, ratio={:.2}", + self.config.name, + self.frame_count, + avg_frame_size, + expected_frame_size, + ratio + ); + } + self.last_quality_log = std::time::Instant::now(); + } + } + Ok(data) } Err(_) => Ok(Vec::::new()), @@ -252,6 +332,18 @@ impl HwRamEncoder { Self::calc_bitrate(width, height, ratio, name.contains("h264")) } + /// Calculate bitrate for hardware encoders based on resolution and quality ratio. + /// + /// NOTE: Hardware encoder quality can vary significantly across different GPUs/drivers. + /// Some hardware may require higher bitrates than others to achieve acceptable quality. + /// The multipliers below provide a baseline, but specific hardware (especially older + /// GPUs or certain driver versions) may still produce poor quality output even with + /// these settings. Monitor logs for "Hardware encoder quality issue detected" warnings. + /// + /// If quality issues persist on specific hardware: + /// - Check GPU driver version and update if needed + /// - Consider forcing VP8/VP9 software codec as fallback + /// - File bug report with GPU model and driver version pub fn calc_bitrate(width: usize, height: usize, ratio: f32, h264: bool) -> u32 { let base = base_bitrate(width as _, height as _) as f32 * ratio; let threshold = 2000.0;