1. 先把这套组合的职责边界划清楚
1.1 信令走 WVP、媒体走 ZLMediaKit,为什么不做成一体
刚接触 wvp-GB28181-pro 的人最容易犯的错,是把 WVP 当成一个"流媒体服务器"。实际上它压根不碰视频流。整套 GB28181 视频平台在 CentOS 7 上跑起来,逻辑上是两台独立服务在配合:WVP 负责 SIP 信令(设备的注册、心跳、目录查询、INVITE 点播、云台控制、语音广播),ZLMediaKit 负责 RTP 收流、解 PS 封装、转协议分发(RTSP/RTMP/HLS/HTTP-FLV/WebRTC)。两者之间靠两样东西通信:WVP 调 ZLM 的 HTTP API(比如openRtpServer开收流端口、closeRtpServer关端口),ZLM 主动回调 WVP 的 hook 接口(on_publish、on_stream_changed、on_rtp_server_timeout等)。
这个拆分带来的最大好处是故障域隔离。信令出问题,只是设备点不开;媒体出问题,只是画面卡或黑屏。如果你把两者揉在一块儿,一次 Java 进程的 Full GC 就会把正在传输的流一起干掉。而且 ZLMediaKit 是 C++ 写的,本身单机撑几千路转发问题不大,Java 侧的 WVP 只处理信令,CPU 占用极低,两者对机器的资源诉求完全不同——这种不对称恰恰说明它们本就该分开部署。
我一般建议至少给出一个明确的分工认知:WVP 是"指挥官",它只下达命令、记录状态、维护设备树;ZLMediaKit 是"搬运工",它只负责把 UDP 上来的 RTP 包拆包、重新封装、按需分发给浏览器或第三方播放器。理解了这层,后面所有配置项的归属就不会搞混——凡是以sip:开头的参数是给 WVP 的,凡是以[rtp]、[rtsp]这种段落结构出现的是给 ZLM 的。
还需要提前明确一个概念:GB28181 里所有媒体流都是"设备推、平台收"。不是平台去设备那里拉。设备收到 INVITE 后,按 SDP 里协商好的 IP 和端口,主动把 PS 流用 RTP 推上来。这一点非常关键,后面讲级联和排查黑屏时会反复用到。
1.2 版本组合选型:先定 JDK,再定其余
这一步很多人跳过,结果编译到一半发现 JDK 版本对不上。WVP-GB28181-pro 从 2.7 开始切换到了 Spring Boot 3,要求 JDK 17;2.6.x 及更早是 Spring Boot 2.x,JDK 8 即可。CentOS 7 上默认的 OpenJDK 只有 1.8,如果你要跑 2.7,得自己装 JDK 17 并改JAVA_HOME。
我个人的建议是:存量设备多、追求稳妥就上 2.6.x + JDK 8,社区资料最全,踩坑帖子也最多;新项目、需要 WebRTC 低延迟播放、需要更规范的接口就上 2.7.x + JDK 17。下表是两种组合的实际差异,供你决策。
| 对比项 | WVP 2.6.x | WVP 2.7.x |
|---|---|---|
| 运行环境 | JDK 8 + Spring Boot 2.7 | JDK 17 + Spring Boot 3.x |
| 数据库 | MySQL 5.7 / 8.0 均可 | 建议 MySQL 8.0 |
| 配置结构 | media节点扁平 | media下拆出rtp、stream等子节点 |
| 前端 | Vue 2 打包后放static | Vue 3 独立打包 |
| 级联稳定性 | 成熟,资料多 | 修复了不少级联保活问题 |
ZLMediaKit 这边反而简单,它提供了编译好的二进制包,不用非得源码编译。CentOS 7 自带的 GCC 是 4.8.5,编译新版 ZLMediaKit 会报 C++14 相关的错,得装devtoolset-8之类的工具链,折腾成本不低。除非你要改源码,否则直接拿官方 Release 里的 linux 包解压就能用,省半天时间。
注意:CentOS 7 在 2024 年 6 月已经走完官方维护周期,默认的
mirror.centos.org地址大多失效。装依赖之前先把 yum 源指向归档地址,或者干脆换成本地镜像源,否则你会在第一步就卡住。
1.3 端口清单与带宽估算,动手前先算一遍
GB28181 平台最典型的故障原因不是代码问题,是端口没开。下面这张表建议你部署前对着firewall-cmd --list-ports逐条核对一遍。
| 端口 | 协议 | 归属 | 用途 |
|---|---|---|---|
| 5060 | UDP/TCP | WVP | SIP 信令,设备注册与点播 |
| 18080 | TCP | WVP | 后端 HTTP 服务,也是 ZLM 的 hook 回调地址 |
| 80 / 8090 | TCP | ZLM | HTTP 拉流、HLS、FLV |
| 554 | TCP | ZLM | RTSP 拉流 |
| 1935 | TCP | ZLM | RTMP 拉流/推流 |
| 8000 | UDP | ZLM | WebRTC 播放 |
| 30000-30500 | UDP | ZLM | RTP 收流端口段 |
带宽一定要提前算。单路 1080P H.264 主码流通常按 4 Mbps 估,如果 100 路设备同时点播,出口就是 400 Mbps。这个数字不是让你去申请这么多带宽,而是提醒你:前端页面别一屏开 16 路,开四路就够看,需要多路监控就上轮巡。媒体端口段的大小也有讲究——30000-30500是 501 个端口,决定了同时最多能有多少路流在收。如果你只开 10000 单端口,靠 SSRC 复用也能跑,但排查问题时流全挤在一个端口上,抓包会非常痛苦。我一般按"预计并发路数 + 50% 余量"来设端口段。
2. CentOS 7 基础环境铺设
2.1 系统初始化:四件必做的事
拿到一台干净的 CentOS 7,先做四件事,顺序别乱。
第一件,处理 yum 源。如果系统还能联网但yum install报 404,改归档地址:
sed -i 's/^mirrorlist=/#mirrorlist=/g' /etc/yum.repos.d/CentOS-Base.repo sed -i 's|^#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo yum clean all && yum makecache第二件,关掉 SELinux。ZLMediaKit 和 WVP 都涉及大量端口监听和文件读写,SELinux 开着会在你看不见的地方拦截。生产环境可以配策略,测试环境直接关:
setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config第三件,开防火墙端口。CentOS 7 用的是 firewalld,RTP 端口段要整段放行:
firewall-cmd --zone=public --add-port=5060/udp --permanent firewall-cmd --zone=public --add-port=5060/tcp --permanent firewall-cmd --zone=public --add-port=18080/tcp --permanent firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --zone=public --add-port=554/tcp --permanent firewall-cmd --zone=public --add-port=1935/tcp --permanent firewall-cmd --zone=public --add-port=8000/udp --permanent firewall-cmd --zone=public --add-port=30000-30500/udp --permanent firewall-cmd --reload第四件,调大文件句柄和时间同步。ZLMediaKit 每路流都会占若干 fd,默认 1024 在几十路并发时就爆了:
echo "* soft nofile 65535" >> /etc/security/limits.conf echo "* hard nofile 65535" >> /etc/security/limits.conf时间同步也要做,SIP 注册的Expires头和心跳全靠时间戳判断,服务器时间飘了会出现设备反复掉线。跑一条yum install -y chrony && systemctl enable --now chronyd就够。
提示:这四件事里最容易被忽略的是文件句柄。我遇到过一次"平台跑了两小时突然所有流中断",查了半天是 ZLM 打不开新 socket,
ulimit -n一查还是 1024,改完重启就再没犯过。
2.2 Java、MySQL 8、Redis 三件套
Java 按前面选定的版本装。如果你走 JDK 17 路线:
yum install -y java-17-openjdk java-17-openjdk-devel alternatives --config java echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk' >> /etc/profile source /etc/profile java -versionMySQL 8 建议用官方 yum 仓库装,别用 CentOS 自带的 MariaDB,WVP 的部分 SQL 语法(比如JSON字段、utf8mb4_0900_ai_ci排序规则)在 MariaDB 上会报错。安装后要改两个地方:character_set_server=utf8mb4和default_authentication_plugin=mysql_native_password。后者是因为 WVP 用的连接池驱动在某些版本下对caching_sha2_password支持不好,会报认证失败。
Redis 装完改/etc/redis.conf,把bind 127.0.0.1保持不动(WVP 和 Redis 同机部署就够),设一个requirepass。WVP 用 Redis 存的是流状态缓存和 token,不是业务数据,丢了不影响设备注册,但会导致正在播放的流状态不一致,所以别再拿 Redis 当主存储用。
这里有个顺序建议:先装 MySQL 和 Redis 并确认能本地连上,再动 WVP。因为 WVP 启动时会立刻去连数据库和 Redis,连不上就是一堆Connection refused刷屏,日志噪音会盖掉真正的报错。
2.3 依赖包一次性装齐
把编译和运行需要的包装齐,避免来回补:
yum install -y epel-release yum install -y gcc gcc-c++ cmake make git wget unzip \ openssl openssl-devel libsrtp libsrtp-devel \ ffmpeg net-tools tcpdump lsofffmpeg一定装上,它不是给平台用的,是给你自己验证 ZLM 收流是否正常用的,后面会用它推一路测试流来确认媒体通道通不通。tcpdump是排查 SIP 和 RTP 的救命工具,没有它你只能靠猜。
3. ZLMediaKit 部署与关键配置
3.1 二进制包 vs 源码编译
官方 Release 页面提供ZLMediaKit_linux_release_xx.tar.gz这类包,解压后目录里就是MediaServer可执行文件和一堆配置。这条路在 CentOS 7 上能省掉工具链的坑。
如果你确实要源码编译,CentOS 7 必须先升 CMake(自带的是 2.8,最低要求 3.1,新版要 3.13+)和 GCC(自带 4.8.5 不支持 C++14 部分特性):
yum install -y centos-release-scl yum install -y devtoolset-8 scl enable devtoolset-8 bash gcc --version然后按官方流程mkdir build && cd build && cmake .. && make -j4。编译过程在 2 核机器上大概要十几分钟,4 核会快不少。
注意:
scl enable只在当前 shell 生效。如果你用了screen或nohup在别的会话里编译,工具链是没切过去的,会报一堆#error This file requires compiler support。
3.2 config.ini 里必须改的那几项
ZLMediaKit 的配置集中在conf/config.ini。这个文件很长,但真正需要动的没几处。下面按段落说明。
[general] mediaServerId=zlm-01 flowThreshold=100 enableVhost=0 [hook] enable=1 on_server_started= on_publish= on_play= on_stream_changed=http://127.0.0.1:18080/index/hook/on_stream_changed on_stream_none_reader=http://127.0.0.1:18080/index/hook/on_stream_none_reader on_rtp_server_timeout=http://127.0.0.1:18080/index/hook/on_rtp_server_timeout timeoutSec=10 [rtp] port_range=30000-30500 [rtc] port=8000mediaServerId必须和 WVP 里配的媒体节点 ID 一致,否则 WVP 调 API 时 ZLM 会拒绝,日志里会出现hook 校验失败之类的字样。on_stream_none_reader这个回调很实用——当一路流没有任何观众时,WVP 会收到通知并主动关流,避免设备白白推流占用带宽。on_rtp_server_timeout则负责回收那些设备没推流的空端口,这个如果不配,跑一段时间端口段就会被耗尽,表现为"新点播全部失败"。
[rtp] port_range要和 WVP 里配的端口段完全对齐,两边不一致会出现 WVP 通知 ZLM 在 30100 开端口、ZLM 却说自己只有 30000-30050 的尴尬局面。
3.3 启动后先自检,别急着接设备
启动 ZLM:
cd ZLMediaKit ./MediaServer -d &然后看log/目录下最新日志,确认三件事:HTTP 端口监听成功、RTP 端口段分配成功、hook 回调没有报 404(此时 WVP 可能还没起,404 正常)。
接着用 ffmpeg 推一路本地测试流,验证 ZLM 的收流和转发链路完整:
ffmpeg -re -i test.mp4 -c copy -f rtsp rtsp://127.0.0.1:554/live/test换个终端用 ffplay 拉一下:
ffplay rtsp://127.0.0.1:554/live/test这一步能出画面,说明 ZLM 本身没问题,后面接 GB28181 出问题就一定是信令或 RTP 参数的事,排查范围直接缩小一半。这个"先隔离验证"的习惯能帮你省掉大量时间,很多人一上来就接摄像头,黑屏了根本分不清是 ZLM 坏了还是 SIP 没通。
4. WVP-GB28181-pro 部署与核心配置
4.1 建库、导 SQL、调连接池
先在 MySQL 里建库:
CREATE DATABASE wvp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'wvp'@'%' IDENTIFIED BY '你的密码'; GRANT ALL PRIVILEGES ON wvp.* TO 'wvp'@'%'; FLUSH PRIVILEGES;然后从 WVP 项目根目录找 SQL 文件导入。不同版本路径不一样,2.6.x 一般在数据库/目录下,2.7.x 在db/或doc/下,文件名通常带mysql或wvp字样。直接mysql -uwvp -p wvp < xxx.sql就行。
导入完检查表数量,正常应该有几十张表,wvp_device、wvp_device_channel、wvp_platform这几个是核心。如果只有两三张表,说明 SQL 文件选错了,你导的可能是升级脚本而不是全量脚本。
连接池这块,WVP 用 HikariCP。单机接几百台设备的话,maximum-pool-size给到 20 就绰绰有余——它只处理信令,不像业务系统那样高并发查库。盲目调到 100 反而会因为连接数过多拖慢 MySQL。
4.2 application.yml 逐段拆解
这是整个部署里最容易出错的地方。下面按 2.6.x 的结构讲,2.7.x 的键名有微调,以你本地文件为准。
server: port: 18080 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai username: wvp password: 你的密码 redis: host: 127.0.0.1 port: 6379 password: 你的redis密码 sip: ip: 192.168.1.100 port: 5060 domain: 3402000000 id: 34020000002000000001 password: 12345678 # 级联时作为下级平台对外暴露的地址 show-ip: 192.168.1.100 media: id: zlm-01 ip: 192.168.1.100 sdp-ip: 192.168.1.100 stream-ip: 192.168.1.100 hook-ip: 192.168.1.100 http-port: 80 secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc rtp-port: 30000几个必须拎出来讲的字段:
sip.ip是 WVP 监听 SIP 的地址,多网卡机器上一定要写内网实际 IP,不能写 0.0.0.0,否则 SDP 里带出去的地址是错的,设备会把流推到错误的地址上。sip.domain是 SIP 域,通常用 10 位数字,比如3402000000。sip.id是平台自身的 SIP 编号,20 位,规则是"域(10位) + 类型编码(4位) + 序号(6位)",其中平台类型编码固定是2000。所以3402000000+2000+00000001=34020000002000000001,这个编号全网不能重复。
media.secret必须和 ZLMconfig.ini里的[general] secret完全一致,这是 WVP 调 ZLM API 的凭证。我见过有人只改了一边,然后所有点播都返回"打开 RTP 服务器失败",日志里却只显示 HTTP 401,很容易误判成网络问题。
media.rtp-port是单端口模式下的收流端口。WVP 支持两种模式:单端口(所有流都发到 30000,靠 SSRC 区分)和多端口(每路流动态分配一个端口)。单端口模式下防火墙规则少,但抓包分析困难;多端口模式更好排查,代价是要放行一整段。
提示:
sdp-ip和stream-ip在 NAT 环境里是最要命的。如果 WVP 部署在云主机、设备在办公网,SDP 里带的必须是公网可达地址,否则设备收不到正确的收流地址,表现为"信令 200 OK 但一直没有画面"。
4.3 打包前端、启动服务、确认 hook 通
WVP 的前端通常是独立目录(比如web/或ui/),用 npm 构建:
cd web npm install npm run build构建产物拷到后端src/main/resources/static下,或者直接用 Nginx 托管。如果只是自己用,我建议用 Nginx 反代,前端改动不用重新打包后端,省事。
后端起服务:
mvn clean package -DskipTests java -jar target/wvp-pro-x.x.x.jar --spring.config.location=application.yml启动成功后,去 WVP 的"媒体节点"页面看 ZLM 是否显示在线。这一步是分水岭:如果显示离线,说明 WVP 调不通 ZLM 的 API,检查media.secret、media.ip、media.http-port三项;如果显示在线但流状态不刷新,说明 ZLM 的 hook 回调打不到 WVP,去 ZLM 日志里看回调 URL 有没有 404。
两个方向是独立的:WVP → ZLM 走 HTTP API,ZLM → WVP 走 hook 回调。一个方向通不代表另一个方向通,这个认知能帮你精准定位。
5. 设备接入与联调实测
5.1 摄像头侧参数怎么填
在摄像头的"平台接入"页面,通常要填这几项:
- SIP 服务器地址:
192.168.1.100 - SIP 服务器端口:
5060 - SIP 域:
3402000000 - SIP 用户/设备编号:20 位,比如
34020000001320000001 - 注册密码:和设备编号对应的密码
- 注册有效期:
3600秒
设备编号的构成规则要记住:前 10 位是域,中间 4 位是类型,后 6 位是序号。常见的类型编码里,132是摄像机,131是球机,118是报警主机。比如3402000000+132+000001=3402000000132000000 1这样 20 位。
不同厂商的界面差异很大。海康一般叫"平台接入 → GB28181",大华叫"国标28181",宇视在"网络 → 高级配置"里。有个通用技巧:填完保存后重启一次设备的网络服务,不少型号不重启不生效,你会以为配错了。
注册成功后,在 WVP 的"设备"页面能看到设备状态变绿,点开能展开通道列表。看不到通道通常是两个原因:设备的目录查询响应超时,或者通道被禁用了。前者等十几秒会自己出来,后者要去设备的"通道管理"里手动启用。
5.2 点播、录像、语音对讲
点播的完整链路是:WVP 收到前端请求 → 调 ZLMopenRtpServer开端口 → 给设备发 INVITE,SDP 里带上收流 IP 和端口 → 设备回 200 OK → 设备开始推 RTP → ZLM 收到第一个包后通过 hook 通知 WVP → WVP 把播放地址返回前端。这条链路上任何一环断了都会黑屏,所以排查时要按顺序看 WVP 日志 → ZLM 日志 → tcpdump。
录像功能依赖 ZLM 的record配置。默认是点播即录,也可以配成按计划录。录像文件默认落在 ZLM 目录下的www/record/里,格式是 MP4。要注意磁盘空间——100 路 24 小时能吃掉几 TB,我一般会在 ZLM 配置里加fileSecond=3600按小时切片,再配合定时任务清理超过 7 天的文件。
语音对讲是比较容易被忽略的能力,它需要满足三个条件:设备本身支持音频通道且麦克风未被禁用;SDP 协商里带了音频媒体描述(通常是a=rtpmap:8 PCMA/8000);ZLM 开启了对应的音频转发。实际调试时如果只有单向能听、反向没声音,八成是设备的音频编码和平台协商的不一致,把 SDP 里的m=audio那行的端口和 payload 号对一遍基本能定位。另外注意,很多球机的对讲通道和视频通道是分开编号的,点播用的是视频通道,对讲得切到音频通道上去发 INVITE。
5.3 级联:媒体流方向这个坑必须先说清
关于热词里提到的"wvp-gb28181-pro 不支持上级平台主动向下级联拉取资源",这里需要澄清一个概念:GB28181 的级联在设计上就不存在"上级去下级拉流"这回事。级联中的媒体流方向永远是下级主动向上级推送。上级发 INVITE 给下级,下级收到后按 SDP 里的地址把流推到上级的收流端口上,方向是下级 → 上级。
所以当你在 WVP 里配级联,遇到"上级平台点播下级资源失败",问题不在"能不能拉",而在下面几个点上:
第一,SIP ID 冲突。上级和下级如果用了同一个域或者同一个平台 ID,SIP 消息会互相打架。级联的两端必须是不同的域,比如上级3402000000、下级3402010000。
第二,网络可达性。上级要能访问下级的 SIP 端口(5060),下级也要能访问上级的 SIP 端口和 RTP 收流端口段。这两个方向是独立的,只开一边不够。
第三,Catalog 查询响应。上级发Catalog查询下级资源,下级必须在超时前返回完整通道列表。如果下级设备多、通道上千,一次性返回可能超过 UDP 的 MTU 被分片丢弃,这时要开 WVP 里的"目录分批返回"选项。
第四,媒体流推送目标地址。下级推流时用的地址来自上级 INVITE 的 SDP。如果上级在 NAT 后面,SDP 里的地址是内网 IP,下级推过去必然失败。这种情况需要在上级配置里显式指定stream-ip为公网地址。
| 级联症状 | 大概率原因 | 验证方法 |
|---|---|---|
| 上级看不到下级设备 | SIP ID 或域冲突 | 两端日志对比 SIP 消息中的 From/To 域 |
| 上级看到设备但点播失败 | RTP 端口未放行 | 在上级机器 tcpdump 看是否有包到达 |
| 点播成功但立即断开 | 下级推流地址不对 | 抓下级出口流量看目标 IP |
| 目录只有部分通道 | 单包过大被分片丢弃 | 开启分批返回 |
6. 踩坑速查与排查手法
6.1 常见问题速查表
下面这张表是我几次部署里攒下来的高频问题,按"症状 → 直接原因 → 处理"的形式整理,出问题时可以当索引查。
| 症状 | 直接原因 | 处理方式 |
|---|---|---|
| 设备一直显示未注册 | SIP 域或编号不匹配 | 核对设备侧域与 WVPsip.domain |
| 注册成功但无通道 | 目录查询超时 | 检查设备目录查询开关,等待重试 |
| 媒体节点显示离线 | secret 或端口不一致 | 两侧比对media.secret和http-port |
| 点播返回 200 但黑屏 | RTP 端口未放行或 IP 错误 | tcpdump 抓 30000 段 UDP 包 |
| 流播几秒后断开 | 无观众自动关流 | 检查on_stream_none_reader配置 |
| 新点播全部失败 | RTP 端口段耗尽 | 检查on_rtp_server_timeout是否生效 |
| 平台跑一段时间卡顿 | 文件句柄耗尽 | ulimit -n调到 65535 |
| 级联后设备重复出现 | 上下级域相同 | 改成不同的 SIP 域 |
6.2 抓包与日志的三层定位法
出问题时不要凭感觉改配置,按这三层往下走。
第一层看 WVP 日志。WVP 用的是 Logback,日志里 SIP 消息会有明确的收发标记。找INVITE、200 OK、ACK这几个关键字,看信令走到哪一步断了。如果连 INVITE 都没发出去,问题在 WVP 内部,可能是 Redis 连不上导致流状态拿不到。
第二层看 ZLM 日志。ZLM 的日志在log/目录下,会记录 RTP 收流的开始和结束,以及 hook 回调的返回码。看到openRtpServer成功但一直没有on_stream_changed,说明设备根本没推流过来,问题在设备侧或网络侧。
第三层抓包。这一步才是确定性的证据:
# 抓 SIP 信令 tcpdump -i eth0 -n port 5060 -w sip.pcap # 抓 RTP 收流 tcpdump -i eth0 -n udp portrange 30000-30500 -w rtp.pcap拿sip.pcap用 Wireshark 打开,看 INVITE 里的 SDP 内容,重点看c=那行的连接地址和m=video那行的端口。这两个值就是设备要推流的目标,一旦不对,后面全白搭。rtp.pcap里如果全是包,说明网络通、是平台侧处理有问题;如果一个包都没有,说明设备没推或防火墙拦了。
提示:抓 RTP 包时用
-c 100限制包数,不然 4 Mbps 的流抓几分钟就是几个 G,磁盘瞬间爆掉。
6.3 几个能提高稳定性的调优点
跑起来和跑得稳是两回事,下面几个点是我在实际运行中调出来的。
ZLM 的线程数。config.ini里有[general] threadNum,默认是 CPU 核数。如果主要是转发不加转码,保持默认就行;如果开了 HLS 切片和 MP4 录制,可以适当加 2-4 个,但别翻倍,线程太多调度开销反而拖慢转发。
WVP 的 JVM 参数。只做信令的话-Xms512m -Xmx1g足够跑几百台设备。设成 4G 是浪费,还会让 GC 停顿变长。加上-XX:+UseG1GC会更平滑。
RTP 端口段的回收。前面提过on_rtp_server_timeout,这个一定要配。我遇到过跑了两天后所有点播都失败的场景,查了半天是设备异常离线没发 BYE,WVP 没释放端口,501 个端口被慢慢占满。配好超时回收之后这个问题就没再出现。
录像磁盘的独立挂载。录像 IO 是持续的写入,如果和系统盘共用,MySQL 的写入延迟会被拖高,表现为偶尔的信令超时。有条件就单独挂一块盘给www/record/。
设备心跳间隔。默认 60 秒,设备多的时候会形成心跳风暴,几百台设备同一秒发注册刷新,SIP 线程会被瞬间打满。把过期时间设长一点,让设备的心跳自然错开,比加机器有效。
最后分享一个我自己用了很久的小习惯:部署完先在本地用 ffmpeg 推一路测试流跑通全链路,再接入真实设备。因为 ffmpeg 的行为完全可控,而摄像头的实现千奇百怪。当你知道平台侧一定没问题时,任何一个报错都能精准指向设备,排查效率至少翻一倍。这个顺序反过来做,你会同时面对两个未知,那是纯粹的时间黑洞。