mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 08:20:49 +08:00
fix: 修复无信号状态同步与采集管线阻塞
- 使用非阻塞 V4L2 句柄并类型化采集错误 - 完善视频管线生命周期,避免复用停止中的管线 - 区分音视频事件并支持 REST/WebSocket 状态快照 - 修复信号丢失后保留最后一帧及刷新后黑屏问题 - 优化无信号提示、状态展示优先级及相关界面细节 - 降低重复 OTG 错误日志级别
This commit is contained in:
@@ -19,7 +19,7 @@ use super::device::{
|
||||
use super::format::{PixelFormat, Resolution};
|
||||
use super::frame::{FrameBuffer, FrameBufferPool, VideoFrame};
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::events::{EventBus, StreamDeviceLostKind, SystemEvent};
|
||||
use crate::events::{EventBus, StreamKind, SystemEvent};
|
||||
use crate::stream::MjpegStreamHandler;
|
||||
use crate::utils::LogThrottler;
|
||||
use crate::video::capture::runtime::open_capture_stream;
|
||||
@@ -28,7 +28,7 @@ use crate::video::capture::status::{
|
||||
CaptureIoErrorKind,
|
||||
};
|
||||
use crate::video::capture::{
|
||||
is_source_changed_error, BridgeContext, CaptureStream, DEFAULT_CAPTURE_BUFFER_COUNT,
|
||||
BridgeContext, CaptureReadError, CaptureStream, DEFAULT_CAPTURE_BUFFER_COUNT,
|
||||
};
|
||||
use crate::video::recovery::{wait_for_source_change, CaptureRecoveryPolicy};
|
||||
|
||||
@@ -252,6 +252,7 @@ impl Streamer {
|
||||
let next = self.next_retry_ms.load(Ordering::Relaxed);
|
||||
|
||||
SystemEvent::StreamStateChanged {
|
||||
kind: StreamKind::Video,
|
||||
state: external.to_string(),
|
||||
device,
|
||||
reason: reason.map(|s| s.to_string()),
|
||||
@@ -1067,16 +1068,19 @@ impl Streamer {
|
||||
let mut owned = buffer_pool.take(MIN_CAPTURE_FRAME_SIZE);
|
||||
let meta = match stream.next_into(&mut owned) {
|
||||
Ok(meta) => meta,
|
||||
Err(e) => {
|
||||
if is_source_changed_error(&e) {
|
||||
info!("Capture SOURCE_CHANGE — soft-restart for DV re-probe");
|
||||
let delay = recovery_policy
|
||||
.retry_delay(no_signal_restart_count.saturating_add(1));
|
||||
set_retry(delay.as_millis() as u64);
|
||||
go_offline();
|
||||
set_state(StreamerState::NoSignal);
|
||||
need_soft_restart = true;
|
||||
break 'capture;
|
||||
Err(CaptureReadError::SourceChanged) => {
|
||||
info!("Capture SOURCE_CHANGE — soft-restart for DV re-probe");
|
||||
let delay =
|
||||
recovery_policy.retry_delay(no_signal_restart_count.saturating_add(1));
|
||||
set_retry(delay.as_millis() as u64);
|
||||
go_offline();
|
||||
set_state(StreamerState::NoSignal);
|
||||
need_soft_restart = true;
|
||||
break 'capture;
|
||||
}
|
||||
Err(CaptureReadError::Io(e)) => {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
continue 'capture;
|
||||
}
|
||||
if e.kind() == std::io::ErrorKind::TimedOut {
|
||||
if signal_present {
|
||||
@@ -1308,9 +1312,11 @@ impl Streamer {
|
||||
pub async fn stats(&self) -> StreamerStats {
|
||||
let config = self.config.read().await;
|
||||
let fps = self.current_fps.load(Ordering::Relaxed) as f32 / 100.0;
|
||||
let (state, reason) = self.state().await.external_state();
|
||||
|
||||
StreamerStats {
|
||||
state: self.state().await,
|
||||
state: state.to_string(),
|
||||
reason: reason.map(str::to_string),
|
||||
device: self.current_device().await.map(|d| d.name),
|
||||
format: Some(config.format.to_string()),
|
||||
resolution: Some((config.resolution.width, config.resolution.height)),
|
||||
@@ -1402,7 +1408,7 @@ impl Streamer {
|
||||
|
||||
// Publish device lost event
|
||||
self.publish_event(SystemEvent::StreamDeviceLost {
|
||||
kind: StreamDeviceLostKind::Video,
|
||||
kind: StreamKind::Video,
|
||||
device: device.clone(),
|
||||
reason: reason.clone(),
|
||||
})
|
||||
@@ -1551,7 +1557,9 @@ impl Default for Streamer {
|
||||
/// Streamer statistics
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct StreamerStats {
|
||||
pub state: StreamerState,
|
||||
pub state: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reason: Option<String>,
|
||||
pub device: Option<String>,
|
||||
pub format: Option<String>,
|
||||
pub resolution: Option<(u32, u32)>,
|
||||
|
||||
Reference in New Issue
Block a user