2026-04-14 14:17:10 +08:00
2026-03-27 17:56:20 +08:00

会议投屏系统

项目简介

面向32台终端的内网低延迟屏幕广播系统支持网络投屏、硬件接口互通、一键自动化运维。

本项目提供 Python (PySide6)Rust (egui) 两个版本的投屏控制客户端。

需求说明

当前会议投屏需求涉及通过视频矩阵系统向32台终端设备进行同步投屏。具体模式可分为以下两类

  1. 模式1主播投屏单台终端设备作为信号源向MediaMTX服务器投送本地画面流的形式其余31台终端设备读取MediaMTX服务器的流。
    • 特殊情况单独架设1台机器用浏览器读取流然后通过HDMI线将显示画面输出到矩阵系统矩阵再发送给第三方视频会议系统。这台机器可利用墙上的插座。
  2. 模式2视频会议在MediaMTX服务器上用采集卡和FFmpeg读取设备内容发布流全部32台终端设备用浏览器读取MediaMTX服务器的流。

技术说明上述32台终端设备均通过有线网络实现互联互通。

解决方案

参考文档:https://docs.qq.com/doc/DQXFpdHN5Rk9yRGpC

客户端版本对比

特性 Python (PySide6) Rust (egui)
GUI 框架 PySide6 (Qt) egui (即时模式)
编译方式 PyInstaller Cargo + GNU 工具链
文件大小 ~50MB ~4MB
启动速度 较慢
依赖 需 Python 运行时 独立可执行文件
界面风格 原生 Windows 风格 自定义风格

整体部署步骤(细化版)

步骤1MediaMTX服务器端

  1. 下载并解压MediaMTX
    • 下载地址:https://github.com/bluenviron/mediamtx/releaseswindows-amd64版
    • 解压到 D:\ScreenCast\mediamtx 目录
  2. 配置MediaMTX
    • 编辑 D:\ScreenCast\mediamtx\mediamtx.yml 文件:
      • 设置 webRTCAddressrtmpAddress
      • 添加 screen 路径配置,暂时不设鉴权
  3. 启动MediaMTX
    • 运行 mediamtx.exe
    • 观察控制台是否监听对应端口
  4. 安装依赖
    • 安装Pythonhttps://www.python.org/downloads/windows/勾选「Add Python to PATH」
    • 安装FFmpeghttps://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z(解压到 D:\ScreenCast\ffmpeg
    • 安装Python依赖管理员cmd执行
      pip install pyautogui requests subprocess32 psutil mss numpy
      
  5. 开启权限
    • 给服务器开启「屏幕录制权限」(设置→隐私和安全性→屏幕录制→允许 Python/CMD

模式1主播投屏本机屏幕 → MediaMTX → 多终端)

单台终端作为信号源将本机屏幕画面推送到MediaMTX服务器其他终端通过浏览器观看。

主播端:推送本机屏幕

方案A使用 Rust 客户端(推荐)

  1. 下载预编译版本

    • 从 Releases 下载 push_screen.exe
    • 或使用源码自行编译(见下方编译说明)
  2. 运行程序

    • 双击运行 push_screen.exe
    • 点击「设置」按钮配置服务器IP和FFmpeg路径
    • 点击「开始全屏投屏」按钮开始推流

方案B使用 Python 客户端

  • 双击运行 push_screen.py
  • 点击「一键全屏投屏」按钮
  • 脚本会自动启动MediaMTX并开始推流

方案C使用mss抓屏+FFmpeg推流

import subprocess
import time
import mss
import numpy as np

# ---------- 配置 ----------
RTMP_URL = "rtmp://服务器IP:1935/screen"   # MediaMTX 的推流地址
FPS = 24
WIDTH, HEIGHT = 1280, 720

# 初始化屏幕捕获
with mss.mss() as sct:
    monitor = sct.monitors[1]  # 主显示器

    # FFmpeg 命令,编码为 H.264 并推 RTMP
    command = [
        'ffmpeg',
        '-y',
        '-f', 'rawvideo',
        '-vcodec', 'rawvideo',
        '-pix_fmt', 'bgr24',
        '-s', f'{WIDTH}x{HEIGHT}',
        '-r', str(FPS),
        '-i', '-',
        '-c:v', 'libx264',
        '-pix_fmt', 'yuv420p',
        '-preset', 'ultrafast',
        '-f', 'flv',
        RTMP_URL
    ]

    process = subprocess.Popen(command, stdin=subprocess.PIPE)
    last_time = time.time()

    try:
        while True:
            img = np.array(sct.grab(monitor))
            frame = img[..., :3]
            process.stdin.write(frame.tobytes())

            elapsed = time.time() - last_time
            sleep_time = (1.0 / FPS) - elapsed
            if sleep_time > 0:
                time.sleep(sleep_time)
            last_time = time.time()

    except KeyboardInterrupt:
        print("用户停止推流")
    finally:
        process.stdin.close()
        process.wait()

观众端:通过浏览器观看

  1. 打开浏览器
    • 在任意观众机上打开 Chrome 浏览器
  2. 访问流地址
    • 输入地址:http://服务器IP:8889/screen
    • MediaMTX 会自动处理 WebRTC 协商,把 RTMP 流转成 WebRTC 给浏览器
    • 确认能看到主播机屏幕画面
  3. 批量部署
    • 在31台终端上运行 auto_receive.py 脚本
    • 脚本会自动打开Chrome并跳转到流地址

特殊情况HDMI矩阵输出

单独架设1台机器通过HDMI线将显示画面输出到视频矩阵系统。

  1. 准备一台小机器
    • 配置要求较低能运行Chrome浏览器即可
  2. 访问流地址
    • 打开浏览器访问:http://服务器IP:8889/screen
  3. HDMI输出到矩阵
    • 通过HDMI线将小机器连接到视频矩阵系统
    • 小机器播放的流画面将输出到矩阵系统

模式2视频会议采集卡 → MediaMTX → 多终端)

在MediaMTX服务器上用采集卡和FFmpeg读取外部视频信号发布流全部32台终端用浏览器观看。

步骤1安装FFmpeg

  • 下载FFmpeghttps://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z
  • 解压到 D:\ScreenCast\ffmpeg 目录
  • 确保 D:\ScreenCast\ffmpeg\bin\ffmpeg.exe 可用

步骤2列出DirectShow设备

在命令行执行以下命令,列出所有 DirectShow 设备:

ffmpeg -list_devices true -f dshow -i dummy

输出示例:

[dshow @ ...] DirectShow video devices
[dshow @ ...]  "USB Video Device"
[dshow @ ...]  "AVerMedia HD Capture"

记下要使用的设备名称。

步骤3配置MediaMTX

编辑 mediamtx.yml,添加 hdmi 路径配置:

paths:
  hdmi:
    # source 不写,或者写 publisher表示"由客户端推流进来"
    # source: publisher

    # 局域网可以先不开认证
    # publishUser: ""
    # publishPass: ""

    # 有读者时才拉源(这里用不到,因为是 publisher 模式)
    sourceOnDemand: no

步骤4启动MediaMTX

./mediamtx

看到日志里 RTMP/RTSP/WebRTC 端口监听就表示启动成功。

步骤5用FFmpeg采集采集卡并推流

假设:

  • MediaMTX 服务器 IP192.168.1.100
  • 推流路径名hdmi
  • DirectShow 设备名称:"AVerMedia HD Capture"
ffmpeg -f dshow -i video="AVerMedia HD Capture" ^
  -c:v libx264 -preset ultrafast -tune zerolatency ^
  -profile:v baseline -pix_fmt yuv420p ^
  -b:v 4000k -maxrate 4000k -bufsize 8000k ^
  -g 30 ^
  -f flv rtmp://192.168.1.100/hdmi

步骤6客户端观看

在浏览器中直接访问:

http://192.168.1.100:8889/hdmi

MediaMTX 会返回内置的 WebRTC 播放页面浏览器会自动播放HDMI信号。

所有32台终端均可通过浏览器访问上述地址观看视频会议内容。

Rust 客户端编译指南

环境准备

  1. 安装 Rust

    • 下载地址:https://rustup.rs/
    • 安装时选择 x86_64-pc-windows-gnu 目标
  2. 安装 MinGW-w64GNU 工具链)

    • 推荐通过 MSYS2 安装:pacman -S mingw-w64-x86_64-toolchain
    • 或下载 winlibs MinGW-w64
  3. 添加 Rust 目标

    rustup target add x86_64-pc-windows-gnu
    

项目结构

push_screen_rust/
├── Cargo.toml          # 项目配置
├── build.rs            # 构建脚本(用于嵌入图标)
├── app.rc              # Windows 资源文件
├── vi.ico              # 应用程序图标
└── src/
    └── main.rs         # 主程序

依赖库对照PySide6 vs Rust

Python/PySide6 Rust 等效库 用途
PySide6 (Qt) eframe + egui GUI 框架
QThread std::thread + mpsc 多线程
QTimer std::time::Duration + 线程休眠 定时器
socket std::net::TcpStream 网络检测
subprocess std::process::Command 进程管理
json serde + serde_json JSON 序列化
logging tracing + tracing-subscriber 日志记录
requests reqwest HTTP 客户端

编译参数对照PyInstaller vs Cargo

PyInstaller 参数 Cargo/Rust 等效配置 说明
--onefile lto = true + strip = true 优化体积
--windowed #![windows_subsystem = "windows"] 隐藏控制台
--icon=vi.ico windres app.rc -O coff -o app.res + 链接 嵌入图标
--noconsole windows_subsystem 属性 无控制台窗口

编译步骤

  1. 进入项目目录

    cd push_screen_rust
    
  2. 编译资源文件(图标)

    windres app.rc -O coff -o app.res
    
  3. 配置构建脚本build.rs

    use std::env;
    
    fn main() {
        let target = env::var("TARGET").unwrap();
        if target.contains("windows") {
            println!("cargo:rustc-link-arg=app.res");
        }
        println!("cargo:rerun-if-changed=app.rc");
        println!("cargo:rerun-if-changed=vi.ico");
    }
    
  4. 配置 Cargo.toml

    [package]
    name = "push_screen_rust"
    version = "0.1.0"
    edition = "2021"
    build = "build.rs"
    
    [dependencies]
    eframe = { version = "0.24", features = ["default"] }
    egui = "0.24"
    tokio = { version = "1", features = ["full"] }
    serde = { version = "1.0", features = ["derive"] }
    serde_json = "1.0"
    reqwest = { version = "0.11", features = ["json"] }
    tracing = "0.1"
    tracing-subscriber = "0.3"
    winapi = { version = "0.3", features = ["winuser", "windef", "wingdi", ...] }
    
    [profile.release]
    opt-level = 3
    lto = true
    # strip = true  # 取消注释以进一步减小体积
    
    [[bin]]
    name = "push_screen"
    path = "src/main.rs"
    
  5. 编译发布版本

    cargo build --release --target x86_64-pc-windows-gnu
    
  6. 输出文件

    • 位置:target/x86_64-pc-windows-gnu/release/push_screen.exe
    • 大小:约 4-5 MB

关键代码说明

隐藏控制台窗口

#![windows_subsystem = "windows"]

设置 UTF-8 编码(解决中文乱码)

#[cfg(windows)]
extern "system" {
    fn SetConsoleOutputCP(wCodePageID: u32) -> i32;
    fn SetConsoleCP(wCodePageID: u32) -> i32;
}

fn set_utf8_encoding() {
    #[cfg(windows)]
    unsafe {
        SetConsoleOutputCP(65001); // UTF-8
        SetConsoleCP(65001);
    }
}

加载中文字体

fn configure_fonts(ctx: &egui::Context) {
    let mut fonts = egui::FontDefinitions::default();
    
    // 从系统加载中文字体
    let font_paths = [
        r"C:\Windows\Fonts\msyh.ttc",      // 微软雅黑
        r"C:\Windows\Fonts\simsun.ttc",    // 宋体
    ];
    
    for font_path in &font_paths {
        if let Ok(font_data) = std::fs::read(font_path) {
            // 加载字体...
        }
    }
    
    ctx.set_fonts(fonts);
}

测试方案

使用1台台式机模拟MediaMTX服务器2台笔记本分别模拟信号源和观众测试两种场景。

设备准备

设备 角色 IP地址示例
台式机 MediaMTX服务器 192.168.1.100
笔记本A 信号源(主播) 192.168.1.101
笔记本B 观众端 192.168.1.102

前置条件

  1. 三台设备处于同一局域网
  2. 台式机已安装MediaMTX和FFmpeg
  3. 笔记本A已安装Python和FFmpeg
  4. 所有设备关闭防火墙或开放对应端口

场景1测试主播投屏

拓扑笔记本A信号源→ 台式机MediaMTX→ 笔记本B观众

步骤

  1. 台式机启动MediaMTX

    D:\ScreenCast\mediamtx\mediamtx.exe
    

    确认控制台显示端口监听正常

  2. 笔记本A启动推流

    • 运行 push_screen.exe (Rust) 或 push_screen.py (Python)
    • 或使用命令行推流:
    ffmpeg -f gdigrab -framerate 30 -i desktop -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp://192.168.1.100:8554/screen
    
  3. 笔记本B观看

    • 打开Chrome浏览器
    • 访问:http://192.168.1.100:8889/screen
    • 确认能看到笔记本A的屏幕画面

验证点

  • 推流端无错误
  • MediaMTX日志显示有客户端连接
  • 观众端能流畅播放画面
  • 延迟在可接受范围内(<1秒

场景2测试视频会议采集卡模式

拓扑外部HDMI信号 → 台式机(采集卡+FFmpeg→ 台式机MediaMTX→ 笔记本A/B观众

前置条件:台式机已连接视频采集卡

步骤

  1. 列出采集卡设备

    ffmpeg -list_devices true -f dshow -i dummy
    

    记录设备名称

  2. 台式机启动MediaMTX

    D:\ScreenCast\mediamtx\mediamtx.exe
    
  3. 台式机启动FFmpeg推流

    ffmpeg -f dshow -i video="采集卡设备名称" -c:v libx264 -preset ultrafast -tune zerolatency -f flv rtmp://192.168.1.100/hdmi
    
  4. 笔记本A和B观看

    • 打开Chrome浏览器
    • 访问:http://192.168.1.100:8889/hdmi
    • 确认能看到采集卡的HDMI输入画面

验证点

  • 采集卡设备被正确识别
  • FFmpeg推流无错误
  • MediaMTX正确转发流
  • 多客户端能同时观看

测试记录表

测试项 预期结果 实际结果 是否通过
MediaMTX启动 端口监听正常
场景1推流 无错误
场景1观看 画面流畅
场景2推流 无错误
场景2多客户端 至少2台同时观看

核心文件结构统一放在服务器D:\ScreenCast

D:\ScreenCast
├── mediamtx/                # MediaMTX解压目录
│   └── mediamtx.exe
├── ffmpeg/                  # FFmpeg解压目录
│   └── bin/ffmpeg.exe
├── push_screen_rust/        # Rust 客户端源码
│   ├── Cargo.toml
│   ├── build.rs
│   ├── app.rc
│   ├── vi.ico
│   └── src/main.rs
├── push_screen.exe          # Rust 编译后的可执行文件(推荐)
├── push_screen.py           # Python 投屏源推流脚本
├── auto_receive.py          # 终端自动打开浏览器脚本
└── manage_server.py         # 运维总控脚本

核心脚本说明

Rust 客户端push_screen_rust

功能:轻量级投屏控制客户端,单文件可执行,无需运行时依赖。

特性

  • 实时状态检测FFmpeg、服务器、端口
  • 配置持久化config.json
  • 一键开始/停止推流
  • 中文字体支持
  • 自定义应用程序图标

编译命令

cd push_screen_rust
windres app.rc -O coff -o app.res
cargo build --release --target x86_64-pc-windows-gnu

Python 客户端push_screen.py

功能:基于 PySide6 的投屏控制界面,功能完善。

特性

  • 可视化配置界面
  • 实时状态指示器
  • 日志显示
  • 设置对话框

运行命令

python push_screen.py

终端自动打开浏览器脚本auto_receive.py

功能双击脚本自动打开Chrome浏览器全屏直接跳转到投屏/会议流页面支持批量部署到32台终端无需手动输入地址。

运维总控脚本manage_server.py

功能一站式运维启动MediaMTX、切换两种模式、批量控制32台终端、停止所有服务适合管理员操作。

Description
面向 32 台终端的内网低延迟屏幕广播系统,支持网络投屏、硬件接口互通、一键自动化运维。
Readme 680 MiB
Languages
Makefile 88.8%
Rust 6%
Python 5.2%