ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Ubuntu桌面共享:FFmpeg+MediaMTX实现RTSP/RTMP推流

2026/9/9 7:38:27 拓冰建站 浏览量
Ubuntu桌面共享:FFmpeg+MediaMTX实现RTSP/RTMP推流 简介DesktopSharing是一套基于C的桌面共享与推流解决方案覆盖从采集、编码到网络传输的完整链路面向音视频开发者、流媒体学习者和需要自建屏幕采集推送服务的工程师也可作为毕业设计或音视频入门实战项目。项目抓取屏幕与声卡的音视频数据经H.264/AAC编码后支持RTSP本地转发、RTSP推流和RTMP推流并集成了DXGI屏幕采集、WASAPI音频采集、NVIDIA NVENC独显硬编和Intel QSV核显硬编等关键模块同时提供基于imgui/SDL2的简单UI界面。资源包为zip压缩格式大小约21.03MB包含可在VS2017/VS2019下编译的完整工程源码及依赖模块说明。目前已有1689人学习下载适合具备一定C基础、希望深入理解音视频编码与推流链路的开发者参考。通过本项目可以快速掌握从屏幕音频采集、编码到RTSP/RTMP推送的完整流程尤其适合研究RTSP/RTMP协议交互、硬件编码适配以及桌面共享应用搭建的人群。 做了这么久流媒体相关的东西我一直觉得“桌面共享”这四个字被大家想窄了。大部分人的第一反应是向日葵、ToDesk、VNC但真要遇到“把一台Ubuntu 24.04机器的桌面画面用标准RTSP/RTMP协议对外分发让任意播放器、算法进程都能拉流”这种需求那些远程控制软件就完全不对味了。这个项目“DesktopSharing”做的就是这件事把桌面画面变成一路能被标准化消费的流媒体同时支持RTSP转发、RTSP推流、RTMP推流。整条链路梳理下来并不复杂核心就三板斧桌面捕获、编码推流、媒体服务器做协议转换与分发。但这三板斧里面藏了不少坑尤其是Ubuntu 24.04从X11往Wayland切换之后采集方式变了编码参数调不好拉流端延迟能让你怀疑人生OpenCV打开RTSP失败更是家常便饭。这篇文章我把完整的搭建过程、参数计算逻辑和踩坑记录都摊开讲适合手头有Ubuntu机器、需要做屏幕推流或者搞视频监控分发的人直接照着抄。1. 项目思路与方案选型为什么是“RTSPRTMP”双通道先说清楚这个项目的核心链路桌面画面通过FFmpeg采集并编码推送到一个流媒体服务里再由这个服务对外提供RTSP和RTMP两种拉流入口。有人可能会问直接推一路RTSP不就行了为什么还要带RTMP答案在消费端。RTSP是监控、算法、低延迟播放的老本行VLC、FFplay、OpenCV、甚至海康/小米这类摄像头生态全都认识它而RTMP则是直播生态的事实标准手机App、网页播放器、各类直播平台基本都走RTMP。你只推RTSP意味着网页端和移动端想直接看就得另做转码或者找中间层你只推RTMP那本地算法进程、OpenCV、监控平台再过来拉流又是一顿折腾。两路都出所有端都直接闭嘴干活这是我在实际对接中总结出来的最省沟通成本的做法。1.1 先弄明白三件事推流、拉流、转发这个项目里会反复出现这三个词我用自己的话解释一下推流把本地采集到的画面数据通过某种协议主动发送给服务器。FFmpeg往RTSP服务器推流、OBS往直播平台推RTMP都是推流。拉流播放端或者消费端主动从服务器获取流数据。用户拿VLC打开一个RTSP地址、OpenCV的VideoCapture去读一个RTSP URL都是拉流。转发服务器把收到的流复制多份再通过相同或者不同的协议分发给多个下游。桌面这台机器推上去一路流十台设备同时拉流观看就是服务器在转发。我最初踩过的一个认知误区是以为推流端和拉流端数量必须一一对应。其实只要中间有一个媒体服务器推流端永远是只需要推一路剩下的复制分发是服务器的事。这也是为什么这个项目一定要在桌面机器和消费端之间架一个服务层的原因。1.2 为什么最终选了MediaMTX而不是Nginx-RTMP媒体服务器我纠结过两个方案Nginx加nginx-rtmp-module或者MediaMTX前身叫rtsp-simple-server。两个我都实际搭过说下对比项目Nginx-RTMPMediaMTX协议支持主要是RTMPRTSP要额外折腾原生同时支持RTSP、RTMP、HLS、WebRTC配置复杂度改nginx.conf模块编译麻烦单个yaml文件结构清晰拉流时延偏高需要调优默认低延迟设计资源占用Nginx为主体较重单二进制非常轻量从RTSP自动转RTMP需要额外配置收到RTSP推流后RTMP出口自动可用实际用下来MediaMTX赢了。最核心的一个理由是它天然具备“协议转换”能力我只需要往它的RTSP地址推流它内部就把流同时挂到了RTMP入口上下游用哪个协议拉都行完全不需要我手动做转封装。而Nginx-RTMP虽然也能做到但配置路径又臭又长尤其在新版Ubuntu上编译模块每次系统更新我都提心吊胆。当然Nginx-RTMP不是没有优势如果你本来就有Nginx在跑、想顺便做HLS切片或者CDN分发那它依然是个好选择。但单纯为了桌面共享这种轻量场景MediaMTX的敏捷性完胜。2. 桌面捕获与编码推流前的最后一公里媒体服务器搞定之后下一个核心问题就是桌面画面怎么高效地变成编码器能吃的帧数据。这一步看起来简单实际上在Ubuntu 24.04上藏了不少变数。2.1 X11采集与Wayland的坑FFmpeg采集桌面最经典的方式是x11grabffmpeg -f x11grab -video_size 1920x1080 -framerate 25 -i :0.0 -c:v libx264 ...:0.0指代本地X Server的0号显示器的0号屏幕。这个方案在X11会话下非常稳定一台老机器挂着循环推流跑上一周都不带掉线的。但Ubuntu 24.04默认的GNOME会话已经切换到Waylandx11grab直接失效。如果你在Wayland环境下执行上面的命令大概率会报错或者抓到黑屏原因是FFmpeg根本没有权限访问Wayland的合成器输出。我的解决思路有两个登录系统时在登录界面右下角齿轮里选择“Ubuntu on Xorg”切回X11会话再采集。这是最省事、最稳的方案。如果是无头服务器压根没有物理显示器那就直接用Xvfb建一个虚拟显示器Xvfb :99 -screen 0 1920x1080x24 export DISPLAY:99在服务器上启动一个虚拟屏幕往里面跑应用再通过x11grab抓:99。这个组合是远程无头桌面共享的绝配我有一台跑自动化脚本的机器就是这么干的。2.2 编码参数怎么定才不卡顿桌面共享不是电影直播画面里大量静态区域但偶尔会有快速滚动的窗口或者高帧率的动画。编码器我固定用H.264因为兼容性最好。关键参数是这三组ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 -i :0.0 \ -c:v libx264 -preset veryfast -tune zerolatency \ -pix_fmt yuv420p -g 50 -b:v 4M \ -f rtsp rtsp://localhost:8554/desktop-preset veryfast牺牲一点压缩率换编码速度。桌面画面实时性优先别用slow那种档位编码延迟直接拉满。-tune zerolatency这是H.264编码器里最关键的“低延迟开关”它会压制B帧、控制线程调度让画面从采集到推出去的延迟降到最低。不加这个参数即使网络再好画面也会在编码器里积压出几百毫秒的延迟。-g 50也就是GOP关键帧间隔设为50帧按25fps算就是每2秒一个I帧。这个值不是随便定的它决定了拉流端的“起播速度”。拉流端打开播放器时必须等到下一个I帧才能开始解码出画面GOP越大客户端黑屏等待的时间就越长。但GOP也不能太小否则I帧太多、码率爆炸。50到100之间是我常用的平衡区间低延迟场景就取50。码率这块静态桌面2Mbps完全够用但如果要展示视频播放或者动效建议给到4-5Mbps。要是不想手动算可以直接用-crf 23代替固定码率让编码器自适应画面复杂度桌面共享这种场景用CRF模式反而更省心。2.3 要不要顺带采集音频如果你需要桌面共享的同时带系统声音FFmpeg可以加一条音频输入-f pulse -i defaultUbuntu桌面默认走PipeWire但FFmpeg用pulse接入一般也能兼容。音频编码用AAC-b:a 128k就够。需要注意如果只是做监控或者远程操作辅助音频不是必需品而且一旦加上音频整个推流链路的同步问题会多出很多建议前期先把纯视频跑通再加音频。3. 完整落地从零搭建桌面共享服务下面把这套服务的完整落地过程过一遍包含所有我实际敲过的命令和配置。3.1 安装MediaMTX并配置双协议入口MediaMTX的安装非常简单官方GitHub Release里一个压缩包解压出来是单个二进制和一个yaml配置文件。我习惯把它放到/opt/mediamtx然后写一个systemd服务来守护进程。配置文件mediamtx.yml里核心这样设置rtsp: yes rtmp: yes hls: no webrtc: no paths: desktop: camera:desktop和camera两个路径就是对外暴露的流名。客户端推流到rtsp://服务器IP:8554/desktop或者rtmp://服务器IP:1935/desktop拉流也是同样的地址。因为RTMP和RTSP共用同一个路径名MediaMTX会自动在协议之间做映射不需要额外写转换规则。如果对公网开放强烈建议加一层用户名密码鉴权paths: desktop: readUser: viewer readPass: yourpassword publishUser: pusher publishPass: pushsecret这里有个细节MediaMTX允许把“推流凭证”和“拉流凭证”分开设置。推流端只有知道publish账号密码的人才能往上推而观看者用read账号就能拉流。这个机制在多人协作的场景里非常实用避免所有角色都共用同一套高权限凭证。3.2 用FFmpeg把桌面推上去环境准备完成后推流命令如下X11会话环境ffmpeg -f x11grab -video_size 2560x1440 -framerate 30 -i :0.0 \ -c:v libx264 -preset veryfast -tune zerolatency \ -pix_fmt yuv420p -g 60 -crf 23 \ -f rtsp rtsp://localhost:8554/desktop我常用CRF模式代替固定码率好处是桌面长时间不动时实际码率会掉到几百Kbps节省大量带宽而画面动起来时又能自动加大码率保证清晰度。我还会再加一条RTMP推流做备份ffmpeg -f x11grab -video_size 2560x1440 -framerate 30 -i :0.0 \ -c:v libx264 -preset veryfast -tune zerolatency \ -pix_fmt yuv420p -g 60 -crf 23 \ -f flv rtmp://localhost:1935/desktop等等这里要提醒一个关键点。如果你同时推两路流到同一个路径名MediaMTX默认会报错因为同一个路径在同一时刻只允许一个发布者。所以实际操作中我推一路RTSP进来就够了因为MediaMTX会自动把它映射到RTMP出口上。上面这个RTMP推流命令其实是想测试“RTMP推流”这个项目能力时单独开的另一个路径比如推给rtmp://localhost:1935/desktop_rtmp用来验证RTMP入口是否正常工作。3.3 多端拉流验证VLC、FFplay、OpenCV推流稳定运行后分别用三种方式拉流验证VLC打开网络串流输入rtsp://192.168.1.100:8554/desktopVLC的好处是无脑、兼容性强手机端VLC也能直接拉。FFplay做低延迟预览更合适ffplay -fflags nobuffer -flags low_delay -framedrop -probesize 32 rtsp://192.168.1.100:8554/desktop-fflags nobuffer和-framedrop是降低播放延迟的关键前者让播放器不等数据攒够就解码后者在解码速度跟不上时主动丢帧保证画面实时性。实测下来这个命令的延迟在200-400毫秒左右远低于VLC默认的1秒以上。OpenCV拉流则更能代表“算法进程消费”的典型场景import cv2 cap cv2.VideoCapture(rtsp://192.168.1.100:8554/desktop) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while True: ret, frame cap.read() if not ret: break # 在这里对 frame 做你要做的处理注意CAP_PROP_BUFFERSIZE必须设成1否则OpenCV内部会默认缓冲几十帧你拿到手的画面永远是好几秒之前的旧帧。这个坑我吃了大亏一开始没设置做实时检测时发现画面慢半拍调了半天最后发现是缓冲区积压。4. RTSP转发把摄像头的流也纳管进来桌面共享做完之后很自然就想把已有摄像头系统的RTSP流转发也一起纳管。尤其像门店监控、办公区巡查这种场景桌面画面、海康摄像头、小米摄像头最好能在一个串流体系里统一访问。4.1 配置上游拉流MediaMTX支持“主动去上游拉流”再转发给下游的source模式。在配置里写清楚上游地址即可paths: desktop: camera: source: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101这一行配置的含义是MediaMTX启动后会主动向海康摄像头的rtsp://...地址发起拉流请求然后把拉到的流挂载到本机camera路径下。客户端访问rtsp://服务器IP:8554/camera就能看到摄像头画面而且完全不需要知道摄像头本身在哪、密码是什么。这个模式的好处特别明显摄像头往往散落在不同网段或者藏在NAT后面逐个给它们做映射、记地址很痛苦。统一拉到MediaMTX之后所有下游只有一个入口地址鉴权和访问控制也能集中管理。4.2 一个服务统一输出桌面摄像头有了桌面推流和摄像头拉流MediaMTX的配置文件里就能同时存在多个路径。实际部署时我会让桌面画面和摄像头画面通过同一个MediaMTX实例对外服务配合前面提到的用户名密码机制实现“一套服务、多路画面、统一鉴权”。热词里那些“小米摄像头rtsp取流教程”“海康威视rtsp取流地址”的搜索需求本质都是在找摄像头输出的RTSP URL格式。接入到MediaMTX后你根本不用记这些厂商不统一的URL规则映射成一个短路径名就行。对做集成的同学来说这一层抽象能省下大量联调时间。5. 异常排查那些让人崩溃的坑整个项目从零搭起来踩过的坑能绕会议室一圈。挑几个最有代表性的说说。5.1 OpenCV打开RTSP失败这个问题的概率极高尤其是直接在Mac或者Windows上跑Python的OpenCV去拉RTSP常常直接返回False。常见原因有三层安装的OpenCV是精简版没有编进RTSP所需的FFmpeg后端。解决方式是用opencv-python这种带FFmpeg的发行包或者干脆在Ubuntu上装python3-opencv系统包。网络不通目标端口被防火墙拦了。先用nc -vz 192.168.1.100 8554测下端口是否可达。RTSP地址格式不对注意rtsp://后面的路径要和MediaMTX配置里的路径名完全一致。5.2 画面延迟越来越大前面已经涉及了这个问题的核心除了播放端要关缓冲之外还有一个重要点确认摄像头或者推送端用的是TCP传输模式还是UDP模式。VLC在拉流时如果开着UDP丢包会导致花屏和长时间卡顿FFmpeg推流时可以强制指定传输协议-rtsp_transport tcp加上这个参数后再推流延迟和稳定性都会好不少。UDP虽然天生延迟低但在弱网环境下重传机制缺失体验反而更不稳定。5.3 公网访问的安全顾虑RTSP协议本身不加密而且默认端口554和8554是扫描器的重点关注对象。如果你非要把流开放到公网一定要做三层防护MediaMTX里启用鉴权这是底线。在防火墙层面限制来源IP只允许公司出口IP或者可信网段访问。不要把服务器直接暴露在公网用跳板机或者安全组只映射必要端口。踩过这个坑之后我现在的默认原则是非必要不公网非要公网就加鉴权并且把拉流账号设置为只读权限。5.4 推流进程频繁崩溃FFmpeg推流命令挂在终端里一旦终端关闭或者断网进程就没了。这是新手最容易犯的错误。我用的是systemd服务来托管推流进程写成这样一个unit文件[Unit] DescriptionDesktop Streaming Afternetwork.target [Service] ExecStart/usr/bin/ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 -i :0.0 -c:v libx264 -preset veryfast -tune zerolatency -pix_fmt yuv420p -g 50 -crf 23 -rtsp_transport tcp -f rtsp rtsp://localhost:8554/desktop Restartalways RestartSec3 [Install] WantedBymulti-user.targetRestartalways保证了进程挂掉后3秒自动拉起因为桌面推流这种服务没人会24小时盯着终端看是否掉线。配置好之后systemctl enable开机自启整个桌面共享服务就是一套无人值守的常驻服务了。6. 项目扩展还有哪些可以玩的方向这套“桌面共享RTSP/RTMP双协议推流”的基础设施搭好后往上叠加的应用真的很多。我这边已经在用的几个方向给你参考无头服务器远程桌面配合Xvfb虚拟屏幕不用物理显示器也能对外开放一个“虚拟桌面”跑浏览器自动化、批量任务渲染都很实用。教学录播与实时演示老师在自己电脑桌面推一路流学生端不管用什么播放器、什么系统能联网就能看到画面。与算法框架打通OpenCV、TensorFlow等直接以RTSP为数据源做实时图像识别、人数统计不需要额外的采集SDK。多路监控画面汇合把不同厂商的摄像头全部拉到MediaMTX统一暴露集中控制访问权限。这个项目其实已经远远超出了“桌面共享”字面上的范畴本质是搭建了一个标准化的实时画面接入与分发平台。整套方案轻量、可控、代码量极少部署一台普通配置的Ubuntu机器就够了。我已经把它用到日常的自动化演示和监控画面汇总里从配置完成到稳定运行几乎没有再操心过。最后分享一个实用小技巧如果你同时管理多台推流机器在MediaMTX的路径命名上就按“位置用途”来比如house_front_door、office_lobby比单纯叫stream1、stream2好维护十倍。这个经验是我重建了三次配置之后总结出来的教训。本文还有配套的精品资源点击获取