2026-03-18 15:19:07 +08:00
|
|
|
|
# 会议投屏系统
|
|
|
|
|
|
|
|
|
|
|
|
## 项目简介
|
|
|
|
|
|
|
|
|
|
|
|
面向32台终端的内网低延迟屏幕广播系统,支持网络投屏、硬件接口互通、一键自动化运维。
|
|
|
|
|
|
|
|
|
|
|
|
## 需求说明
|
|
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
当前会议投屏需求涉及通过视频矩阵系统向32台终端设备进行同步投屏。具体模式可分为以下两类:
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
1. **模式1(主播投屏)**:单台终端设备作为信号源,向MediaMTX服务器投送本地画面(流的形式),其余31台终端设备读取MediaMTX服务器的流。
|
2026-03-19 15:48:45 +08:00
|
|
|
|
- **特殊情况**:单独架设1台机器,用浏览器读取流,然后通过HDMI线将显示画面输出到矩阵系统,矩阵再发送给第三方视频会议系统。这台机器可利用墙上的插座。
|
2026-03-19 15:21:00 +08:00
|
|
|
|
2. **模式2(视频会议)**:在MediaMTX服务器上用采集卡和FFmpeg读取设备内容,发布流,全部32台终端设备用浏览器读取MediaMTX服务器的流。
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
|
|
|
|
|
技术说明:上述32台终端设备均通过有线网络实现互联互通。
|
|
|
|
|
|
|
|
|
|
|
|
## 解决方案
|
|
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
参考文档:<https://docs.qq.com/doc/DQXFpdHN5Rk9yRGpC>
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
2026-03-18 17:14:55 +08:00
|
|
|
|
## 整体部署步骤(细化版)
|
|
|
|
|
|
|
2026-03-19 13:55:05 +08:00
|
|
|
|
### 步骤1:MediaMTX服务器端
|
2026-03-18 17:14:55 +08:00
|
|
|
|
|
|
|
|
|
|
1. **下载并解压MediaMTX**
|
|
|
|
|
|
- 下载地址:`https://github.com/bluenviron/mediamtx/releases`(windows-amd64版)
|
|
|
|
|
|
- 解压到 `D:\ScreenCast\mediamtx` 目录
|
|
|
|
|
|
2. **配置MediaMTX**
|
|
|
|
|
|
- 编辑 `D:\ScreenCast\mediamtx\mediamtx.yml` 文件:
|
|
|
|
|
|
- 设置 `webRTCAddress` 和 `rtmpAddress`
|
|
|
|
|
|
- 添加 `screen` 路径配置,暂时不设鉴权
|
|
|
|
|
|
3. **启动MediaMTX**
|
|
|
|
|
|
- 运行 `mediamtx.exe`
|
|
|
|
|
|
- 观察控制台是否监听对应端口
|
|
|
|
|
|
4. **安装依赖**
|
|
|
|
|
|
- 安装Python:`https://www.python.org/downloads/windows/`(勾选「Add Python to PATH」)
|
|
|
|
|
|
- 安装FFmpeg:`https://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)
|
|
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
## 模式1:主播投屏(本机屏幕 → MediaMTX → 多终端)
|
|
|
|
|
|
|
|
|
|
|
|
单台终端作为信号源,将本机屏幕画面推送到MediaMTX服务器,其他终端通过浏览器观看。
|
2026-03-19 13:55:05 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
### 主播端:推送本机屏幕
|
2026-03-18 17:14:55 +08:00
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
**方案A:使用push\_screen.py脚本**
|
|
|
|
|
|
|
2026-03-18 17:14:55 +08:00
|
|
|
|
- 双击运行 `push_screen.py`
|
|
|
|
|
|
- 点击「一键全屏投屏」按钮
|
|
|
|
|
|
- 脚本会自动启动MediaMTX并开始推流
|
|
|
|
|
|
|
|
|
|
|
|
**方案B:使用mss抓屏+FFmpeg推流**
|
2026-03-19 15:48:45 +08:00
|
|
|
|
|
2026-03-18 17:14:55 +08:00
|
|
|
|
```python
|
|
|
|
|
|
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:
|
2026-03-19 13:55:05 +08:00
|
|
|
|
monitor = sct.monitors[1] # 主显示器
|
2026-03-18 17:14:55 +08:00
|
|
|
|
|
|
|
|
|
|
# FFmpeg 命令,编码为 H.264 并推 RTMP
|
|
|
|
|
|
command = [
|
|
|
|
|
|
'ffmpeg',
|
2026-03-19 13:55:05 +08:00
|
|
|
|
'-y',
|
|
|
|
|
|
'-f', 'rawvideo',
|
2026-03-18 17:14:55 +08:00
|
|
|
|
'-vcodec', 'rawvideo',
|
2026-03-19 13:55:05 +08:00
|
|
|
|
'-pix_fmt', 'bgr24',
|
|
|
|
|
|
'-s', f'{WIDTH}x{HEIGHT}',
|
|
|
|
|
|
'-r', str(FPS),
|
|
|
|
|
|
'-i', '-',
|
|
|
|
|
|
'-c:v', 'libx264',
|
2026-03-18 17:14:55 +08:00
|
|
|
|
'-pix_fmt', 'yuv420p',
|
2026-03-19 13:55:05 +08:00
|
|
|
|
'-preset', 'ultrafast',
|
2026-03-18 17:14:55 +08:00
|
|
|
|
'-f', 'flv',
|
|
|
|
|
|
RTMP_URL
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
process = subprocess.Popen(command, stdin=subprocess.PIPE)
|
|
|
|
|
|
last_time = time.time()
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
while True:
|
|
|
|
|
|
img = np.array(sct.grab(monitor))
|
2026-03-19 13:55:05 +08:00
|
|
|
|
frame = img[..., :3]
|
2026-03-18 17:14:55 +08:00
|
|
|
|
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()
|
|
|
|
|
|
```
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
### 观众端:通过浏览器观看
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
2026-03-18 17:14:55 +08:00
|
|
|
|
1. **打开浏览器**
|
|
|
|
|
|
- 在任意观众机上打开 Chrome 浏览器
|
|
|
|
|
|
2. **访问流地址**
|
2026-03-19 11:18:54 +08:00
|
|
|
|
- 输入地址:`http://服务器IP:8889/screen`
|
|
|
|
|
|
- MediaMTX 会自动处理 WebRTC 协商,把 RTMP 流转成 WebRTC 给浏览器
|
2026-03-18 17:14:55 +08:00
|
|
|
|
- 确认能看到主播机屏幕画面
|
|
|
|
|
|
3. **批量部署**
|
2026-03-19 15:21:00 +08:00
|
|
|
|
- 在31台终端上运行 `auto_receive.py` 脚本
|
2026-03-18 17:14:55 +08:00
|
|
|
|
- 脚本会自动打开Chrome并跳转到流地址
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
### 特殊情况:HDMI矩阵输出
|
2026-03-19 13:55:05 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
单独架设1台机器,通过HDMI线将显示画面输出到视频矩阵系统。
|
2026-03-19 13:55:05 +08:00
|
|
|
|
|
|
|
|
|
|
1. **准备一台小机器**
|
|
|
|
|
|
- 配置要求较低,能运行Chrome浏览器即可
|
|
|
|
|
|
2. **访问流地址**
|
|
|
|
|
|
- 打开浏览器访问:`http://服务器IP:8889/screen`
|
|
|
|
|
|
3. **HDMI输出到矩阵**
|
|
|
|
|
|
- 通过HDMI线将小机器连接到视频矩阵系统
|
|
|
|
|
|
- 小机器播放的流画面将输出到矩阵系统
|
|
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
## 模式2:视频会议(采集卡 → MediaMTX → 多终端)
|
2026-03-19 11:46:41 +08:00
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
在MediaMTX服务器上用采集卡和FFmpeg读取外部视频信号,发布流,全部32台终端用浏览器观看。
|
2026-03-19 11:46:41 +08:00
|
|
|
|
|
|
|
|
|
|
### 步骤1:安装FFmpeg
|
|
|
|
|
|
|
|
|
|
|
|
- 下载FFmpeg:`https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z`
|
|
|
|
|
|
- 解压到 `D:\ScreenCast\ffmpeg` 目录
|
|
|
|
|
|
- 确保 `D:\ScreenCast\ffmpeg\bin\ffmpeg.exe` 可用
|
|
|
|
|
|
|
|
|
|
|
|
### 步骤2:列出DirectShow设备
|
|
|
|
|
|
|
|
|
|
|
|
在命令行执行以下命令,列出所有 DirectShow 设备:
|
|
|
|
|
|
|
|
|
|
|
|
```bat
|
|
|
|
|
|
ffmpeg -list_devices true -f dshow -i dummy
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
输出示例:
|
2026-03-19 15:48:45 +08:00
|
|
|
|
|
2026-03-19 11:46:41 +08:00
|
|
|
|
```
|
|
|
|
|
|
[dshow @ ...] DirectShow video devices
|
|
|
|
|
|
[dshow @ ...] "USB Video Device"
|
|
|
|
|
|
[dshow @ ...] "AVerMedia HD Capture"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
记下要使用的设备名称。
|
|
|
|
|
|
|
|
|
|
|
|
### 步骤3:配置MediaMTX
|
|
|
|
|
|
|
|
|
|
|
|
编辑 `mediamtx.yml`,添加 `hdmi` 路径配置:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
paths:
|
|
|
|
|
|
hdmi:
|
|
|
|
|
|
# source 不写,或者写 publisher,表示"由客户端推流进来"
|
|
|
|
|
|
# source: publisher
|
|
|
|
|
|
|
|
|
|
|
|
# 局域网可以先不开认证
|
|
|
|
|
|
# publishUser: ""
|
|
|
|
|
|
# publishPass: ""
|
|
|
|
|
|
|
|
|
|
|
|
# 有读者时才拉源(这里用不到,因为是 publisher 模式)
|
|
|
|
|
|
sourceOnDemand: no
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 步骤4:启动MediaMTX
|
|
|
|
|
|
|
|
|
|
|
|
```bat
|
|
|
|
|
|
./mediamtx
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
看到日志里 RTMP/RTSP/WebRTC 端口监听就表示启动成功。
|
|
|
|
|
|
|
|
|
|
|
|
### 步骤5:用FFmpeg采集采集卡并推流
|
|
|
|
|
|
|
|
|
|
|
|
假设:
|
2026-03-19 15:48:45 +08:00
|
|
|
|
|
2026-03-19 11:46:41 +08:00
|
|
|
|
- MediaMTX 服务器 IP:192.168.1.100
|
|
|
|
|
|
- 推流路径名:hdmi
|
|
|
|
|
|
- DirectShow 设备名称:"AVerMedia HD Capture"
|
|
|
|
|
|
|
|
|
|
|
|
```bat
|
|
|
|
|
|
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:客户端观看
|
|
|
|
|
|
|
|
|
|
|
|
在浏览器中直接访问:
|
2026-03-19 15:48:45 +08:00
|
|
|
|
|
2026-03-19 11:46:41 +08:00
|
|
|
|
```
|
|
|
|
|
|
http://192.168.1.100:8889/hdmi
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
MediaMTX 会返回内置的 WebRTC 播放页面,浏览器会自动播放HDMI信号。
|
|
|
|
|
|
|
2026-03-19 15:21:00 +08:00
|
|
|
|
所有32台终端均可通过浏览器访问上述地址观看视频会议内容。
|
|
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
## 测试方案
|
|
|
|
|
|
|
|
|
|
|
|
使用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.py`
|
|
|
|
|
|
- 或使用命令行推流:
|
|
|
|
|
|
```bat
|
|
|
|
|
|
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. **列出采集卡设备**
|
|
|
|
|
|
```bat
|
|
|
|
|
|
ffmpeg -list_devices true -f dshow -i dummy
|
|
|
|
|
|
```
|
|
|
|
|
|
记录设备名称
|
|
|
|
|
|
|
|
|
|
|
|
2. **台式机启动MediaMTX**
|
|
|
|
|
|
```
|
|
|
|
|
|
D:\ScreenCast\mediamtx\mediamtx.exe
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
3. **台式机启动FFmpeg推流**
|
|
|
|
|
|
```bat
|
|
|
|
|
|
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台同时观看 | | |
|
|
|
|
|
|
|
2026-03-18 17:14:55 +08:00
|
|
|
|
## 核心文件结构(统一放在服务器D:\ScreenCast)
|
2026-03-18 15:19:07 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
D:\ScreenCast
|
|
|
|
|
|
├── mediamtx/ # MediaMTX解压目录
|
|
|
|
|
|
│ └── mediamtx.exe
|
|
|
|
|
|
├── ffmpeg/ # FFmpeg解压目录
|
|
|
|
|
|
│ └── bin/ffmpeg.exe
|
|
|
|
|
|
├── push_screen.py # 投屏源推流脚本(一键采集)
|
|
|
|
|
|
├── auto_receive.py # 终端自动打开浏览器脚本
|
|
|
|
|
|
└── manage_server.py # 运维总控脚本(启动/切换模式/停止)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 核心Python脚本
|
|
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
### 脚本1:投屏源推流脚本(push\_screen.py)
|
|
|
|
|
|
|
2026-03-18 15:19:07 +08:00
|
|
|
|
功能:双击即可采集Win10屏幕,自动推流到MediaMTX,无需敲命令;支持「全屏/指定窗口」投屏,带可视化提示。
|
|
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
### 脚本2:终端自动打开浏览器脚本(auto\_receive.py)
|
|
|
|
|
|
|
2026-03-18 15:19:07 +08:00
|
|
|
|
功能:双击脚本自动打开Chrome浏览器(全屏),直接跳转到投屏/会议流页面;支持批量部署到32台终端,无需手动输入地址。
|
|
|
|
|
|
|
2026-03-19 15:48:45 +08:00
|
|
|
|
### 脚本3:运维总控脚本(manage\_server.py)
|
|
|
|
|
|
|
|
|
|
|
|
功能:一站式运维(启动MediaMTX、切换两种模式、批量控制32台终端、停止所有服务),适合管理员操作。
|