feat: v4l2 buffer 由2改为4

This commit is contained in:
mofeng-git
2026-07-05 21:15:44 +08:00
parent bb2691fe8f
commit 16a65289f2
4 changed files with 11 additions and 24 deletions

View File

@@ -3,6 +3,8 @@
pub(crate) mod runtime; pub(crate) mod runtime;
pub(crate) mod status; pub(crate) mod status;
pub const DEFAULT_CAPTURE_BUFFER_COUNT: u32 = 4;
#[cfg(unix)] #[cfg(unix)]
mod linux; mod linux;
#[cfg(windows)] #[cfg(windows)]

View File

@@ -38,7 +38,6 @@ const CSI_BRIDGE_NOSIGNAL_INTERVAL_MS: u64 = 500;
const NOSIGNAL_POLL_MAX: Duration = Duration::from_secs(20); const NOSIGNAL_POLL_MAX: Duration = Duration::from_secs(20);
/// Throttle repeated encoding errors to avoid log flooding /// Throttle repeated encoding errors to avoid log flooding
const ENCODE_ERROR_THROTTLE_SECS: u64 = 5; const ENCODE_ERROR_THROTTLE_SECS: u64 = 5;
const INVALID_MJPEG_LOG_THROTTLE_SECS: u64 = 5;
static PROCESS_START: std::sync::OnceLock<Instant> = std::sync::OnceLock::new(); static PROCESS_START: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
@@ -61,6 +60,7 @@ use crate::video::frame::{FrameBuffer, FrameBufferPool, VideoFrame};
use crate::video::signal::SignalStatus; use crate::video::signal::SignalStatus;
const MIN_CAPTURE_FRAME_SIZE: usize = 128; const MIN_CAPTURE_FRAME_SIZE: usize = 128;
#[cfg(all( #[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"), any(target_arch = "aarch64", target_arch = "arm"),
not(target_os = "android") not(target_os = "android")
@@ -870,8 +870,6 @@ impl SharedVideoPipeline {
let mut sequence: u64 = 0; let mut sequence: u64 = 0;
let mut consecutive_timeouts: u32 = 0; let mut consecutive_timeouts: u32 = 0;
let capture_error_throttler = LogThrottler::with_secs(5); let capture_error_throttler = LogThrottler::with_secs(5);
let invalid_mjpeg_throttler =
LogThrottler::with_secs(INVALID_MJPEG_LOG_THROTTLE_SECS);
let mut suppressed_capture_errors: HashMap<String, u64> = HashMap::new(); let mut suppressed_capture_errors: HashMap<String, u64> = HashMap::new();
while pipeline.running_flag.load(Ordering::Acquire) { while pipeline.running_flag.load(Ordering::Acquire) {
@@ -1232,24 +1230,8 @@ impl SharedVideoPipeline {
} }
owned.truncate(frame_size); owned.truncate(frame_size);
if pixel_format.is_compressed() && !VideoFrame::is_valid_jpeg_bytes(&owned) {
if invalid_mjpeg_throttler.should_log("invalid_mjpeg_capture_frame") {
let b0 = owned.first().copied().unwrap_or_default();
let b1 = owned.get(1).copied().unwrap_or_default();
warn!(
"Dropping invalid MJPEG capture frame: size={}, starts with 0x{:02x} 0x{:02x}",
owned.len(),
b0,
b1
);
}
continue;
}
// Notify streaming only after frame validation passes // Notify streaming only after the short-frame guard passes.
// stale/warm-up frames from V4L2 kernel queues can cause
// DQBUF Ok with invalid data, which would prematurely
// clear the frontend error overlay.
pipeline.notify_state(PipelineStateNotification::streaming()); pipeline.notify_state(PipelineStateNotification::streaming());
let frame = Arc::new(VideoFrame::from_pooled( let frame = Arc::new(VideoFrame::from_pooled(
Arc::new(FrameBuffer::new(owned, Some(buffer_pool.clone()))), Arc::new(FrameBuffer::new(owned, Some(buffer_pool.clone()))),

View File

@@ -27,7 +27,9 @@ use crate::video::capture::status::{
capture_error_log_key, classify_capture_io_error, signal_status_from_capture_kind, capture_error_log_key, classify_capture_io_error, signal_status_from_capture_kind,
CaptureIoErrorKind, CaptureIoErrorKind,
}; };
use crate::video::capture::{is_source_changed_error, BridgeContext, CaptureStream}; use crate::video::capture::{
is_source_changed_error, BridgeContext, CaptureStream, DEFAULT_CAPTURE_BUFFER_COUNT,
};
const MIN_CAPTURE_FRAME_SIZE: usize = 128; const MIN_CAPTURE_FRAME_SIZE: usize = 128;
@@ -769,7 +771,7 @@ impl Streamer {
const MAX_RETRIES: u32 = 5; const MAX_RETRIES: u32 = 5;
const RETRY_DELAY_MS: u64 = 200; const RETRY_DELAY_MS: u64 = 200;
const IDLE_STOP_DELAY_SECS: u64 = 5; const IDLE_STOP_DELAY_SECS: u64 = 5;
const BUFFER_COUNT: u32 = 2; const BUFFER_COUNT: u32 = DEFAULT_CAPTURE_BUFFER_COUNT;
/// Initial back-off after signal loss before the first soft restart. /// Initial back-off after signal loss before the first soft restart.
/// ///
/// PiKVM/ustreamer drops to sub-second recovery because it subscribes to /// PiKVM/ustreamer drops to sub-second recovery because it subscribes to

View File

@@ -12,6 +12,7 @@ use crate::audio::{AudioController, OpusFrame};
use crate::error::{AppError, Result}; use crate::error::{AppError, Result};
use crate::events::{EventBus, StreamDeviceLostKind, SystemEvent}; use crate::events::{EventBus, StreamDeviceLostKind, SystemEvent};
use crate::hid::HidController; use crate::hid::HidController;
use crate::video::capture::DEFAULT_CAPTURE_BUFFER_COUNT;
use crate::video::codec::h264_bitstream; use crate::video::codec::h264_bitstream;
use crate::video::device::{ use crate::video::device::{
enumerate_devices, select_recovery_device, VideoDevice, VideoDeviceRecoveryHint, enumerate_devices, select_recovery_device, VideoDevice, VideoDeviceRecoveryHint,
@@ -319,7 +320,7 @@ impl WebRtcStreamer {
current_capture current_capture
.as_ref() .as_ref()
.map(|capture| capture.buffer_count) .map(|capture| capture.buffer_count)
.unwrap_or(2), .unwrap_or(DEFAULT_CAPTURE_BUFFER_COUNT),
) )
}; };
@@ -745,7 +746,7 @@ impl WebRtcStreamer {
}); });
*self.capture_device.write().await = Some(CaptureDeviceConfig { *self.capture_device.write().await = Some(CaptureDeviceConfig {
device_path, device_path,
buffer_count: 2, buffer_count: DEFAULT_CAPTURE_BUFFER_COUNT,
jpeg_quality, jpeg_quality,
subdev_path, subdev_path,
bridge_kind, bridge_kind,