init: MVP remote desktop (agent/viewer/common) with H.264 pipeline

This commit is contained in:
dev-team committed 2026-10-23 09:17:55 +08:00
commit 61e8a75cdd
14 files changed
+3098

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
//! 通用协议与帧封装:控制消息(bincode + 长度前缀)与视频帧格式。
use anyhow::Result;
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
/// 控制通道消息(双向)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ControlMsg {
/// 主控端认证(临时验证码,MVP 明文走 QUIC-TLS 加密通道)
Auth { code: String },
AuthOk { width: u32, height: u32 },
AuthFail(String),
KeyFrameReq,
Ping(u64),
/// t=主控端发送时刻(主控时钟),agent_ts=被控端收到时刻(被控时钟)
/// 两者配合可估计时钟偏移,进而用帧头 ts_ms 计算端到端延迟
Pong { t: u64, agent_ts: u64 },
Input(InputEvent),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MouseButton {
Left,
Right,
Middle,
}
/// 输入事件。鼠标坐标为虚拟桌面归一化 0..=65535;
/// 按键为 Set-1 扫描码,extended 标记扩展键(方向键/Home 等,需要 EXTENDEDKEY 标志)。
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum InputEvent {
MouseMove { x: u16, y: u16 },
MouseDown(MouseButton),
MouseUp(MouseButton),
Wheel { dy: i32 },
KeyDown { scancode: u16, extended: bool },
KeyUp { scancode: u16, extended: bool },
}
/// 视频帧头(载荷为 LZ4 压缩的 BGRA 像素,按行紧凑排列)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VideoFrameHeader {
pub width: u32,
pub height: u32,
pub ts_ms: u64,
}
async fn read_u32le<R: AsyncReadExt + Unpin>(r: &mut R) -> Result<u32> {
let mut b = [0u8; 4];
r.read_exact(&mut b).await?;
Ok(u32::from_le_bytes(b))
}
/// 编码一条控制消息为「长度前缀 + bincode」
pub fn encode_control(msg: &ControlMsg) -> Vec<u8> {
let body = bincode::serialize(msg).expect("serialize control");
let mut out = Vec::with_capacity(body.len() + 4);
out.extend_from_slice(&(body.len() as u32).to_le_bytes());
out.extend_from_slice(&body);
out
}
pub async fn write_control<W: AsyncWriteExt + Unpin>(
w: &mut W,
msg: &ControlMsg,
) -> Result<()> {
w.write_all(&encode_control(msg)).await?;
Ok(())
}
pub async fn read_control<R: AsyncReadExt + Unpin>(r: &mut R) -> Result<ControlMsg> {
let len = read_u32le(r).await? as usize;
anyhow::ensure!(len < 1 << 20, "control frame too large: {len}");
let mut buf = vec![0u8; len];
r.read_exact(&mut buf).await?;
Ok(bincode::deserialize(&buf)?)
}
/// 编码视频帧:[总长 u32][头长 u32][bincode 头][LZ4 载荷]
pub fn encode_video_frame(header: &VideoFrameHeader, payload: &[u8]) -> Vec<u8> {
let hb = bincode::serialize(header).expect("serialize header");
let total = 4 + hb.len() + payload.len();
let mut out = Vec::with_capacity(total + 4);
out.extend_from_slice(&(total as u32).to_le_bytes());
out.extend_from_slice(&(hb.len() as u32).to_le_bytes());
out.extend_from_slice(&hb);
out.extend_from_slice(payload);
out
}
pub async fn read_video_frame<R: AsyncReadExt + Unpin>(
r: &mut R,
) -> Result<(VideoFrameHeader, Vec<u8>)> {
let total = read_u32le(r).await? as usize;
anyhow::ensure!(total < 256 << 20, "video frame too large: {total}");
let mut buf = vec![0u8; total];
r.read_exact(&mut buf).await?;
let hlen = u32::from_le_bytes(buf[0..4].try_into().unwrap()) as usize;
let header: VideoFrameHeader = bincode::deserialize(&buf[4..4 + hlen])?;
Ok((header, buf[4 + hlen..].to_vec()))
}