diff --git a/src/video/capture/mod.rs b/src/video/capture/mod.rs index 3eb930cd..82e0a7dc 100644 --- a/src/video/capture/mod.rs +++ b/src/video/capture/mod.rs @@ -3,6 +3,8 @@ pub(crate) mod runtime; pub(crate) mod status; +pub const DEFAULT_CAPTURE_BUFFER_COUNT: u32 = 4; + #[cfg(unix)] mod linux; #[cfg(windows)] diff --git a/src/video/pipeline/shared.rs b/src/video/pipeline/shared.rs index f649797d..f0ee968c 100644 --- a/src/video/pipeline/shared.rs +++ b/src/video/pipeline/shared.rs @@ -38,7 +38,6 @@ const CSI_BRIDGE_NOSIGNAL_INTERVAL_MS: u64 = 500; const NOSIGNAL_POLL_MAX: Duration = Duration::from_secs(20); /// Throttle repeated encoding errors to avoid log flooding const ENCODE_ERROR_THROTTLE_SECS: u64 = 5; -const INVALID_MJPEG_LOG_THROTTLE_SECS: u64 = 5; static PROCESS_START: std::sync::OnceLock = std::sync::OnceLock::new(); @@ -61,6 +60,7 @@ use crate::video::frame::{FrameBuffer, FrameBufferPool, VideoFrame}; use crate::video::signal::SignalStatus; const MIN_CAPTURE_FRAME_SIZE: usize = 128; + #[cfg(all( any(target_arch = "aarch64", target_arch = "arm"), not(target_os = "android") @@ -870,8 +870,6 @@ impl SharedVideoPipeline { let mut sequence: u64 = 0; let mut consecutive_timeouts: u32 = 0; 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 = HashMap::new(); while pipeline.running_flag.load(Ordering::Acquire) { @@ -1232,24 +1230,8 @@ impl SharedVideoPipeline { } 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 — - // stale/warm-up frames from V4L2 kernel queues can cause - // DQBUF Ok with invalid data, which would prematurely - // clear the frontend error overlay. + // Notify streaming only after the short-frame guard passes. pipeline.notify_state(PipelineStateNotification::streaming()); let frame = Arc::new(VideoFrame::from_pooled( Arc::new(FrameBuffer::new(owned, Some(buffer_pool.clone()))), diff --git a/src/video/streamer.rs b/src/video/streamer.rs index e0463c90..31c57233 100644 --- a/src/video/streamer.rs +++ b/src/video/streamer.rs @@ -27,7 +27,9 @@ use crate::video::capture::status::{ capture_error_log_key, classify_capture_io_error, signal_status_from_capture_kind, 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; @@ -769,7 +771,7 @@ impl Streamer { const MAX_RETRIES: u32 = 5; const RETRY_DELAY_MS: u64 = 200; 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. /// /// PiKVM/ustreamer drops to sub-second recovery because it subscribes to diff --git a/src/webrtc/webrtc_streamer.rs b/src/webrtc/webrtc_streamer.rs index a0799b21..8a1fd2c6 100644 --- a/src/webrtc/webrtc_streamer.rs +++ b/src/webrtc/webrtc_streamer.rs @@ -12,6 +12,7 @@ use crate::audio::{AudioController, OpusFrame}; use crate::error::{AppError, Result}; use crate::events::{EventBus, StreamDeviceLostKind, SystemEvent}; use crate::hid::HidController; +use crate::video::capture::DEFAULT_CAPTURE_BUFFER_COUNT; use crate::video::codec::h264_bitstream; use crate::video::device::{ enumerate_devices, select_recovery_device, VideoDevice, VideoDeviceRecoveryHint, @@ -319,7 +320,7 @@ impl WebRtcStreamer { current_capture .as_ref() .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 { device_path, - buffer_count: 2, + buffer_count: DEFAULT_CAPTURE_BUFFER_COUNT, jpeg_quality, subdev_path, bridge_kind,