mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-21 12:11:06 +03:00
Compare commits
5 Commits
master
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de11e95598 | ||
|
|
b3957febe1 | ||
|
|
eacfdaed61 | ||
|
|
8c8e6deb18 | ||
|
|
22000245bc |
@@ -55,6 +55,10 @@ pub struct HwRamEncoder {
|
|||||||
pub pixfmt: AVPixelFormat,
|
pub pixfmt: AVPixelFormat,
|
||||||
bitrate: u32, //kbs
|
bitrate: u32, //kbs
|
||||||
config: HwRamEncoderConfig,
|
config: HwRamEncoderConfig,
|
||||||
|
// Frame statistics for quality monitoring
|
||||||
|
frame_count: u64,
|
||||||
|
total_frame_size: u64,
|
||||||
|
last_quality_log: std::time::Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncoderApi for HwRamEncoder {
|
impl EncoderApi for HwRamEncoder {
|
||||||
@@ -94,13 +98,35 @@ impl EncoderApi for HwRamEncoder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
match Encoder::new(ctx.clone()) {
|
match Encoder::new(ctx.clone()) {
|
||||||
Ok(encoder) => Ok(HwRamEncoder {
|
Ok(encoder) => {
|
||||||
encoder,
|
// Log detailed encoder information for diagnostics
|
||||||
format,
|
log::info!(
|
||||||
pixfmt: ctx.pixfmt,
|
"Hardware encoder created successfully: name='{}', format={:?}, resolution={}x{}, bitrate={} kbps, fps={}, gop={}, rate_control={:?}",
|
||||||
bitrate,
|
config.name,
|
||||||
config,
|
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"))),
|
Err(_) => Err(anyhow!(format!("Failed to create encoder"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,6 +197,7 @@ impl EncoderApi for HwRamEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_quality(&mut self, ratio: f32) -> ResultType<()> {
|
fn set_quality(&mut self, ratio: f32) -> ResultType<()> {
|
||||||
|
let old_bitrate = self.bitrate;
|
||||||
let mut bitrate = Self::bitrate(
|
let mut bitrate = Self::bitrate(
|
||||||
&self.config.name,
|
&self.config.name,
|
||||||
self.config.width,
|
self.config.width,
|
||||||
@@ -181,6 +208,22 @@ impl EncoderApi for HwRamEncoder {
|
|||||||
bitrate = Self::check_bitrate_range(&self.config, bitrate);
|
bitrate = Self::check_bitrate_range(&self.config, bitrate);
|
||||||
self.encoder.set_bitrate(bitrate as _).ok();
|
self.encoder.set_bitrate(bitrate as _).ok();
|
||||||
self.bitrate = bitrate;
|
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;
|
self.config.quality = ratio;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -234,6 +277,43 @@ impl HwRamEncoder {
|
|||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
let mut data = Vec::<EncodeFrame>::new();
|
let mut data = Vec::<EncodeFrame>::new();
|
||||||
data.append(v);
|
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)
|
Ok(data)
|
||||||
}
|
}
|
||||||
Err(_) => Ok(Vec::<EncodeFrame>::new()),
|
Err(_) => Ok(Vec::<EncodeFrame>::new()),
|
||||||
@@ -252,6 +332,18 @@ impl HwRamEncoder {
|
|||||||
Self::calc_bitrate(width, height, ratio, name.contains("h264"))
|
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 {
|
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 base = base_bitrate(width as _, height as _) as f32 * ratio;
|
||||||
let threshold = 2000.0;
|
let threshold = 2000.0;
|
||||||
@@ -264,17 +356,21 @@ impl HwRamEncoder {
|
|||||||
5.0
|
5.0
|
||||||
}
|
}
|
||||||
} else if h264 {
|
} 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 {
|
if base > threshold {
|
||||||
1.0 + 1.0 / (1.0 + (base - threshold) * decay_rate)
|
1.0 + 1.0 / (1.0 + (base - threshold) * decay_rate)
|
||||||
} else {
|
} else {
|
||||||
2.0
|
2.0
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if base > threshold {
|
|
||||||
1.0 + 0.5 / (1.0 + (base - threshold) * decay_rate)
|
|
||||||
} else {
|
|
||||||
1.5
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
(base * factor) as u32
|
(base * factor) as u32
|
||||||
}
|
}
|
||||||
@@ -761,3 +857,53 @@ pub fn start_check_process() {
|
|||||||
std::thread::spawn(f);
|
std::thread::spawn(f);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h264_h265_bitrate_calculation() {
|
||||||
|
// Test with 1920x1080 resolution (base_bitrate() returns 2073 kbps for 1080p)
|
||||||
|
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 (>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
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user