MediaMTX 定时抓帧快照:基于 runOnAvailable 钩子 + FFmpeg 的流截图方案
【免费下载链接】mediamtxReady-to-use Media-over-QUIC / SRT / WebRTC / RTSP / RTMP / LL-HLS / MPEG-TS / RTP live media server and media proxy that allows to read, publish, proxy, record and playback real-time video and audio streams.项目地址: https://gitcode.com/GitHub_Trending/me/mediamtx
MediaMTX 作为通用的实时媒体服务器,本身不做视频解码,但可以通过runOnAvailable钩子把"流已就绪"这个事件暴露给外部命令,从而借助 FFmpeg 从 RTSP 拉流并周期性地抽取单帧画面保存为 JPG 快照。本文围绕 docs/2-features/13-extract-snapshots.md 中的方案,完整讲解快照目录规划、抓帧命令、间隔控制、钩子生命周期与环境变量,并结合仓库源码说明底层调用机制,读完即可在自己的 MediaMTX 实例上落地一套"按路径定时出图"的监控截图流程。
方案概览:为什么用 runOnAvailable 而不是手动脚本
runOnAvailable是 MediaMTX 提供的路径级钩子(Hook),当某个 path 上开始有可读的流时触发,执行指定的外部命令;当流不再可用时,该命令会收到 SIGINT 被终止(详见 钩子文档)。这与本需求的契合点在于:
- 快照抓取逻辑完全在钩子命令里实现,MediaMTX 只负责"流一上线就拉起命令、流一下线就停掉命令",无需解码任何视频数据;
- 抓帧用 FFmpeg 完成,MediaMTX 不需要内置任何转码能力,方案与"媒体服务器只做分发"的架构保持一致;
- 命令内部是一个
while true循环,天然支持以固定间隔(如 10 秒)反复抽帧,直到流消失为止。
因此,该方案的核心是"钩子负责生命周期、FFmpeg 负责抽帧、shell 循环负责定时"三段式配合。
基础配置:完整的抓帧钩子示例
在配置文件(仓库根目录的 mediamtx.yml 即为其标准模板)中,把钩子写进pathDefaults,可对所有 path 生效:
pathDefaults: runOnAvailable: | bash -c " while true; do mkdir -p $(dirname snapshots/$MTX_PATH) ffmpeg -i rtsp://localhost:8554/$MTX_PATH -frames:v 1 -update true -y snapshots/$MTX_PATH.jpg sleep 10 done"其中10就是两次快照之间的间隔秒数,可按需调整。
逐行拆解这段命令:
| 片段 | 作用 |
|---|---|
bash -c "..." | 用 bash 执行多行脚本;YAML 的\|块标量保留换行,配合双引号字符串即可承载整个循环体 |
mkdir -p $(dirname snapshots/$MTX_PATH) | 依据路径名创建对应目录。$MTX_PATH是钩子注入的环境变量,值为 path 名(可能含/,如cam/gate),因此先取dirname建目录,避免 FFmpeg 因目录不存在而写入失败 |
ffmpeg -i rtsp://localhost:8554/$MTX_PATH | 从本机 RTSP 服务(默认端口 8554)拉取与 path 同名的实时流作为输入 |
-frames:v 1 | 只解码输出 1 帧视频画面,即"抓一张图" |
-update true | 让 image2 muxer 进入"单图覆盖"模式:重复写入同一个文件时覆盖旧文件,而不是自动追加编号(如out-001.jpg、out-002.jpg) |
-y | 输出文件已存在时直接覆盖,配合循环中的重复抓帧,保证快照始终是最新一帧 |
snapshots/$MTX_PATH.jpg | 输出文件路径,最终效果是snapshots/<path名>.jpg,例如snapshots/cam/gate.jpg |
sleep 10 | 抓完一帧后休眠 10 秒,形成周期性的快照节奏 |
这里有一个值得注意的细节:RTSP 地址中的$MTX_PATH和输出文件名中的$MTX_PATH会同时被展开。MediaMTX 的钩子环境变量通过os.Expand注入(见 internal/externalcmd/cmd.go 中的expandEnv实现),因此同一变量可复用于拉流 URL、目录结构与文件名,一个 path 对应一套完整快照。
钩子的可用环境变量
runOnAvailable触发时,MediaMTX 会注入以下环境变量(与 mediamtx.yml 中该钩子注释以及 钩子文档 的描述一致):
| 变量 | 含义 |
|---|---|
MTX_PATH | path 名称 |
MTX_QUERY | 发布者携带的查询参数(URL 编码) |
MTX_SOURCE_TYPE | 流来源类型(如rtsp、rtmp、webrtc、rtspPull等) |
MTX_SOURCE_ID | 流来源的唯一 ID |
RTSP_PORT | RTSP 服务端口 |
G1、G2、… | 若 path 名是正则表达式,则为各捕获组匹配到的内容 |
其中MTX_SOURCE_TYPE与MTX_SOURCE_ID在源码中由钩子调用处显式注入(见 internal/hooks/on_available.go),可用来在快照文件名中区分不同来源的流。
快照方案的关键参数与调优
抓帧间隔
sleep 10的值即间隔秒数。间隔越小快照越密集,但会持续占用 FFmpeg 进程与网络带宽;由于 FFmpeg 每次都要重新建立 RTSP 会话并解码到关键帧,sleep过短(如小于 1 秒)意义不大,一般监控场景取 5~30 秒较合理。
帧选择与画质
-frames:v 1取的是解码后输出的第一帧画面,具体是哪一帧由推流端的关键帧间隔(IDR 周期)与 FFmpeg 解码节奏决定,并不能保证是"当前最新画面"。若需要控制快照画质,可追加-q:v参数指定 JPEG 质量(如-q:v 2质量较高),例如:
ffmpeg -i rtsp://localhost:8554/$MTX_PATH -frames:v 1 -q:v 2 -update true -y snapshots/$MTX_PATH.jpg输出格式
-update true配合.jpg后缀是"周期性覆盖单张图"的标准写法。如果希望每次抓帧都保留历史,可以把文件名改为带时间戳的格式(注意此时不应使用-update true):
ffmpeg -i rtsp://localhost:8554/$MTX_PATH -frames:v 1 -y "snapshots/$MTX_PATH-$(date +%Y%m%d%H%M%S).jpg"只对特定 path 生效
pathDefaults会作用于所有 path;若只想对某一路流抓帧,可把钩子写在paths下对应的 path 条目中,实现"逐路定制间隔与目录":
paths: gate_cam: runOnAvailable: | bash -c " while true; do mkdir -p snapshots/gate_cam ffmpeg -i rtsp://localhost:8554/gate_cam -frames:v 1 -update true -y snapshots/gate_cam.jpg sleep 5 done"与钩子生命周期配套的配置项
runOnAvailableRestart
默认false(见 mediamtx.yml 中runOnAvailableRestart: false)。当命令在流存续期间意外退出时,置为true会让 MediaMTX 自动重启命令;从源码看,重启前会有 5 秒的固定停顿(见 internal/externalcmd/cmd.go 中的restartPause = 5 * time.Second),避免崩溃后高频重试。对于快照这种长时间循环任务,建议保持默认false或自行在脚本内处理错误。
runOnUnavailable
流不可用(消失、断流)时执行的命令,环境变量与runOnAvailable相同。在抓帧场景里,可用它做收尾清理,例如删除孤立的快照文件:
pathDefaults: runOnAvailable: | bash -c " while true; do mkdir -p $(dirname snapshots/$MTX_PATH) ffmpeg -i rtsp://localhost:8554/$MTX_PATH -frames:v 1 -update true -y snapshots/$MTX_PATH.jpg sleep 10 done" runOnUnavailable: rm -f snapshots/$MTX_PATH.jpg与旧的 runOnReady 的兼容
历史版本中的runOnReady/runOnReadyRestart/runOnNotReady已废弃,配置解析时会自动迁移到runOnAvailable/runOnAvailableRestart/runOnUnavailable(见 internal/conf/path.go 的迁移逻辑),旧配置仍可继续工作,但新配置请使用runOnAvailable系列参数。
源码视角:runOnAvailable 钩子的实际触发链路
runOnAvailable钩子在内核路径对象中随流上线被注册。在 internal/core/path.go 中可以看到调用点:当流初始化成功、availableTime被记录之后,MediaMTX 立即构造hooks.OnAvailableParams并调用hooks.OnAvailable,返回的闭包即为后续用于流不可用时的回调。
internal/hooks/on_available.go 中的实现进一步揭示了生命周期管理逻辑:
- 只要
RunOnAvailable非空,就以externalcmd.Cmd方式启动命令,并按RunOnAvailableRestart决定是否自动重启(Cmd.Restart字段); - 命令运行期间日志会输出
runOnAvailable command started;退出时输出runOnAvailable command exited,便于在日志中排查抓帧脚本异常; - 返回的闭包被保存为
onUnavailableHook:当流不可用时,先向运行中的抓帧命令发送 SIGINT(对应钩子文档中"流不再可用时以 SIGINT 终止命令"的说明),随后执行runOnUnavailable命令(若配置)。
这个"启动时注入环境变量、结束时发 SIGINT"的机制,正是抓帧循环能够"流在就抓、流断就停"的底层保证:脚本里的sleep 10循环不会在无流时空转,也不会在流恢复后残留多个 FFmpeg 进程。
验证与排错
配置完成后,可通过以下步骤确认快照链路是否正常:
- 用任意方式向某个 path 推流(如
ffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://localhost:8554/mypath),或触发runOnInit/runOnDemand拉起静态源; - 等待约一个
sleep间隔后,检查snapshots/mypath.jpg是否生成,图片内容是否为流中最新画面; - 在 MediaMTX 日志中查找
runOnAvailable command started与(若异常退出时的)runOnAvailable command exited记录; - 停止推流,确认抓帧进程被 SIGINT 终止(日志出现
runOnAvailable command stopped),若配置了runOnUnavailable,验证其收尾命令是否按预期执行。
常见问题:快照目录不存在导致 FFmpeg 报错(务必保留mkdir -p步骤)、$MTX_PATH含子路径时输出文件名嵌套目录(由dirname逻辑处理)、以及-update true缺失导致生成带编号的递增文件而非单张覆盖图。
小结
通过runOnAvailable钩子 + FFmpeg 的-frames:v 1 -update true组合,MediaMTX 在不引入任何转码负担的前提下,即可为任意 path 提供周期性单帧快照。该方案完全复用 MediaMTX 已有的钩子生命周期管理(启动/终止/重启/环境变量注入),配置仅需一段 shell 脚本,同时支持按 path 定制、按来源区分、断流清理与历史留档等扩展,是监控大屏、安防预览、封面生成等场景的轻量级截图方案。
【免费下载链接】mediamtxReady-to-use Media-over-QUIC / SRT / WebRTC / RTSP / RTMP / LL-HLS / MPEG-TS / RTP live media server and media proxy that allows to read, publish, proxy, record and playback real-time video and audio streams.项目地址: https://gitcode.com/GitHub_Trending/me/mediamtx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考