跨平台Desk
PoixsDesk 是一个基于 WebRTC 技术的远程桌面/云电脑系统,支持通过 Web 浏览器远程访问和控制 Windows 桌面。该系统实现了低延迟的屏幕共享、音视频传输和输入控制功能。
┌─────────────────────────────────────────────────────────────────┐
│ PoixsDesk 系统架构 │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ Web 客户端 │ │ 桌面端程序 │
│ │ │ │
│ - 浏览器播放器 │ ◄──────►│ - 屏幕捕获 │
│ - 输入事件发送 │ │ - 音视频编码 │
│ - 信令客户端 │ │ - 输入控制 │
└──────────────────┘ └──────────────────┘
│ │
│ │
▼ ▼
┌──────────────────────────────────────────────┐
│ 信令服务器 (WebSocket) │
│ │
│ - SDP 交换 │
│ - ICE 候选交换 │
│ - 房间管理 │
└──────────────────────────────────────────────┘
│ │
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ WebRTC 媒体传输 │ │ WebRTC 媒体传输 │
│ (SRTP/SRTP) │ ◄──────►│ (SRTP/SRTP) │
│ │ │ │
│ - 视频流 │ │ - 视频流 │
│ - 音频流 │ │ - 音频流 │
│ - 数据通道 │ │ - 数据通道 │
└──────────────────┘ └──────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ PoixsDesk 模块架构 │
└─────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
│ PoixsDesk (主程序) │
│ - main.cpp: 程序入口 │
│ - 会话监控和生命周期管理 │
└───────────────────────────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ libcommon │ │ libdevice │ │ Win │
│ (公共库) │ │ (设备控制) │ │ (GUI程序) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ │ │ │ │ │
┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐
│客户端│ │RTC │ │输入 │ │桌面 │ │窗口 │ │对话框│
│ │ │发布 │ │设备 │ │控制 │ │界面 │ │界面 │
└─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
职责:
类结构:
class crtc_client : public crtc_publisher::clistener
{
// 状态管理
ERtc_Type m_status;
bool m_stoped;
// RTC发布者
std::unique_ptr<crtc_publisher> m_rtc_publisher;
// 信令相关
std::string m_room_name;
std::string m_user_name;
// 方法
void init(int gpu_index);
void Loop(const std::string& rtc_url);
void destroy();
};
状态机:
ERtc_None → ERtc_WebSocket_Init → ERtc_WebSocket →
ERtc_WebSocket_Wait → ERtc_WebSocket_Close → ERtc_Destory → ERtc_Exit
职责:
类结构:
class crtc_publisher : public PeerConnectionObserver,
public CreateSessionDescriptionObserver
{
// PeerConnection
rtc::scoped_refptr<PeerConnectionInterface> peer_connection_;
// 音视频源
rtc::scoped_refptr<VideoTrackSourceInterface> video_track_source_;
// 数据通道
std::unique_ptr<cdata_channel> m_data_channel_ptr;
// 回调接口
clistener* m_callback_ptr;
// 方法
void create_offer();
void set_remoter_description(const std::string& sdp);
void InitializePeerConnection();
};
职责:
类结构:
class cinput_device
{
// 数据通道
rtc::scoped_refptr<DataChannelInterface> dataChannel;
// 消息队列
std::list<rtc::CopyOnWriteBuffer> m_messages;
std::mutex m_messages_lock;
// 工作线程
std::thread m_work_thread;
// 方法
void OnMessage(const webrtc::DataBuffer& buffer);
void OnMouseMove(...);
void OnKeyDown(...);
// ... 其他输入事件处理
};
输入事件流程:
远程事件 → DataChannel接收 → 消息队列 → 工作线程处理 →
坐标转换 → Windows API → 本地输入
职责:
职责:
主要函数:
// 鼠标控制
void abs_mouse(FEvent& input, float x, float y); // 绝对鼠标移动
void move_mouse(FEvent& input, int deltaX, int deltaY); // 相对鼠标移动
void button_mouse(FEvent& input, int32_t posX, int32_t posY, int button, bool release); // 鼠标按键
void scroll(FEvent& input, int distance); // 垂直滚动
void hscroll(FEvent& input, int distance); // 水平滚动
// 键盘控制
void keyboard_update(FEvent& input, uint16_t modcode, bool release, uint8_t flags);
// 输入发送
void send_input(INPUT& i);
坐标映射:
virtual_x = pixel_x * (65535.0 / screen_width)职责:
关键功能:
职责:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 桌面捕获 │─────►│ 视频编码 │─────►│ WebRTC传输 │
│ │ │ │ │ │
│ DXGI/WGC │ │ H.264/VP9 │ │ RTP/SRTP │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐
│ Web 客户端 │
│ 视频播放 │
└──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 音频捕获 │─────►│ 音频编码 │─────►│ WebRTC传输 │
│ │ │ │ │ │
│ Windows Audio│ │ Opus/G.711 │ │ RTP/SRTP │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Web 客户端 │─────►│ 信令/数据通道 │─────►│ 输入设备处理 │
│ │ │ │ │ │
│ 鼠标/键盘事件 │ │ JSON/Binary │ │ 坐标转换 │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐
│ Windows API │
│ SendInput │
└──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 桌面端 │ │ 信令服务器 │ │ Web 客户端 │
│ │ │ │ │ │
│ Offer SDP │─────►│ │◄─────│ 信令连接 │
│ │ │ 信令转发 │ │ │
│ ICE Candidate│─────►│ │◄─────│ Answer SDP │
│ │ │ │ │ │
│ Answer SDP │◄─────│ │─────►│ ICE Candidate│
└──────────────┘ └──────────────┘ └──────────────┘
{
"type": "offer",
"room_name": "room123",
"user_name": "user1",
"sdp": "v=0\r\no=- ..."
}
{
"type": "answer",
"room_name": "room123",
"user_name": "user2",
"sdp": "v=0\r\no=- ..."
}
{
"type": "ice-candidate",
"room_name": "room123",
"user_name": "user1",
"candidate": {
"candidate": "candidate:...",
"sdpMLineIndex": 0,
"sdpMid": "0"
}
}
┌──────────┬──────────┬──────────┬──────────┐
│ 消息类型 │ 按键标志 │ 坐标X │ 坐标Y │
│ (1 byte) │ (1 byte) │ (2 bytes)│ (2 bytes)│
└──────────┴──────────┴──────────┴──────────┘
{
"type": "mouse_move",
"x": 100,
"y": 200,
"deltaX": 10,
"deltaY": 20
}
pixel = normalized * screen_dimensionsyncThreadDesktop() 同步线程到输入桌面VK_TO_SCANCODE_MAP 直接查找扫描码MapVirtualKey() 动态转换PoixsDesk/
├── CMakeLists.txt # 主构建文件
├── README.md # 项目说明
├── ARCHITECTURE.md # 架构文档(本文档)
├── libcommon/ # 公共库
│ ├── client.h/cpp # RTC客户端
│ ├── crtc_publisher.h/cpp # RTC发布者
│ ├── cinput_device.h/cpp # 输入设备
│ ├── cdesktop_capture.h/cpp # 桌面捕获
│ ├── cdata_channel.h/cpp # 数据通道
│ └── ...
├── libdevice/ # 设备控制库
│ └── window/
│ ├── device.h/cpp # 输入设备控制
│ └── misc.h/cpp # 辅助函数
├── PoixsDesk/ # 主程序
│ └── main.cpp # 程序入口
├── Win/ # Windows GUI程序
│ ├── LiveWin32.cpp # 主窗口
│ └── Dlg*.cpp # 对话框
├── Tools/ # 工具程序
│ ├── PoisxDeskService.cpp # Windows服务
│ └── audio.cpp # 音频处理
├── www/ # Web前端
│ ├── index.html # 主页
│ ├── player.html # 播放器页面
│ └── scripts/ # JavaScript脚本
└── build/ # 构建目录
# 1. 创建构建目录
mkdir build
cd build
# 2. 配置CMake(需要指定WebRTC路径)
cmake .. -DWebRTC_ROOT=/path/to/webrtc
# 3. 编译
cmake --build . --config Release
# 4. 运行
./PoixsDesk/PoixsDesk.exe ws://localhost:8080/rtc
cdesktop_capture 模块VideoEncoderInterfacecrtc_client 中的 WebSocket 实现问题1:输入事件无效
问题2:视频不显示
问题3:高延迟
文档版本:1.0
最后更新:2025-01-XX
作者:chensong