feat(rustdesk): 优化视频编码协商和添加公共服务器支持

- 调整视频编码优先级为 H264 > H265 > VP8 > VP9,优先使用硬件编码
- 对接 RustDesk 客户端质量预设 (Low/Balanced/Best) 到 BitratePreset
- 添加 secrets.toml 编译时读取机制,支持配置公共服务器
- 默认公共服务器: rustdesk.mofeng.run:21116
- 前端 ID 服务器输入框添加问号提示,显示公共服务器信息
- 用户留空时自动使用公共服务器
This commit is contained in:
mofeng-git
2026-01-02 17:22:34 +08:00
parent be4de59f3b
commit 28ecf951df
29 changed files with 776 additions and 316 deletions

View File

@@ -109,11 +109,11 @@ pub async fn apply_stream_config(
}
// 更新码率
if old_config.bitrate_kbps != new_config.bitrate_kbps {
if old_config.bitrate_preset != new_config.bitrate_preset {
state
.stream_manager
.webrtc_streamer()
.set_bitrate(new_config.bitrate_kbps)
.set_bitrate_preset(new_config.bitrate_preset)
.await
.ok(); // Ignore error if no active stream
}
@@ -143,9 +143,9 @@ pub async fn apply_stream_config(
}
tracing::info!(
"Stream config applied: encoder={:?}, bitrate={} kbps",
"Stream config applied: encoder={:?}, bitrate={}",
new_config.encoder,
new_config.bitrate_kbps
new_config.bitrate_preset
);
Ok(())
}

View File

@@ -4,7 +4,7 @@ use axum::{extract::State, Json};
use std::sync::Arc;
use crate::error::Result;
use crate::rustdesk::config::RustDeskConfig;
use crate::rustdesk::config::{PublicServerInfo, RustDeskConfig};
use crate::state::AppState;
use super::apply::apply_rustdesk_config;
@@ -21,6 +21,8 @@ pub struct RustDeskConfigResponse {
pub has_password: bool,
/// 是否已设置密钥对
pub has_keypair: bool,
/// 是否使用公共服务器(用户留空时)
pub using_public_server: bool,
}
impl From<&RustDeskConfig> for RustDeskConfigResponse {
@@ -32,6 +34,7 @@ impl From<&RustDeskConfig> for RustDeskConfigResponse {
device_id: config.device_id.clone(),
has_password: !config.device_password.is_empty(),
has_keypair: config.public_key.is_some() && config.private_key.is_some(),
using_public_server: config.is_using_public_server(),
}
}
}
@@ -42,6 +45,8 @@ pub struct RustDeskStatusResponse {
pub config: RustDeskConfigResponse,
pub service_status: String,
pub rendezvous_status: Option<String>,
/// 公共服务器信息(仅当有公共服务器配置时返回)
pub public_server: Option<PublicServerInfo>,
}
/// 获取 RustDesk 配置
@@ -65,10 +70,14 @@ pub async fn get_rustdesk_status(State(state): State<Arc<AppState>>) -> Json<Rus
}
};
// 获取公共服务器信息
let public_server = RustDeskConfig::public_server_info();
Json(RustDeskStatusResponse {
config: RustDeskConfigResponse::from(&config),
service_status,
rendezvous_status,
public_server,
})
}

View File

@@ -3,6 +3,7 @@ use typeshare::typeshare;
use crate::config::*;
use crate::error::AppError;
use crate::rustdesk::config::RustDeskConfig;
use crate::video::encoder::BitratePreset;
// ===== Video Config =====
#[typeshare]
@@ -71,8 +72,7 @@ impl VideoConfigUpdate {
pub struct StreamConfigResponse {
pub mode: StreamMode,
pub encoder: EncoderType,
pub bitrate_kbps: u32,
pub gop_size: u32,
pub bitrate_preset: BitratePreset,
pub stun_server: Option<String>,
pub turn_server: Option<String>,
pub turn_username: Option<String>,
@@ -85,8 +85,7 @@ impl From<&StreamConfig> for StreamConfigResponse {
Self {
mode: config.mode.clone(),
encoder: config.encoder.clone(),
bitrate_kbps: config.bitrate_kbps,
gop_size: config.gop_size,
bitrate_preset: config.bitrate_preset,
stun_server: config.stun_server.clone(),
turn_server: config.turn_server.clone(),
turn_username: config.turn_username.clone(),
@@ -100,8 +99,7 @@ impl From<&StreamConfig> for StreamConfigResponse {
pub struct StreamConfigUpdate {
pub mode: Option<StreamMode>,
pub encoder: Option<EncoderType>,
pub bitrate_kbps: Option<u32>,
pub gop_size: Option<u32>,
pub bitrate_preset: Option<BitratePreset>,
/// STUN server URL (e.g., "stun:stun.l.google.com:19302")
pub stun_server: Option<String>,
/// TURN server URL (e.g., "turn:turn.example.com:3478")
@@ -114,16 +112,7 @@ pub struct StreamConfigUpdate {
impl StreamConfigUpdate {
pub fn validate(&self) -> crate::error::Result<()> {
if let Some(bitrate) = self.bitrate_kbps {
if !(1000..=15000).contains(&bitrate) {
return Err(AppError::BadRequest("Bitrate must be 1000-15000 kbps".into()));
}
}
if let Some(gop) = self.gop_size {
if !(10..=300).contains(&gop) {
return Err(AppError::BadRequest("GOP size must be 10-300".into()));
}
}
// BitratePreset is always valid (enum)
// Validate STUN server format
if let Some(ref stun) = self.stun_server {
if !stun.is_empty() && !stun.starts_with("stun:") {
@@ -150,11 +139,8 @@ impl StreamConfigUpdate {
if let Some(encoder) = self.encoder.clone() {
config.encoder = encoder;
}
if let Some(bitrate) = self.bitrate_kbps {
config.bitrate_kbps = bitrate;
}
if let Some(gop) = self.gop_size {
config.gop_size = gop;
if let Some(preset) = self.bitrate_preset {
config.bitrate_preset = preset;
}
// STUN/TURN settings - empty string means clear, Some("value") means set
if let Some(ref stun) = self.stun_server {

View File

@@ -13,6 +13,7 @@ use crate::auth::{Session, SESSION_COOKIE};
use crate::config::{AppConfig, StreamMode};
use crate::error::{AppError, Result};
use crate::state::AppState;
use crate::video::encoder::BitratePreset;
// ============================================================================
// Health & Info
@@ -742,12 +743,12 @@ pub async fn update_config(
state
.stream_manager
.webrtc_streamer()
.set_bitrate(new_config.stream.bitrate_kbps)
.set_bitrate_preset(new_config.stream.bitrate_preset)
.await
.ok(); // Ignore error if no active stream
tracing::info!("Stream config applied: encoder={:?}, bitrate={} kbps",
new_config.stream.encoder, new_config.stream.bitrate_kbps);
tracing::info!("Stream config applied: encoder={:?}, bitrate={}",
new_config.stream.encoder, new_config.stream.bitrate_preset);
}
// HID config processing - always reload if section was sent
@@ -1191,7 +1192,7 @@ pub struct AvailableCodecsResponse {
/// Set bitrate request
#[derive(Deserialize)]
pub struct SetBitrateRequest {
pub bitrate_kbps: u32,
pub bitrate_preset: BitratePreset,
}
/// Set stream bitrate (real-time adjustment)
@@ -1199,19 +1200,11 @@ pub async fn stream_set_bitrate(
State(state): State<Arc<AppState>>,
Json(req): Json<SetBitrateRequest>,
) -> Result<Json<LoginResponse>> {
// Validate bitrate range (1000-15000 kbps)
if req.bitrate_kbps < 1000 || req.bitrate_kbps > 15000 {
return Err(AppError::BadRequest(format!(
"Bitrate must be between 1000 and 15000 kbps, got {}",
req.bitrate_kbps
)));
}
// Update config
state
.config
.update(|config| {
config.stream.bitrate_kbps = req.bitrate_kbps;
config.stream.bitrate_preset = req.bitrate_preset;
})
.await?;
@@ -1219,18 +1212,18 @@ pub async fn stream_set_bitrate(
if let Err(e) = state
.stream_manager
.webrtc_streamer()
.set_bitrate(req.bitrate_kbps)
.set_bitrate_preset(req.bitrate_preset)
.await
{
warn!("Failed to set bitrate dynamically: {}", e);
// Don't fail the request - config is saved, will apply on next connection
} else {
info!("Bitrate updated to {} kbps", req.bitrate_kbps);
info!("Bitrate updated to {}", req.bitrate_preset);
}
Ok(Json(LoginResponse {
success: true,
message: Some(format!("Bitrate set to {} kbps", req.bitrate_kbps)),
message: Some(format!("Bitrate set to {}", req.bitrate_preset)),
}))
}