fix(video): 修复 FFmpeg 硬编码 EAGAIN 刷屏并为编码错误增加日志节流

This commit is contained in:
mofeng-git
2026-03-26 22:30:53 +08:00
parent 46ae0c81e2
commit 200f947b5d
2 changed files with 93 additions and 26 deletions

View File

@@ -33,6 +33,8 @@ const CAPTURE_TIMEOUT_RESTART_THRESHOLD: u32 = 5;
const MIN_CAPTURE_FRAME_SIZE: usize = 128;
/// Validate JPEG header every N frames to reduce overhead
const JPEG_VALIDATE_INTERVAL: u64 = 30;
/// Throttle repeated encoding errors to avoid log flooding
const ENCODE_ERROR_THROTTLE_SECS: u64 = 5;
use crate::error::{AppError, Result};
use crate::utils::LogThrottler;
@@ -161,6 +163,51 @@ impl SharedVideoPipelineConfig {
}
}
fn classify_encode_error(err: &AppError) -> String {
let message = err.to_string();
if message.contains("FFmpeg HW encode failed") {
if message.contains("avcodec_send_packet failed") && message.contains("ret=-11") {
"encode_ffmpeg_hw_send_packet_eagain".to_string()
} else if message.contains("avcodec_send_frame failed") && message.contains("ret=-11") {
"encode_ffmpeg_hw_send_frame_eagain".to_string()
} else if message.contains("avcodec_receive_packet failed") && message.contains("ret=-11") {
"encode_ffmpeg_hw_receive_packet_eagain".to_string()
} else if message.contains("Resource temporarily unavailable") {
"encode_ffmpeg_hw_eagain".to_string()
} else if message.contains("avcodec_send_packet failed") {
"encode_ffmpeg_hw_send_packet".to_string()
} else if message.contains("avcodec_send_frame failed") {
"encode_ffmpeg_hw_send_frame".to_string()
} else if message.contains("avcodec_receive_packet failed") {
"encode_ffmpeg_hw_receive_packet".to_string()
} else {
"encode_ffmpeg_hw".to_string()
}
} else {
format!("encode_{}", message)
}
}
fn log_encoding_error(
throttler: &LogThrottler,
suppressed_errors: &mut HashMap<String, u64>,
err: &AppError,
) {
let key = classify_encode_error(err);
if throttler.should_log(&key) {
let suppressed = suppressed_errors.remove(&key).unwrap_or(0);
if suppressed > 0 {
error!("Encoding failed: {} (suppressed {} repeats)", err, suppressed);
} else {
error!("Encoding failed: {}", err);
}
} else {
let counter = suppressed_errors.entry(key).or_insert(0);
*counter = counter.saturating_add(1);
}
}
/// Pipeline statistics
#[derive(Debug, Clone, Default)]
pub struct SharedVideoPipelineStats {
@@ -1070,6 +1117,8 @@ impl SharedVideoPipeline {
let mut last_fps_time = Instant::now();
let mut fps_frame_count: u64 = 0;
let mut running_rx = pipeline.running_rx.clone();
let encode_error_throttler = LogThrottler::with_secs(ENCODE_ERROR_THROTTLE_SECS);
let mut suppressed_encode_errors: HashMap<String, u64> = HashMap::new();
// Track when we last had subscribers for auto-stop feature
let mut no_subscribers_since: Option<Instant> = None;
@@ -1138,7 +1187,11 @@ impl SharedVideoPipeline {
}
Ok(None) => {}
Err(e) => {
error!("Encoding failed: {}", e);
log_encoding_error(
&encode_error_throttler,
&mut suppressed_encode_errors,
&e,
);
}
}
@@ -1214,6 +1267,8 @@ impl SharedVideoPipeline {
let mut last_fps_time = Instant::now();
let mut fps_frame_count: u64 = 0;
let mut last_seq = *frame_seq_rx.borrow();
let encode_error_throttler = LogThrottler::with_secs(ENCODE_ERROR_THROTTLE_SECS);
let mut suppressed_encode_errors: HashMap<String, u64> = HashMap::new();
while pipeline.running_flag.load(Ordering::Acquire) {
if frame_seq_rx.changed().await.is_err() {
@@ -1258,7 +1313,11 @@ impl SharedVideoPipeline {
}
Ok(None) => {}
Err(e) => {
error!("Encoding failed: {}", e);
log_encoding_error(
&encode_error_throttler,
&mut suppressed_encode_errors,
&e,
);
}
}