MediaMTX 是一个流媒体服务器:一个 57336320 字节的 exe,起来之后开放 RTSP、RTMP 和 HLS 这几套端口,谁推流进来都能收,谁想看都能拉。
它替掉的是那种「装个 Nginx 配 RTMP 模块,再装 ffmpeg 转码,再写个 HLS 切片脚本」的活。这里一条命令起来,不用配置文件也能跑,需要定制的部分写在 yml 里。
为了验证它到底能不能搬流,我造了一段 20 秒的 720p 测试视频,从 ffmpeg 推进去,再用 ffmpeg 从另一边录出来,中间的耗时和体积都记了下来。
一个 exe 开七个端口
启动瞬间日志里打出一排监听记录,每行一个协议。这份日志本身就是它的能力清单,七个端口一次全开,不需要挑。
| 协议 | 端口 | 用它干什么 |
|---|---|---|
| RTSP | 8554 | 摄像头和 ffmpeg 推流最常用 |
| RTMP | 1935 | OBS 直接往里推 |
| HLS | 8888 | 浏览器播放,切 ts 分片 |
| WebRTC | 8889 | 延迟最低的一路,走 UDP |
| SRT | 8890 | 弱网传输,抗丢包 |
| MoQ | 8892 | HTTP/3 上的新媒体协议 |
| API | 9997 | HTTP 接口,查流状态改配置 |

装的过程只有解压
官方 zip 28734316 字节,里面三个文件:mediamtx.exe、mediamtx.yml 和一份 LICENSE。解压 0.31 秒完事,没有安装向导,不写注册表。
| 文件 | 字节 | 作用 |
|---|---|---|
| 官方 zip | 28734316 | 三个文件打成一个包 |
| mediamtx.exe | 57336320 | 主程序,Go 单文件 |
| mediamtx.yml | 38940 | 默认配置,注释比配置多 |
| LICENSE | 1062 | MIT |
exe 57336320 字节是 Go 编译出来的单文件,双击就能跑。配置默认从同目录的 mediamtx.yml 读,不在就报错退出,这点和大部分绿色软件不一样。
它不做转码,只搬流
推流进来什么样,拉出去就是什么样。H.264 进来还是 H.264,码率一点不变。要转码得自己再接一路 ffmpeg 处理,它这边只负责把流分发出去。
这个设计决定了它的开销很低。同样一路 720p 的流,转码要吃掉几个核,搬流几乎不占 CPU,机器差也能扛。
装之前有三行配置要改
API 默认是关的
# mediamtx.yml 第 147 行
api: false
# Address of the TCP/HTTP listener.
apiAddress: :9997
这一段就是默认值。不改它,9997 端口根本不会开,任何查询接口都返回连接失败。
我第一轮就是被这个绊住的:服务起来了,日志里 RTSP、RTMP 和 HLS 都有监听记录,唯独没有 API 那行。改成 api: true 之后才出现 [API] started with listener on 127.0.0.1:9997。
监听地址默认是整个网卡
默认值是 :8554 这种写法,意思是所有网卡都听。放在内网机器上没问题。
挂到公网就等于把推流口敞开,谁都能往里灌东西。
测试时我把七个地址全改成 127.0.0.1 开头,只给这台机器用。要对外提供服务的场景,得配合防火墙只放行特定来源。
端口占用要提前看
七个端口里 8888 最容易撞。不少开发工具拿它当默认端口,起来之前先 netstat -ano | findstr 8888 看一眼。
撞了不会报得很清楚,日志里是一行 address already in use 然后整进程退出。改 yml 里对应的那一行再起。
推流进来:0.53 秒看到流
用 ffmpeg 把测试视频按原速推过去,命令里 -re 是关键,不加它会用最快速度灌进去,时间对不上。
ffmpeg -re -i test20.mp4 -c copy -f rtsp -rtsp_transport tcp \
rtsp://127.0.0.1:8554/live
推上去之后查 API,从按下回车到流状态变成 ready 是 0.53 秒。API 那一次查询往返 3.0 毫秒。
| 环节 | 数值 | 说明 |
|---|---|---|
| 测试源 | 4089702 | 字节,20 秒 720p 25 帧 1500k |
| 推流到 ready | 0.53 | 秒 |
| API 查询 | 3.0 | 毫秒 |
| 轨道数 | 2 | 条,H264 加 MPEG-4 Audio |
它认两种轨道
API 返回的 tracks 就两项:H264 和 MPEG-4 Audio。视频只认 H.264,音频只认 AAC。
H.265 推上去会被直接拒绝,得先在推流端转一次再送进来。
这条限制影响选摄像头。不少新设备默认 H.265,图的是同画质省一半带宽,接进来之前要先把它调成 H.264。
三种进法,本次只验证了 RTSP
推流口开了三个:RTSP 8554、RTMP 1935、SRT 8890。OBS 默认走 RTMP,摄像头和 ffmpeg 走 RTSP,弱网环境走 SRT。
本次全程用 RTSP,命令里加了 -rtsp_transport tcp。不加这个参数,ffmpeg 默认先用 UDP,UDP 在丢包时会花屏,TCP 更稳但延迟略高。RTMP 和 SRT 这两条路没有验证,不写结论。
| 推流口 | 典型客户端 | 本次是否验证 |
|---|---|---|
| RTSP 8554 | ffmpeg、摄像头 | 验证,0.53 秒变 ready |
| RTMP 1935 | OBS | 未验证 |
| SRT 8890 | 弱网推流器 | 未验证 |
包太大它会自己拆
日志里出现过一行 RTP packets are too big (1460 > 1440), remuxing them into smaller ones。1500k 码率的 720p,关键帧一刀下去超过 MTU,它自己拆包重封。
这一步是自动的,不影响播放,但每拆一次多一次拷贝。推流端把关键帧间隔调大、码率降一点,这行日志就少了。
拉流出去:10 秒录到 1.39 MB
另一边用 ffmpeg 当客户端,从 RTSP 拉 10 秒写进 mp4。这条链路就是「抓直播」的实际做法,直播源换成摄像头或平台地址也是同一套命令。
ffmpeg -rtsp_transport tcp -i rtsp://127.0.0.1:8554/live \
-t 10 -c copy rec10.mp4
结果:耗时 10.42 秒,产出 1389877 字节。10 秒的 720p 落在 1.39 MB,换算下来约 1.11 Mbps,跟推流端设的 1500k 基本吻合。
| 项目 | 数值 |
|---|---|
| 录制时长 | 10.42 秒 |
| 产物体积 | 1389877 字节 |
| 视频轨 | 489 KiB |
| 音频轨 | 117 KiB |
| 封装开销 | 0.715019% |
| 折算每小时 | 500 MB |
| 折算每天 | 12 GB |

录多长由 -t 决定
-t 10 是墙钟时间,不是帧数。想按体积控制就换成 -fs 100M,想一直录到手动停就干脆不写这个参数。
不写 -t 的时候它会一直跟着流写,直播中断才结束。定时录制要自己在外层加计划任务,它不自带这个功能。
录下来的文件能直接播
rec10.mp4 是标准 mp4 封装,1389877 字节里有 489 KiB 视频和 117 KiB 音频,剩下的封装开销占 0.715019%。播放器打开就能看,不用再处理。
这也是 -c copy 的价值:不重新编码,写进去的就是收到的原始帧,画质一点不掉,CPU 也不动。代价是封装格式得选对,要 flv 就写 -f flv,要分段就写 -f segment。
服务端一断,录制端立刻报错
我在测试脚本末尾把服务进程杀了,录制端的画面立刻变成一片红:Failed reading RTSP data: Error number -10054,接着是 Error submitting a packet to the muxer: Broken pipe,最后 Task finished with error code: -32。
这说明录制的可靠性完全压在服务端上。真实场景里要防这个,得让 ffmpeg 带重连参数,或者在录制脚本里写一个「断了就重连」的循环。
浏览器这条路没走通
HLS 的地址是 http://127.0.0.1:8888/live/index.m3u8,用无头 Chrome 打开播放页,页面加载 6.1 秒,但 video 元素的 videoWidth 和 videoHeight 都是 0。
同一时刻日志一直在刷 [HLS] session closed: no stream is available on path 'live'。原因在时序:HLS 要先由请求触发建 muxer,再切出第一个分片,这中间有三四秒是空的,我请求得太早。
| 播放方式 | 本次结果 | 原因 |
|---|---|---|
| RTSP 拉流 | 成功,10.42 秒录 1389877 字节 | ffmpeg 直接解码 |
| HLS 页面 | video 尺寸 0 | 请求早于首个分片生成 |
| WebRTC | 未验证 | 无头浏览器拿不到摄像头栈 |
HLS 要等 muxer 起来
日志的时间线是:请求进来先报 no stream is available,几秒后才有 muxer live created 和 is converting into HLS, 2 tracks。等到这行出现,m3u8 就能拿到了。
要做页面播放,前端得带重试,别一次失败就当流挂了。它自己那个播放页也是靠轮询,不是一打开就出画面。
性能:启动 0.58 秒,API 0.5 毫秒
从进程启动到 API 能应答是 0.58 秒。之后连续查五次流列表,延迟 0.9 / 0.6 / 0.5 / 0.6 / 0.6 毫秒,稳定在半毫秒附近。
| 指标 | 数值 |
|---|---|
| API 就绪 | 0.58 秒 |
| API 延迟 | 0.5 到 0.9 毫秒 |
| 推流到可用 | 0.53 秒 |
| 10 秒录制 | 10.42 秒 |
| 内存占用 | 没测,不作为结论 |
搬运模式下 CPU 几乎不动,瓶颈全在网络和磁盘。10.42 秒录 10 秒的流,多出来的 0.42 秒是建立连接和写文件尾的开销。
几个真踩过的报错
| 症状 | 原因 | 处理 |
|---|---|---|
| API 端口连不上 | yml 里 api: false | 改成 true 再重启 |
| 进程起来就退 | 端口被占用 | netstat 查占用,改 yml 端口 |
| 找不到配置文件 | 不在 exe 同目录 | 用绝对路径传第二个参数 |
| 推 H.265 被拒 | 只支持 H.264 | 推流端先转码 |
| m3u8 返回 404 | 首个分片还没生成 | 等 muxer 建好再请求 |
| 录制端 Broken pipe | 服务端断开 | 录制脚本加重连 |
第一条和最后一条最容易浪费时间。前者看起来像网络问题,其实是配置没开;后者看起来像 ffmpeg 的错,实际是上游没了。
什么场景值得上
| 你的情况 | 结论 |
|---|---|
| 多路摄像头汇到一个出口 | 合适,一个进程全收 |
| 把一路流转成 RTSP 和 HLS 两套给不同端 | 合适,推一次到处能用 |
| 要做录制存档 | 能用,但要自己写录制循环 |
| 要转码、要加水印 | 不合适,它不碰画面 |
| 要账号体系和权限 | 不合适,认证得自己加在前面 |
要判断它适不适合,先算两笔账。第一笔是带宽:一路 720p 按 1500k 算,十路并发下行就是 15 Mbps,家里宽带扛不住,得上行专线或者内网分发。
第二笔是磁盘。按刚测的比例,连录一小时就是半 GB 量级,一天不断写要十来个 GB。要长期存档得配轮转删除,或者只录关键时段。
它的定位是管道,不是平台。上游谁推、下游谁看它不关心,中间这一段的协议转换和分发归它管。
单机跑几路 720p 完全够用。要扛几十路,得考虑带宽和磁盘写入,以及把录制拆到另一台机器上。







