配置详解
本文定位
本文讲清国标监控两个最容易配错的配置域——SIP 接入配置(§二)与媒体配置(ZLM / 媒体节点)(§三),§一 先给出全量 yaml 清单。媒体节点的多节点负载与选节点原理见 核心概念 §五,页面操作见 视频中心。
配置前缀
模块配置根前缀是 vms(非 blade.vms),总开关 vms.enabled=false 时整个模块不绑端口、不起定时器、不加载 ZLM。
一、配置全景
模块根前缀为 vms.*,完整 yaml 清单如下(含默认值;${...} 为部署期环境变量,留空字段按注释所述回退)。字段语义在 §二(SIP)、§三(媒体)分述,本节先给全貌:
vms:
enabled: true # 模块总开关:false 则不绑 SIP 端口 / 不起定时器 / 不加载 ZLM
gb28181:
sip:
nic: 0.0.0.0 # SIP 监听网卡,0.0.0.0=所有网卡(全局单端口,多租户按 server-id 区分)
host: ${VMS_SIP_HOST} # 对外信令 IP(写入出站 Via/Contact,须设备可路由;留空自动探测,多网卡环境建议显式配)
port: 5060
server-id: ${VMS_SERVER_ID} # 平台 SIP 服务器 ID(20 位)
domain: ${VMS_SIP_DOMAIN} # SIP 域(10 位)
password: ${VMS_SIP_PASSWORD} # 平台默认 Digest 密码(设备可配独立密码覆盖)
register-expire: 3600
keepalive-interval: 60
transport: BOTH # UDP / TCP / BOTH
charset: GB2312 # 国标默认,部分上级要 UTF-8
default-tenant-id: "000000"
thread-pool-size: 50
mobile-position-event: presence # GPS 订阅 SUBSCRIBE 的 Event 头,默认 presence(附录 J.18);个别固件需改为 MobilePosition
# 多信令节点分片(单实例可全部留空,零配置即工作;详见 §2.1)
instance-id: ${VMS_INSTANCE_ID} # 本信令节点编码,多实例须各配唯一值;留空回退「信令地址:端口」
internal-base-url: ${VMS_INTERNAL_BASE_URL} # 本节点对其它实例暴露的内部 HTTP 转发入口基址;留空按「信令地址 + 服务端口」推导
node-forward-secret: ${VMS_NODE_FORWARD_SECRET} # 实例间内部转发的鉴权口令,多实例部署须配置
cascade-node: false # 本实例是否承担级联(向上级注册/保活);自报进 iot_vms_signal_node.cascade_node 列,承担实例在「在线且标记」节点中按编码最小者自动推选,留空(false)单实例经兜底仍由本实例承担
signal-node-heartbeat-seconds: 10 # 信令节点自报心跳间隔(秒)
signal-node-keepalive-timeout-multiplier: 3 # 信令节点离线判定倍数,在线窗口 = 间隔 × 倍数
# ⚠️ 以下 zlm 配置仅作「默认节点种子」:启动时植入默认租户 000000 的媒体节点表。
# 运行期 ZLM 节点以 iot_vms_media_node 表为准,各租户在【媒体节点】页面自助增删改;
# 多节点按并发自动负载、按 node-id 绑定会话,无需在此配置多个节点(详见 §三)。
zlm:
node-id: node-1 # 须与 ZLM general.mediaServerId 一致
host: ${ZLM_HOST} # 平台访问 ZLM REST API 的 IP
public-host: ${ZLM_PUBLIC_HOST} # ⚠️ 浏览器拉流地址,启动期强制非空(dev profile 例外)
fail-on-missing-public-host: true
http-port: 80
internal-http-port: 0 # docker NAT 部署填容器内端口,0 回退 http-port
sdp-ip: ${ZLM_SDP_IP} # 对设备暴露的 RTP 媒体 IP,空回退 host
rtmp-port: 1935
secret: ${ZLM_SECRET} # ZLM HTTP API secret
hook-secret: ${ZLM_HOOK_SECRET} # webhook 校验密钥,启动期 fail-fast 强制非空
allowed-hook-ips: 10.0.0.5,10.0.0.6 # 可选,CSV webhook 源 IP 白名单
waiting-ttl-seconds: 90 # WAITING 短超时(INVITE 已发但未收到 RTP)
session-ttl-seconds: 7200 # 流活跃 TTL,GC 与 CLOSE_PENDING 转 EXPIRED 都依据
session-retain-days: 3 # 终态流会话历史保留天数,超期由清理任务删除
tcp-mode: true # NAT 场景建议 true(可在节点表按节点覆盖)
rtp-port: 10000 # RTP 收流端口:设为固定端口即单端口收流(所有取流共用、按 SSRC 分流,须与 ZLM config.ini [rtp_proxy] port 一致,Docker 仅放行此端口);留 0 则每路流动态分配独立端口
http-ssl-port: 0 # 浏览器侧 HTTPS/WSS 拉流端口(对应 ZLM http.sslport),管理端经 HTTPS 提供时必配,0 走 http/ws
keepalive-interval-seconds: 10 # 节点心跳上报间隔(秒),与 ZLM [hook] alive_interval 对齐
keepalive-timeout-multiplier: 6 # 心跳超时容忍次数,节点在线窗口 = 间隔 × 次数(默认 60s)
snapshot:
oss-path-prefix: vms/snapshot
expire-days: 7
auto-stream-timeout-ms: 5000
settle-delay-ms: 1000
record: # 录像 / 下载配置(录像去本地化机制见 运行流程·流媒体部署要点)
oss-path-prefix: vms/record
download-retain-days: 7 # 下载任务产物保留天数(归档保留按各录像计划的 retain-days)
download-concurrency: 4
download-speed: 4 # 录像下载倍速(SDP a=downloadspeed),1=实时速率
plan-executor-pool: 2
max-range-seconds: 21600 # 单次录像下载段(end-start)时长上限,超出拒绝下载
stream-ready-timeout-millis: 10000 # 起录前等流就绪超时
record-fetch-timeout-millis: 30000 # 从 ZLM 拉取 MP4 直传 OSS 的超时
manual-max-seconds: 7200 # 手动录像单次时长上限(2h)
stream:
retry:
batch-size: 200 # CLOSE_PENDING 单次扫描批量
max-rounds: 50 # 单次调度最多 50 轮
max-fail-per-session: 5 # 单会话失败 5 次转 EXPIRED
query:
cache-ttl-ms: 60000 # 异步查询结果缓存 TTL
oss:
enabled: true
name: minio # minio / aliyun / qcloud / huawei / qiniu
tenant-mode: false
endpoint: ${OSS_ENDPOINT}
access-key: ${OSS_ACCESS_KEY}
secret-key: ${OSS_SECRET_KEY}
bucket-name: vms-prodsip 与 zlm 是部署落地的两个核心,职责正交:
| 维度 | SIP 配置 | ZLM / 媒体配置 |
|---|---|---|
| 面向 | 设备 / 上级平台的信令通道 | 流媒体服务的媒体通道 |
| 形态 | 全局单实例(单端口) | 租户级多节点(详见 §三) |
| 协议 | GB/T 28181 SIP | HTTP REST + Webhook |
| 配错典型症状 | 设备注册不上 / 401 循环 | 信令正常但点播黑屏 / 浏览器拉不到流 |
二、SIP 接入配置
2.1 一个端口,多个租户
平台 SIP 是全局单实例:进程只在 vms.gb28181.sip.port(默认 5060)上绑定一个 UDP+TCP 端口,所有租户的设备都连这同一个端口。租户不是靠端口区分的,而是靠 server-id:
- 设备 REGISTER 报文里携带平台 server-id;平台按 server-id 反查
iot_vms_platform行,拿到该行的tenant_id注入上下文。 - 所以不同租户用不同的 server-id(20 位国标编号),共用同一个 5060 端口与同一份密码语义。
vms.gb28181.sip.server-id是平台自身的默认 server-id;多租户场景下各租户在【国标平台】页面各配一条平台记录(各自的 server-id / domain)。 default-tenant-id(默认000000):当报文无法定位到具体租户平台时的兜底租户。
不需要每租户开一个端口
有的国标实现给每个租户开独立 SIP 端口(5060/5061/…)。本平台不采用此方案——单端口配合 server-id 路由,既节省端口又天然支持多租户,运维只需放通一个端口。
单端口 ≠ 单实例:多信令节点分片
单端口指「一个进程只绑一个 SIP 端口」,不是「全平台只能跑一个进程」。需要水平扩展信令接入容量时,可部署多个 blade-server 信令实例做分片——设备按其「上级 SIP 地址」注册到某个实例、该实例即属主,控制请求经属主路由自动落到正确实例。相关配置项都在 vms.gb28181.sip.* 下:
| 配置项 | 说明 |
|---|---|
instance-id | 本节点编码,多实例须各配唯一值;留空回退「信令地址:端口」 |
internal-base-url | 本节点对其它实例暴露的内部 HTTP 转发入口基址;留空按「信令地址 + 服务端口」推导 |
node-forward-secret | 实例间内部转发的鉴权口令,多实例部署须配置 |
cascade-node | 布尔,本实例是否承担级联(向上级注册/保活);各实例只声明自己,承担实例在「在线且标记」节点中按编码最小者自动推选,留空(false)单实例经兜底仍由本实例承担 |
signal-node-heartbeat-seconds / signal-node-keepalive-timeout-multiplier | 信令节点自报心跳间隔与离线判定倍数,在线窗口 = 间隔 × 倍数(默认 10s × 3 = 30s) |
单实例上述全部可留空、零配置即工作。 四项配置的精确语义、cascade-node 标记须打在接收级联媒体 ZLM 回调实例上的要求、双实例示例与故障转移取舍详见 分片部署;信令节点的查看与管理见 信令节点。
2.2 nic 与 host:监听网卡 ≠ 对外地址
这是 SIP 接入最隐蔽的坑,docker / NAT 部署必须区分:
| 字段 | 含义 | 取值建议 |
|---|---|---|
nic | SIP 协议栈监听绑定的本机网卡地址 | 一般 0.0.0.0(绑所有网卡) |
host | 写入出站 Via / Contact 头的对外信令 IP,设备据此把响应/媒体回送过来 | 设备可路由到的平台地址(容器内网 IP / 宿主机 IP / 公网 IP);留空自动探测,多网卡 / NAT / 公网建议显式配 |
host 配错 = 设备注册看似成功却收不到后续信令
若 host 填了容器内回环或错误地址,设备的 200 OK / RTP 会发到错误目的地,表现为"REGISTER 通过但点播无响应"。docker 部署务必设为设备能访问到的平台地址;多网卡环境留空自动探测可能选中错误网卡,应显式指定。
2.3 鉴权:平台默认密码 + 设备独立密码 + 注册模式
密码两级:
vms.gb28181.sip.password—— 平台级默认 Digest 密码。- 设备记录上的
password字段 —— 设备独立密码,配置后优先于平台默认密码用于该设备鉴权。适用于"一机一密"安全要求。
注册模式(平台级 register-mode):
| 模式 | 行为 | 适用 |
|---|---|---|
| AUTO(自动接入) | 任意设备只要携带正确密码鉴权通过,即自动落库接入 | 设备可信、批量接入 |
| MANUAL(手动注册) | 仅放行已在白名单登记的设备;未登记设备 REGISTER 一律拒绝(403) | 强管控、防陌生设备接入 |
准入与密码解析统一收敛在协议层的 resolveRegisterAuth 闸门,业务表不感知 SIP 细节。手动注册的页面操作见 国标设备。
2.4 其余 SIP 参数
| 字段 | 默认 | 说明 |
|---|---|---|
transport | BOTH | UDP / TCP / BOTH 双栈 |
charset | GB2312 | 国标默认;部分上级要 UTF-8 |
register-expire | 3600 | 注册有效期(秒) |
keepalive-interval | 60 | 设备心跳周期(秒) |
keepalive-timeout-multiplier | 3 | 心跳超时倍数;离线判定窗口 = keepalive-interval × 倍数,连续该倍数周期无心跳即判离线 |
keepalive-window-max-seconds | 600 | 离线判定窗口上限(秒),防设备自报超大 HeartBeatInterval 撑爆窗口;0 = 不封顶 |
keepalive-redis-scan-seconds | 10 | 离线检测 Redis 主通道扫描间隔(秒) |
keepalive-db-scan-seconds | 60 | 离线检测 SQL 兜底扫描间隔(秒) |
mobile-position-event | presence | GPS 订阅 Event 头;默认 presence(GB28181-2016 附录 J.18),个别固件需改为 MobilePosition |
sdp-owner-strategy | DEVICE_ID | 下发 SDP o= 行 owner 取值:DEVICE_ID=被点播设备国标编号(互通性最佳,默认),SERVER_ID=平台 server-id(RFC 4566 严格语义);少数上级要求严格语义时切 SERVER_ID |
thread-pool-size | 50 | SIP 处理线程池 |
default-tenant-id | 000000 | 无法定位租户时的兜底 |
三、媒体配置(ZLM / 媒体节点)
ZLM 配置本质就是媒体节点配置。运行期媒体节点以 iot_vms_media_node 表为准——按租户多行存储,各租户在【媒体节点】页面自助增删改;vms.gb28181.zlm 仅作「默认节点种子」(见 §3.6)。媒体节点的多节点负载、选节点算法等设计原理见 核心概念 §五,本节聚焦配置项填什么。
3.1 平台 ↔ ZLM 的两条通道
平台与 ZLM 是两个独立进程,通过两条方向相反的通道协作:
- ① REST(平台 → ZLM):开/关收流端口、抓图、录制。平台用
host:http-port,带secret鉴权。 - ② Webhook(ZLM → 平台):流就绪/断流/推流鉴权/节点心跳。ZLM 用
?secret=hook-secret校验。
下面三组配置项——地址、安全、收流——正对应这两条通道的两端,每个媒体节点都按这三组填写。
3.2 地址:谁访问谁,决定填什么
媒体配置最易混淆的是几个地址各服务于不同的访问方向,务必逐个对号:
| 字段 | 谁来访问这个地址 | 含义 | 留空回退 |
|---|---|---|---|
host / http-port | 平台 调 ZLM REST API | ZLM 的 HTTP API 地址与端口 | host 默认 127.0.0.1;生产须显式配为平台可访问 ZLM 的地址 |
sdp-ip | 设备 推 RTP 流 | 下发给设备 SDP 里的媒体接收 IP | → host |
public-host | 浏览器 拉流 | 回填到 flv/hls/ws/rtmp/webrtc URL 的地址(WebRTC 复用管理面 http(s) 端口协商) | → host |
internal-http-port | ZLM 自己(回环抓图) | ZLM 经 127.0.0.1 自访问的 HTTP 端口 | → http-port |
三个最常见的"信令正常但黑屏"
sdp-ip是回环/错误地址 → 设备无法把 RTP 推到平台,点播/回放无画面(生产启动期强制校验非回环)。public-host错 → 浏览器侧 ws-flv 地址不可达,前端转圈拉不到流(生产启动期强制非空)。- docker NAT 下用了对外映射端口当
internal-http-port→ ZLM 抓图回环拉不到自身流,getSnap 失败。容器内须填容器内[http] port。
3.3 安全:三个密钥
| 字段 | 作用 | 强制性 |
|---|---|---|
secret | 平台调 ZLM REST API 的密钥,须等于 ZLM config.ini [api] secret | 必配 |
hook-secret | ZLM 回调平台的校验密钥,配在各 hook URL 的 ?secret= | 启动期 fail-fast 强制非空 |
allowed-hook-ips | webhook 源 IP 白名单(CSV),防伪造回调 | 启动期 fail-fast 强制非空(非 dev) |
3.4 收流、流控与心跳
| 字段 | 默认 | 说明 |
|---|---|---|
tcp-mode | true | TCP 被动收流;NAT 场景建议 true(可按节点覆盖) |
rtp-port | 0 | RTP 收流端口模式开关(见 §3.5);>0 单端口被动收流,0 多端口动态分配 |
rtmp-port | 1935 | 回填 rtmp 拉流地址用 |
http-ssl-port | 0 | 浏览器侧 HTTPS/WSS 拉流端口(对应 ZLM http.sslport);>0 时 flv/hls/ws 拉流地址按 https/wss 生成,0 走 http/ws。管理端经 HTTPS 提供时必配,否则浏览器按混合内容策略拦截 ws/http 拉流 |
waiting-ttl-seconds | 90 | WAITING 短超时:INVITE 已发但未收到 RTP 即回收 |
session-ttl-seconds | 7200 | 流活跃 TTL;GC 与 CLOSE_PENDING→EXPIRED 都依据 |
session-retain-days | 3 | 终态会话历史保留天数 |
keepalive-interval-seconds | 10 | 节点心跳上报间隔(秒),与 ZLM [hook] alive_interval 对齐 |
keepalive-timeout-multiplier | 6 | 心跳超时容忍倍数;节点在线窗口 = 间隔 × 倍数(默认 60s) |
逐节点 vs 全局:节点的地址、密钥与收流模式(
host/sdp-ip/public-host/secret/tcp-mode/rtp-port/rtmp-port等)随每个媒体节点在【媒体节点】页逐个配置(存iot_vms_media_node,可按节点不同);而waiting-ttl-seconds/session-ttl-seconds/http-ssl-port/hook-secret/keepalive-*等是平台级全局参数(vms.gb28181.zlm.*)。
3.5 单端口 vs 多端口收流(rtp-port)
rtp-port 决定设备 RTP 流落到 ZLM 的收流端口模式,直接影响 docker 端口放行策略:
| 模式 | 条件 | 行为 | 适配 |
|---|---|---|---|
| 单端口被动收流 | rtp-port>0 | 所有取流共用这一个固定端口,ZLM 按 RTP 头 SSRC 解复用建流;平台不再调 openRtpServer,直接复用 ZLM 常驻收流口,closeRtpServer 为 no-op(设备 BYE 后由 ZLM RTP 空闲超时回收) | docker 仅需放行该端口;须与 ZLM config.ini [rtp_proxy] port 一致 |
| 多端口动态收流 | rtp-port=0 | 每路流由 openRtpServer 动态分配独立端口(port=0、放开 SSRC 过滤) | docker 需放行整个端口段 |
ZLM 单端口建流的流名归一
单端口模式下 ZLM 被动建流的媒体流名取自 RTP 头中 SSRC 的 8 位大写十六进制(例 0200000035 → 0BEBC223),与平台显式登记的 10 位十进制 SSRC 不同形。SsrcAllocator.canonicalSsrc 把它还原成标准 10 位十进制以反查会话;on_stream_changed / on_stream_none_reader 回调据此归一,否则媒体已在流而会话仍卡 WAITING。
3.6 多节点负载与默认种子
多节点负载:同一租户配置多个媒体节点即自动组成负载集群(无集群开关)。每路新流由 pickNode 选当前并发最低的在线节点承载,会话按 node_id 绑定,后续开流、回调回填拉流地址、关流、录制、抓图都回查同一节点,杜绝"在 A 节点开、去 B 节点关"的错配。选节点的双重过滤(enabled + 心跳在线)、为何不回退到离线节点等设计取舍,详见 核心概念 §五。
默认节点种子:启动时 VmsMediaNodeSeeder 若发现 vms.gb28181.zlm.node-id 尚不存在,则把该 yaml 单节点植入默认租户 000000 的节点表(以 node-id 全局存在性幂等),既有单节点部署升级后无需手工录入即继续取流。其余租户在【媒体节点】页面自助配置,页面操作见 媒体节点。
3.7 ZLM 侧必配
| 项 | 要求 |
|---|---|
general.mediaServerId | 必须等于该节点在媒体节点表中的「节点编码」node-id |
| hook 列表 | 配置 4 个回调 on_publish / on_stream_changed / on_stream_none_reader / on_server_keepalive,URL 形如 http://平台地址/blade-iot/vms/webhook/<hook名>?secret=<hook-secret> |
[api] secret | 等于节点 secret |
四、启动期配置校验
平台启动期对关键配置 fail-fast,避免"配错却跑起来、运行时才黑屏":
| 校验 | 触发条件 | 目的 |
|---|---|---|
hook-secret 非空 | 任意环境 | 防公网部署被任意 IP 伪造 webhook |
sdp-ip(或 host)非回环 | 非 dev profile | 防设备 SDP 媒体 IP 为回环导致无画面 |
public-host 非空 | 非 dev + fail-on-missing-public-host=true | 防浏览器拉流地址回退 127.0.0.1 |
allowed-hook-ips 非空 | 非 dev profile | 生产强制 webhook 源 IP 白名单 |
开发环境可启用 spring.profiles.active=dev 跳过上述生产校验。
