fix(avhw): handle tx.send() failure and pause encoding on WebRTC disconnect (closes #6)

- Replace 'let _ = tx.send()' with proper error handling: log warning,
  set webrtc_disconnected flag, and break drain loop on SendError
- Add Arc<AtomicBool> webrtc_paused shared between State/StatePortal
  and SwEncState, synced from wrtc.is_connected() in poll_webrtc()
- Skip encoding in encode_filtered_frame() when paused or disconnected
- Drain and discard stale channel frames on disconnect
- Resume encoding automatically on WebRTC reconnection
This commit is contained in:
dailz
2026-06-06 15:12:49 +08:00
parent fd170b66d9
commit 226768c3e3
3 changed files with 163 additions and 47 deletions

View File

@@ -3,6 +3,8 @@ use std::mem;
use std::os::fd::{AsFd, OwnedFd};
use std::os::unix::io::FromRawFd;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Instant;
use anyhow::Result;
@@ -223,6 +225,7 @@ pub struct State<S: CaptureSource> {
pub webrtc_tx: Option<crossbeam_channel::Sender<Vec<u8>>>,
webrtc_rx: Option<crossbeam_channel::Receiver<Vec<u8>>>,
webrtc_frames_sent: u64,
webrtc_paused: Option<Arc<AtomicBool>>,
}
// ---------------------------------------------------------------------------
@@ -273,12 +276,14 @@ impl<S: CaptureSource> State<S> {
let fps = args.fps;
let drm_device = args.drm_device.as_ref().map(PathBuf::from);
let (webrtc, webrtc_tx, webrtc_rx) = if args.port > 0 {
let (webrtc, webrtc_tx, webrtc_rx, webrtc_paused) = if args.port > 0 {
let (tx, rx) = crossbeam_channel::bounded(32);
let wrtc = WebRtcState::new(args.port, args.fps)?;
(Some(wrtc), Some(tx), Some(rx))
// paused=true until first WebRTC client connects
let paused = Arc::new(AtomicBool::new(true));
(Some(wrtc), Some(tx), Some(rx), Some(paused))
} else {
(None, None, None)
(None, None, None, None)
};
let mut state = Self {
@@ -309,6 +314,7 @@ impl<S: CaptureSource> State<S> {
webrtc_tx,
webrtc_rx,
webrtc_frames_sent: 0,
webrtc_paused,
};
// registry_queue_init consumes registry events internally during its
@@ -641,9 +647,25 @@ impl<S: CaptureSource> State<S> {
wrtc.handle_signaling()?;
wrtc.poll_and_feed()?;
let connected = wrtc.is_connected();
if let Some(ref paused) = self.webrtc_paused {
let was_paused = paused.load(Ordering::Relaxed);
let now_paused = !connected;
if was_paused && !now_paused {
tracing::info!("WebRTC client connected, resuming encoding");
} else if !was_paused && now_paused {
tracing::warn!("WebRTC client disconnected, pausing encoding");
}
paused.store(now_paused, Ordering::Relaxed);
}
if let Some(ref rx) = self.webrtc_rx {
let mut count = 0u32;
while let Ok(data) = rx.try_recv() {
if !connected {
continue;
}
count += 1;
if let Err(e) = wrtc.write_h264_frame(&data, self.webrtc_frames_sent, self.args.fps) {
tracing::debug!("WebRTC write frame error: {e}");
@@ -703,6 +725,7 @@ impl<S: CaptureSource> State<S> {
bitrate,
actual_gop_size,
tx.clone(),
self.webrtc_paused.as_ref().expect("webrtc_paused must exist when webrtc_tx exists").clone(),
) {
Ok(enc) => StreamingEncoder::WebRtc(enc),
Err(e) => {