☰
局域网屏幕共享方案全解析:协议选型与低延迟实战
2026/10/15 6:18:38 网站建设 项目流程

1. 方案整体设计与架构拆解

先说结论:局域网里的屏幕共享和镜像,看起来是个小需求,做起来水很深。我这次做的这套方案代号叫 MirrorCast,目标就一个——在完全不依赖外网的情况下,覆盖"手机投屏到电脑""电脑画面共享给多台设备""跨设备传文件"这三个高频场景。

很多人第一反应是:直接用现成工具不行吗?当然行。但真到实际用的时候你会发现,工具A只能手机投电视,工具B只能电脑投电脑,工具C传文件倒是好用但画面延迟高得离谱。更麻烦的是,很多现成方案走的是云中转,数据绕一圈回来,在局域网里反而慢。所以自研一套可控的方案,把视频流和文件传输都收编进统一架构,是值得的。

这套方案的技术栈,我最终圈定了六个关键词:MirrorCast(方案代号)、RTSP、RTP/RTCP、RTC、WebRTC、RTMP。很多人看到这一串协议会头大,我先放一张架构分层逻辑,把它们的定位说清楚。

  • 采集层:负责抓取屏幕画面。Windows 用系统级桌面捕获,macOS 用 CGDisplayStream,Linux 走 X11/Wayland 的 PipeWire,手机端走系统录屏 API。
  • 编码层:把原始画面压成 H.264/H.265 码流,关键参数是分辨率、帧率、码率、GOP 间隔。
  • 传输层:RTSP 负责会话管理和拉流控制,RTP 负责扛实际音视频数据,RTCP 反馈丢包与质量,WebRTC 走 UDP 上的 SRTP 加密通道,RTMP 走 TCP 做低延迟直播兜底。
  • 文件传输层:HTTP + WebSocket 为主,断点续传和目录同步另走自定义 UDP 协议加速。

这套分层最大的好处是:上层业务逻辑不用绑定某个具体协议。同一个采集编码模块,想输出 RTSP 流就推给 RTSP 服务端,想走 WebRTC 就包一层 SRTP,想兼容老设备就推 RTMP。一句话,协议是手段,屏幕共享和文件传输才是目的。

2. 关键协议选型背后的取舍逻辑

2.1 RTSP/RTP/RTCP:传统派的主力打法

RTSP(Real Time Streaming Protocol)的核心角色是"会话导演",它不负责搬运视频数据,只管建立会话、发起播放、暂停、seek 这些控制动作。真正搬运视频数据的活儿,交给 RTP(Real-time Transport Protocol)。

局域网里用 RTSP + RTP 有很现实的优势。RTSP 是基于文本的协议,报文可读性极强,出问题了一抓包就能看懂。RTP 本身不保证可靠传输,但配合 RTCP 的反馈机制,接收端可以定期上报丢包率、抖动、往返时延,发送端据此调整码率或者重传关键帧。

RTCP 有两个关键信息要想清楚:SR(Sender Report)由发送端发出,里面带 NTP 时间戳和 RTP 时间戳的对应关系,这是音画同步的地基;RR(Receiver Report)由接收端发出,包含丢包统计和 jitter 值,这是码率自适应控制的输入源。

我在 MirrorCast 里用 RTSP 方案处理"单向低延迟监控类"场景,比如把实验室某台电脑的画面推给大屏。延迟可以控制在 120ms 左右,这在局域网内是够用的水平。代价是 RTP 本身没有加密机制,同一网段里的其他设备如果开了抓包,可以看到裸流数据。

2.2 WebRTC/RTC:交互场景的默认答案

WebRTC 近些年在局域网共享里越来越吃香,原因很直接:它把网络穿透、加密、自适应码率都做进了协议栈里。就算在纯局域网环境,WebRTC 的 ICE 流程依然能走通,因为它会先做 host 候选的本地探测,直连成功后就绕开了 STUN/TURN 的依赖。

WebRTC 走的是 UDP + SRTP,数据面加密是默认行为,这一点比裸 RTP 安全得多。再加上它内置了拥塞控制(GCC,Google Congestion Control),弱网环境下会主动降码率保流畅,这个特性在无线局域网里非常实用——毕竟 WiFi 的信号抖动是常态。

在 MirrorCast 里,WebRTC 承接的是"双向交互"场景,比如同事之间互相共享桌面、远程协助操作。我实测在普通 WiFi 下,720p 30fps 的屏幕流延迟能压到 200ms 以下,画面基本跟手,文字的边缘锐利度也能接受。代价是 WebRTC 信令流程复杂,需要自己搭建信令服务来交换 SDP(Session Description Protocol)和 ICE 候选。

2.3 RTMP:留给兼容性兜底

RTMP 是老大不小的协议了,新兴方案里它经常被拿来批判,但它有两个特性让它在局域网方案里依然有位置:一是基于 TCP,传输可靠,不会像 UDP 那样一说丢包就花屏;二是几乎所有现成的播放器、编码器都支持推 RTMP 流。

MirrorCast 把 RTMP 放在"兼容性兜底"的位置:当接收端是老旧设备,或者网络环境复杂导致 UDP 被限制时,自动切到 RTMP 通道。代价是延迟会比 RTP/WebRTC 高一些,实测在 500ms 到 1s 之间,但胜在稳。这个取舍我认为很值得,毕竟方案的可用性优先级高于极限性能。

3. 屏幕采集与编码的实操细节

3.1 各平台的采集方式记录

屏幕采集这项工作的复杂度被大多数人低估了。Windows 上老的 GDI 采集方式在画面静止时不耗 CPU,但一旦画面开始滚动就简直灾难,因为 GDI 不做硬件加速。后来我全部切到 Desktop Duplication API(DDA),它走 GPU 的 Copy Engine,能拿到带硬件加速的桌面帧。实测下来,同一台机器同样场景,GDI 采集 CPU 占用 15%,DDA 只有 3%。

macOS 上的情况类似,CGDisplayStream 是不错的系统级接口,支持指定显示器和帧率,拿到的是 IOSurface,可以直接丢给 VideoToolbox 做硬编。Linux 这边稍微麻烦一点,X11 下可以用 XShm + Xrender 抓帧,性能一般;Wayland 下必须走 PipeWire 的 ScreenCast 协议,需要 xdg-desktop-portal 配合授权弹窗,自动化程度低一些。

手机端的高效做法是走系统的屏幕录制 API:Android 用 MediaProjection,iOS 用 ReplayKit。这两个接口都直接产出编码后的 H.264 流,不需要自己再做一遍编码,CPU 开销极小。这里有个坑,MediaProjection 要求必须先弹授权窗口,如果想让投屏流程全自动化,这个权限弹窗必须在第一次连接时就处理好。

3.2 编码参数的取舍实验

屏幕共享场景和摄像头直播场景的编码需求很不一样。摄像头画面是连续运动,屏幕画面则是"大范围静止 + 局部突变"。针对这个特点,我在 MirrorCast 里的编码参数做了如下配置。

  • 编码格式:H.264 High Profile,Level 4.1,兼容性最好,几乎万能播放器支持。
  • 分辨率与帧率:1080p 30fps 作为默认档位;极速档调整到 720p 30fps;文档演示场景可以压到 15fps 省带宽。
  • 码率控制:CBR(恒定码率)用于推流场景,保证带宽占用可预测;VBR 用于本地录制场景,保证静态画面清晰度。
  • GOP 间隔:建议 1 秒一个关键帧,即 30fps 下 GOP=30。关键帧间隔过长会导致接收端秒开时间变长,切流时恢复画面也会变慢。
  • 编码级联参数:开启x264opts=keyint=30:min-keyint=30:scenecut=0,关掉场景切换自动插关键帧,避免镜头切换导致的码率尖峰——这一点对屏幕共享尤其重要,因为屏幕内容切换本质上就是一种"场景切换",如果开着 scenecut,切换窗口时码率会瞬间爆炸。

我用 ffmpeg 做了一套完整的模拟方案,核心命令如下,可以直接在局域网里验证效果:

# 采集屏幕(Windows 下用 gdigrab,Linux 下用 x11grab) ffmpeg -f gdigrab -framerate 30 -i desktop \ -c:v libx264 -preset veryfast -tune zerolatency \ -profile:v high -b:v 4M -maxrate 4M -bufsize 8M \ -g 30 -keyint_min 30 -sc_threshold 0 \ -f rtsp -rtsp_transport tcp rtsp://192.168.1.100/live/screen # 切到 RTMP 通道做兜底 ffmpeg -f gdigrab -framerate 30 -i desktop \ -c:v libx264 -preset veryfast -tune zerolatency \ -b:v 3M -f flv rtmp://192.168.1.100/live/screen

注意:-tune zerolatency这个参数必须加,它会关掉编码器内部的 lookahead 缓冲,虽然牺牲一点压缩率,但能把端到端延迟显著压低。屏幕共享场景延迟比压缩率重要得多。

4. 接收端与播放器的适配实战

4.1 多端播放器选型

MirrorCast 的接收端分三类,每一类的实现思路差别很大。

第一类是桌面播放器。VLC 是最省事的,支持 RTSP 和 RTMP,rtsp://192.168.1.100/live/screen直接开播。需要低延迟模式时,在 VLC 偏好设置里把缓存调小,默认缓存 1000ms 对实时屏幕共享来说偏大,我一般调到 150ms。

第二类是浏览器端。播放 RTSP 就别想了,浏览器原生不支持;RTMP 也已经停止支持。浏览器端最靠谱的方案是走 WebRTC 或 HLS。MirrorCast 里定义了一个菠菜式逻辑:如果对方打开的是浏览器,优先协商 WebRTC,协商失败就转 HLS。

第三类是嵌入式设备,比如树莓派或商业显示器。这类设备性能有限,但通常硬件解码 H.264 的能力是有的。用 ffplay 或者 GStreamer 拉 RTSP 流就够用。

4.2 WebRTC 信令与浏览器的握手实现

WebRTC 无法直接"输入一个 URL 就开始看",它必须经过信令交换:发起端创建 Offer、接收端回应 Answer、然后双方交换 ICE 候选。MirrorCast 的信令服务是用 WebSocket 写的,纯局域网内足够。

一个典型流程是这样的:

  1. 浏览器端访问信令服务,请求观看某个房间。
  2. 信令服务将请求转发给屏幕共享端。
  3. 屏幕共享端创建 RTCPeerConnection,加上屏幕流的 track,生成 SDP Offer。
  4. 浏览器端设置远程描述后生成 Answer,双方开始 ICE 打洞。
  5. 由于是局域网,ICE 的 host candidate 通常第一轮就直连成功,全网延迟很低。

浏览器端播放 WebRTC 流的核心代码大概长这样:

const pc = new RTCPeerConnection({ iceServers: [] // 纯局域网不需要 STUN }); pc.ontrack = e => { const video = document.getElementById('screenVideo'); video.srcObject = e.streams[0]; video.play(); }; // 把共享端的 Offer 设置为远端描述 await pc.setRemoteDescription({ type: 'offer', sdp: offerSDP }); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); // 把 Answer 和 ICE 候选通过信令服务回传 ws.send(JSON.stringify({ type: 'answer', sdp: pc.localDescription }));

ICE 候选这一步最容易被忽略。原来我踩过一个坑:Offer/Answer 都交换成功了,但浏览器黑屏,原因就是 ICE 候选在 trickle ICE 模式下是分开传输的,我漏写了候选转发的逻辑,导致两个端各自拿着半个路由信息,谁也找不到谁。记住一个原则:协商完 SDP 之后,ICE 候选要持续双向转发,直到iceConnectionState变为connected。

4.3 低延迟播放的公共配置项

无论是 VLC 还是自研播放器,有三个参数对屏幕共享体验影响巨大:缓存区大小、渲染方式、音视频同步策略。

  • 缓存区:RTSP 拉流时尽量用 TCP 而不是 UDP,虽然 TCP 有重传开销,但在局域网内带宽宽裕,TCP 可以避免花屏;UDP 适合弱网但需要自己处理丢包。我用 TCP 模式 + 180ms 缓存,延迟和稳定性最均衡。
  • 渲染方式:Windows 上用硬件加速渲染(如 D3D11VA),纯软件渲染在 4K 屏共享时会明显掉帧。macOS 上 VideoToolbox 的渲染效率也很关键。
  • 音视频同步:RTSP 流中 RTP 的音视频时间戳基数是独立且随机的,需要根据 RTCP SR 报文里的 NTP 时间戳换算对齐。VLC 有 autosync 机制,自研播放器必须手动实现这个换算,否则画面和声音各自为政。

5. 文件传输功能的集成思路

标题里提到文件传输,我把它设计成 MirrorCast 的一个独立但共存的模块。屏幕共享解决"看"的问题,文件传输解决"拿"的问题。开会时共享一份文档,看完直接拖拽到共享区,对方下载,一气呵成。

文件传输走 HTTP 是最稳的,所有语言都有成熟库,浏览器天然支持。我这边给了三个实现档位:

  • 小文件(<10MB):一次性 HTTP 上传下载,用内存流,不落盘。
  • 中文件(10MB 到 1GB):HTTP 分片 + 断点续传,每片 2MB,记录已传分片索引。
  • 大文件(>1GB):先走 HTTP 校验身份和握手,再切换自定义 UDP 传输协议,利用局域网带宽做数据洪泛,目标是把千兆网跑满。

大文件走 UDP 需要解决三个问题:

  1. 确认机制:使用类 TCP 滑动窗口,选固定的窗口大小,比如 512 个包。发送端一次性发完窗口内数据,接收端批量 ACK,不丢包就保持高速。
  2. 乱序重组:每个数据包带 8 字节序号头,接收端用哈希表缓存乱序包,到达后按序写入文件。
  3. 流量整形:我用了一个非常保守的拥塞控制算法:如果连续三个窗口内丢包率超过 2%,就把窗口大小减半;如果持续健康,每两个窗口加 4 个包的发送量。这个算法不追极限,但稳定,对局域网场景足够。

最终实测结果,两设备通过千兆有线网传 10GB 文件,UDP 通道稳定在 850Mbps 左右,比 HTTP 方案快了近一倍。代码层面,核心发送逻辑可以用下面这个缩略版做骨架:

# simplified UDP file transfer sender import socket, struct sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) seq = 0 window = 512 base = 0 acked = set() while base < total_packets: # fill window while seq < min(base + window, total_packets): payload = struct.pack('>Q', seq) + chunks[seq] sock.sendto(payload, addr) seq += 1 # wait ACK while base in acked: base += 1 # adjust window based on loss rate

6. 实测中的常见问题与排查思路

6.1 画面延迟忽然飙到 2 秒以上

先看发送端有没有被降帧。屏幕共享时如果后台有大型应用占满 GPU,编码器拿不到帧就会自动跳帧。排查手段:看编码器的 FPS 指标,如果原本 30fps 掉到 15fps 以下,先解决 GPU 占用问题。

再看接收端缓存区。VLC 的缓存如果当时配置是自动模式,它会在检测到轻微抖动时不断加大缓存,延迟就这么被加上去了。把缓存固定为一个保守值(150ms 左右)就能避免。

最后看网络层。局域网里如果交换机没开启 QoS,同网段有一条大流量下载就能冲垮视频流。WiFi 环境下建议把屏幕共享设备的网卡设置为最高优先级,或者干脆走有线。

6.2 WebRTC 画面卡住,但 ICE 状态正常

这种情况多半是 FEC(前向纠错)和 NACK(丢包重传)在局域网内反而成了负优化。WiFi 下瞬时丢包率可能达到 1%~2%,NACK 机制会请求重传,但如果突发重传包又继续丢,画面就会卡住。

处理办法:在 RTCPeerConnection 的参数里调低丢包容错阈值,或者关闭 FEC 只保留 NACK。屏幕共享对实时性要求高,牺牲一两个 GOP 内帧的完整性,换来的流畅度提升很值得。

6.3 RTSP 拉流花屏问题

如果 RTSP 走的是 UDP 传输,拉流设备带宽波动时 RTP 包丢失会导致画面花屏。前面提过,局域网内优先切到 TCP 传输。ffmpeg 里指定-rtsp_transport tcp,VLC 里在网络选项强制选 TCP。TCP 重传会带来额外延迟,但局域网内带宽充足,这个代价可以接受。

6.4 多台接收端同时拉流时发送端压力过大

单路编码流推给多个接收端,发送端编码压力还是不变,压力会转移到转发节点上。两种解法:

  • 在发送端拉起 RTSP 服务,每来一个接收端就给它建一个独立的 RTP 会话,但编码只做一次,多个会话共用同一个编码帧缓冲。
  • 在局域网里放一个轻量级媒体服务器做流转发,发送端只需要推一路流到服务器,服务器负责扇出。我实测在普通 MiniPC 上,轻量媒体服务器能扛住 30 路以内的并发拉流。

6.5 浏览器端 WebRTC 屏幕流只有画面没有声音

多数浏览器默认不会自动播放带声音的媒体流,必须在用户手势里调用video.play()之后audioTrack才会激活。更隐蔽的情况是:共享端没有采集到系统的默认音频输出设备。Windows 的扬声器如果处于静音或休眠状态,虽然采集 API 说有流,但音频数据是静音。排查方法:把共享端的音量调到非零并播放一段音乐,再看浏览器端是否有声音。

6.6 大文件 UDP 传输时,其他设备网络卡死

UDP 洪泛的代价是可能占满交换机的转发能力。我的解决办法是把 UDP 传输的带宽限制做成可配置参数,默认不超过千兆网带宽的 80%(即约 800Mbps),并且支持 QoS 标记 DSCP CS4,让交换机优先转发视频流业务,文件传输排队。这个细节在办公环境尤其重要,否则一次大文件同步,整个办公室的 Video 会议全卡。

7. 实操总结与个人经验补充分享

折腾完这套 MirrorCast 方案,我个人的几个直接建议:

  • 视频传输协议不要只绑定一个,RTSP + WebRTC + RTMP 三个通道都能输出同一路流,这是方案体面的底线。协议切换逻辑放在配置层,不要写死在代码里。
  • 帧率和码率参数要留出动态调节的接口。局域网看似带宽大,但 WiFi 场景下波动很吓人。我在 MirrorCast 里加了手动挡位:流畅(720p 30fps/2Mbps)、高清(1080p 30fps/4Mbps)、极限(1080p 60fps/8Mbps),开会时根据现场情况手动切。
  • 屏幕共享的编码参数里,关掉场景切割(scenecut)这个操作,我第一次实测时就被这个坑过。平时摄像头直播开 scenecut 是优化,屏幕共享开 scenecut 就是灾难。
  • 文件传输一定要做断点续传和完整性校验。局域网也不是永远可靠的,一次 10GB 的传输可能因为 WiFi 断联白干一半。每个分片收完立刻计算 MD5,发送端维护校验清单,全部完成再合并。
  • 把日志做细。这个方案里我打了三类日志:编码器日志(帧率、码率、跳帧数)、网络日志(实际吞吐、丢包率、RTT)、设备日志(接收端的解码帧率、缓存水位)。实测排查问题时,这三类日志配合抓包工具,几乎能定位所有疑难杂症。
  • 最后是安全习惯。RTSP 裸流在局域网里不加密,如果对敏感信息有要求,路由到 WebRTC 通道,它自带 SRTP。文件传输也建议至少加一层 TLS 或简单的口令校验,局域网不等于安全区。

MirrorCast 这套方案的出发点很简单——把屏幕上能看到的东西,稳定、低延迟、跨设备地送到目的地,同时把文件也一并捎过去。技术上不追求新奇,把现有协议用好、用对,比堆砌酷炫技术更实际。我后面还打算扩展一个"多房间分组"的能力,让不同小组各自共享各自的桌面,互不串流,这个等实现跑通了再单独写一篇分享。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询