# 镜像投屏 **Repository Path**: liulei901112/screen-mirroring ## Basic Information - **Project Name**: 镜像投屏 - **Description**: 镜像投屏Windows软件 - **Primary Language**: C# - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-18 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 屏幕镜像(局域网投屏) 一个 Windows 局域网点对点屏幕镜像软件,分为「发送端」与「接收显示端」两个程序。发送端可直接输入接收端 IP,或通过 UDP 广播自动发现接收端,将本机屏幕实时投送到接收端显示,**无需投屏码或调度服务器**。 ## 架构 | 项目 | 说明 | | --- | --- | | `ScreenMirroring.Core` | 共享核心库:屏幕捕获、脏矩形检测、JPEG 编码、帧协议、TCP 收发、UDP 发现、MVVM 基础 | | `ScreenMirroring.Sender` | 发送端(WPF):抓屏 → 变化检测 → 编码 → 发送,带本地预览与画质 / 帧率 / 分辨率设置 | | `ScreenMirroring.Receiver` | 接收显示端(WPF):监听 → 解码 → 画布合成 → 显示,支持自适应缩放与全屏 | ```mermaid graph LR subgraph Sender[发送端] SUI[界面: 搜索/连接/预览/设置] Cap[ScreenCaptureService 抓屏] Det[DirtyRegionDetector 变化检测] Enc[JpegFrameEncoder 编码] FSend[FrameSender TCP 发送] end subgraph Receiver[接收端] FRecv[FrameReceiver TCP 监听] Canvas[画布合成 全帧/区域帧] RUI[界面: 缩放/全屏] end subgraph Discovery[UDP 发现] Search[发送端广播搜索] Reply[接收端回应 IP:端口] end SUI --> Cap --> Det --> Enc --> FSend FSend -- "TCP 长度前缀帧" --> FRecv FRecv --> Canvas --> RUI Search -.-> Reply Reply -.-> SUI ``` ## 功能 **发送端** - 输入接收端 IP / 端口,一键开始 / 停止投屏;也可点击「搜索」通过 UDP 广播自动发现接收端。 - 本地实时预览当前正在发送的画面。 - 可调 JPEG 画质、目标帧率、输出分辨率(等比缩放,不放大)。 - 底部实时显示实际运行信息:输出分辨率 · 帧率 · 带宽(MB/s)· 平均编码耗时。 - 自动记忆最后一次使用的接收端地址(保存于 `%APPDATA%\ScreenMirroring\sender-settings.json`)。 **接收显示端** - 启动后自动监听(默认端口 `5000`),并在界面显示本机局域网 IP,方便发送端填写。 - 实时接收并显示发送端画面,画面按窗口比例自适应缩放(不变形)。 - 新连接首帧自动进入全屏;双击画面切换全屏;全屏下按 `ESC` 退出。断线自动退出全屏并回到等待连接状态。 **增量传输(脏矩形优化)** - 发送端逐帧分块检测画面变化,静态画面(文档、桌面、代码编辑等)只编码并发送**变化的区域**,显著降低带宽占用。 - 首帧、画面尺寸变化、或变化面积超过 50% 时回退为**全帧**传输,兼顾正确性与效率。 - 接收端维护完整画布:全帧整体替换,区域帧局部贴图;越界或画布未就绪时丢弃该帧并等待下一个全帧,避免画面错乱。 ## 技术栈 - 语言与框架:C# + .NET 10 + WPF(`net10.0-windows`,启用 `UseWPF` + `UseWindowsForms`)。 - 屏幕捕获:`System.Drawing` + `Graphics.CopyFromScreen`(抓取主屏幕)。 - 图像编解码:JPEG(`System.Drawing.Imaging`),质量 1-100 可调。 - 网络传输:`System.Net.Sockets` 的 `TcpClient` / `TcpListener` 与 `UdpClient`。 - 协议序列化:`System.Buffers.Binary`(大端读写)。 - 架构模式:WPF MVVM,项目内手写 `ObservableObject` / `RelayCommand`,**无第三方 MVVM 依赖**。 - 本地设置:`System.Text.Json`。 ## 传输协议 ### TCP 帧协议 每条消息结构为 `[4 字节大端长度][1 字节类型][负载]`,长度字段等于「类型字节 + 负载」的字节数。用于解决 TCP 粘包 / 半包问题,并区分帧类型: | 类型 | 值 | 负载格式 | | --- | --- | --- | | 全帧 | `0x01` | 完整 JPEG 数据 | | 区域帧 | `0x02` | `[4字节 X][4字节 Y][4字节 W][4字节 H][局部 JPEG]`(均大端) | - 接收端从 JPEG 自带的宽高信息确定画面尺寸,因此全帧无需额外分辨率头,发送端中途调整参数也不会导致协议失配。 - 单帧负载限制为最大 64 MB,防止异常长度导致内存溢出。 ### UDP 局域网发现 - 固定发现端口:`47821`。 - 发送端向广播地址发送搜索串 `SCREENMIRROR_SEARCH`。 - 接收端收到后,将本机各 IPv4 地址以 `SCREENMIRROR_REPLY:{ip}:{tcpPort}` 逐个回复;发送端去重后取第一个结果填入地址栏。 ## 目录结构 ``` screen-mirroring/ ├── ScreenMirroring.slnx ├── README.md ├── ScreenMirroring.Core/ # 共享核心库(net10.0-windows) │ ├── Models/StreamConfig.cs # 分辨率 / 帧率 / JPEG 质量配置 │ ├── Protocol/FrameProtocol.cs # 帧类型、长度前缀打包与解析 │ ├── Capture/ScreenCaptureService.cs # 主屏抓取 + 等比缩放 │ ├── Capture/DirtyRegionDetector.cs # 分块帧差脏矩形检测 │ ├── Encoding/JpegFrameEncoder.cs # Bitmap → JPEG │ ├── Mvvm/ObservableObject.cs # 属性变更通知基类 │ ├── Mvvm/RelayCommand.cs # 无参命令 │ └── Network/ │ ├── FrameSender.cs # TCP 客户端发送帧 │ ├── FrameReceiver.cs # TCP 服务端监听并接收帧 │ └── UdpDiscovery.cs # UDP 局域网发现 ├── ScreenMirroring.Sender/ # 发送端(WPF) │ ├── AppSettings.cs # 本地设置持久化 │ ├── App.xaml(.cs) # 应用入口与深色主题 │ ├── MainWindow.xaml(.cs) # 发送端主界面 │ └── ViewModels/MainViewModel.cs # 连接、发送循环、预览、统计 └── ScreenMirroring.Receiver/ # 接收端(WPF) ├── App.xaml(.cs) # 应用入口与深色主题 ├── MainWindow.xaml(.cs) # 接收端主界面、全屏逻辑 └── ViewModels/MainViewModel.cs # 监听、画布合成、状态管理 ``` ## 环境要求 - Windows 10 / 11 - .NET 10 SDK(构建用) - 两台机器处于同一局域网,且接收端防火墙放行 TCP `5000` 与 UDP `47821` ## 构建与运行 ```powershell # 构建整个解决方案 dotnet build ScreenMirroring.slnx # 运行接收端 dotnet run --project ScreenMirroring.Receiver # 运行发送端 dotnet run --project ScreenMirroring.Sender ``` ## 使用步骤 1. 在接收端机器上启动「接收端」,默认自动开始监听(端口 `5000`),界面会显示本机局域网 IP。 2. 在发送端机器上启动「发送端」,可点击「搜索」通过 UDP 自动发现接收端,或手动输入接收端 IP 与端口。 3. 点击「开始投屏」。发送端抓取本机主屏幕并实时发送,接收端自动进入全屏显示;发送端界面同步展示本地预览与实时统计。 4. 停止投屏或断开连接后,接收端自动退出全屏并回到等待连接状态。 ## 后续规划 ### 第二版:副屏 / 扩展屏(规划中,待实现) 在现有镜像能力之上新增「扩展屏」模式:发送端可一键在「镜像模式」(投主屏)与「扩展模式」(投虚拟副屏)之间切换。扩展模式下,发送端通过虚拟显示器驱动创建一块虚拟副屏,副屏画面经现有 TCP 通道实时传输到接收端显示;发送端本机鼠标可原生移入该副屏操作(Windows 扩展桌面行为),画面渲染在接收端。 计划要点(尚未实现): - **帧源抽象**:新增 `IFrameSource` 接口,现有抓屏改造为 `PrimaryScreenFrameSource`,新增 `VirtualDisplayFrameSource`,发送循环只依赖接口。 - **虚拟显示器**:集成开源驱动 [VirtualDrivers/Virtual-Display-Driver](https://github.com/VirtualDrivers/Virtual-Display-Driver)(MIT,基于微软 IDD 虚拟显示器模型,需管理员权限安装),由 `VirtualDisplayManager` 封装创建 / 销毁 / 枚举 / 等待出现。 - **高性能抓屏**:扩展模式使用 Desktop Duplication API(`Vortice.Direct3D11` / `Vortice.DXGI`)抓取虚拟副屏合成画面。 - **协议升级**:在现有长度前缀协议上引入消息类型,连接后先双向握手(接收端上报本机分辨率、发送端回报模式),再进入帧流;副屏分辨率跟随接收端屏幕。 - **模式切换 UI**:发送端提供镜像 / 扩展切换与状态提示,接收端显示当前模式与分辨率;切换失败时安全回退镜像模式。 ### 其他方向 - 多显示器选择。 - 鼠标键盘反向控制、声音传输。 - H.264 视频编码以进一步降低带宽与延迟。 ```