feat: 支持 rtsp 功能

This commit is contained in:
mofeng-git
2026-02-11 16:06:06 +08:00
parent 261deb1303
commit 3824e57fc5
23 changed files with 2154 additions and 37 deletions

View File

@@ -0,0 +1,193 @@
use crate::config::{AppConfig, RtspCodec, StreamMode};
use crate::error::Result;
use crate::video::encoder::registry::VideoEncoderType;
use crate::video::encoder::VideoCodecType;
use crate::video::VideoStreamManager;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct StreamCodecConstraints {
pub rustdesk_enabled: bool,
pub rtsp_enabled: bool,
pub allowed_webrtc_codecs: Vec<VideoCodecType>,
pub allow_mjpeg: bool,
pub locked_codec: Option<VideoCodecType>,
pub reason: String,
}
#[derive(Debug, Clone)]
pub struct ConstraintEnforcementResult {
pub changed: bool,
pub message: Option<String>,
}
impl StreamCodecConstraints {
pub fn unrestricted() -> Self {
Self {
rustdesk_enabled: false,
rtsp_enabled: false,
allowed_webrtc_codecs: vec![
VideoCodecType::H264,
VideoCodecType::H265,
VideoCodecType::VP8,
VideoCodecType::VP9,
],
allow_mjpeg: true,
locked_codec: None,
reason: "No codec lock active".to_string(),
}
}
pub fn from_config(config: &AppConfig) -> Self {
let rustdesk_enabled = config.rustdesk.enabled;
let rtsp_enabled = config.rtsp.enabled;
if rtsp_enabled {
let locked_codec = match config.rtsp.codec {
RtspCodec::H264 => VideoCodecType::H264,
RtspCodec::H265 => VideoCodecType::H265,
};
return Self {
rustdesk_enabled,
rtsp_enabled,
allowed_webrtc_codecs: vec![locked_codec],
allow_mjpeg: false,
locked_codec: Some(locked_codec),
reason: if rustdesk_enabled {
format!(
"RTSP enabled with codec lock ({:?}) and RustDesk enabled",
locked_codec
)
} else {
format!("RTSP enabled with codec lock ({:?})", locked_codec)
},
};
}
if rustdesk_enabled {
return Self {
rustdesk_enabled,
rtsp_enabled,
allowed_webrtc_codecs: vec![
VideoCodecType::H264,
VideoCodecType::H265,
VideoCodecType::VP8,
VideoCodecType::VP9,
],
allow_mjpeg: false,
locked_codec: None,
reason: "RustDesk enabled, MJPEG disabled".to_string(),
};
}
Self::unrestricted()
}
pub fn is_mjpeg_allowed(&self) -> bool {
self.allow_mjpeg
}
pub fn is_webrtc_codec_allowed(&self, codec: VideoCodecType) -> bool {
self.allowed_webrtc_codecs.contains(&codec)
}
pub fn preferred_webrtc_codec(&self) -> VideoCodecType {
if let Some(codec) = self.locked_codec {
return codec;
}
self.allowed_webrtc_codecs
.first()
.copied()
.unwrap_or(VideoCodecType::H264)
}
pub fn allowed_codecs_for_api(&self) -> Vec<&'static str> {
let mut codecs = Vec::new();
if self.allow_mjpeg {
codecs.push("mjpeg");
}
for codec in &self.allowed_webrtc_codecs {
codecs.push(codec_to_id(*codec));
}
codecs
}
}
pub async fn enforce_constraints_with_stream_manager(
stream_manager: &Arc<VideoStreamManager>,
constraints: &StreamCodecConstraints,
) -> Result<ConstraintEnforcementResult> {
let current_mode = stream_manager.current_mode().await;
if current_mode == StreamMode::Mjpeg && !constraints.allow_mjpeg {
let target_codec = constraints.preferred_webrtc_codec();
stream_manager.set_video_codec(target_codec).await?;
let _ = stream_manager
.switch_mode_transaction(StreamMode::WebRTC)
.await?;
return Ok(ConstraintEnforcementResult {
changed: true,
message: Some(format!(
"Auto-switched from MJPEG to {} due to codec lock",
codec_to_id(target_codec)
)),
});
}
if current_mode == StreamMode::WebRTC {
let current_codec = stream_manager.webrtc_streamer().current_video_codec().await;
if !constraints.is_webrtc_codec_allowed(current_codec) {
let target_codec = constraints.preferred_webrtc_codec();
stream_manager.set_video_codec(target_codec).await?;
return Ok(ConstraintEnforcementResult {
changed: true,
message: Some(format!(
"Auto-switched codec from {} to {} due to codec lock",
codec_to_id(current_codec),
codec_to_id(target_codec)
)),
});
}
}
Ok(ConstraintEnforcementResult {
changed: false,
message: None,
})
}
pub fn codec_to_id(codec: VideoCodecType) -> &'static str {
match codec {
VideoCodecType::H264 => "h264",
VideoCodecType::H265 => "h265",
VideoCodecType::VP8 => "vp8",
VideoCodecType::VP9 => "vp9",
}
}
pub fn encoder_codec_to_id(codec: VideoEncoderType) -> &'static str {
match codec {
VideoEncoderType::H264 => "h264",
VideoEncoderType::H265 => "h265",
VideoEncoderType::VP8 => "vp8",
VideoEncoderType::VP9 => "vp9",
}
}
pub fn video_codec_to_encoder_codec(codec: VideoCodecType) -> VideoEncoderType {
match codec {
VideoCodecType::H264 => VideoEncoderType::H264,
VideoCodecType::H265 => VideoEncoderType::H265,
VideoCodecType::VP8 => VideoEncoderType::VP8,
VideoCodecType::VP9 => VideoEncoderType::VP9,
}
}
pub fn encoder_codec_to_video_codec(codec: VideoEncoderType) -> VideoCodecType {
match codec {
VideoEncoderType::H264 => VideoCodecType::H264,
VideoEncoderType::H265 => VideoCodecType::H265,
VideoEncoderType::VP8 => VideoCodecType::VP8,
VideoEncoderType::VP9 => VideoCodecType::VP9,
}
}

View File

@@ -3,6 +3,7 @@
//! This module provides V4L2 video capture, encoding, and streaming functionality.
pub mod capture;
pub mod codec_constraints;
pub mod convert;
pub mod decoder;
pub mod device;

View File

@@ -37,6 +37,7 @@ use crate::error::Result;
use crate::events::{EventBus, SystemEvent, VideoDeviceInfo};
use crate::hid::HidController;
use crate::stream::MjpegStreamHandler;
use crate::video::codec_constraints::StreamCodecConstraints;
use crate::video::format::{PixelFormat, Resolution};
use crate::video::streamer::{Streamer, StreamerState};
use crate::webrtc::WebRtcStreamer;
@@ -144,6 +145,16 @@ impl VideoStreamManager {
*self.config_store.write().await = Some(config);
}
/// Get current stream codec constraints derived from global configuration.
pub async fn codec_constraints(&self) -> StreamCodecConstraints {
if let Some(ref config_store) = *self.config_store.read().await {
let config = config_store.get();
StreamCodecConstraints::from_config(&config)
} else {
StreamCodecConstraints::unrestricted()
}
}
/// Get current streaming mode
pub async fn current_mode(&self) -> StreamMode {
self.mode.read().await.clone()