wvp-GB28181-pro与ZLMediaKit国标视频平台部署联调实战
2026/9/18 3:08:04 网站建设 项目流程

1. 先搞清楚这套组合各自干什么:wvp-GB28181-pro 与 ZLMediaKit 的分工

GB28181 这套东西,刚接触的人最容易卡在一个地方:不知道信令和媒体流是两条完全独立的链路。我最早做项目的时候,把 SIP 信令调通了,看到设备在平台上显示在线,就以为大功告成,结果点播的时候黑屏,查了一整天才反应过来——信令通了只代表"我知道你在",媒体流能不能过来是另外一回事。所以这篇东西我打算从职责边界讲起,先把 wvp-GB28181-pro 和 ZLMediaKit 这两块拼图的位置摆正,后面的部署步骤才不会变成照抄命令却不理解在干什么。

wvp-GB28181-pro 在整个架构里扮演的是信令控制与业务管理的角色。它是一个基于 Spring Boot 的 Java 项目,核心职责包括:作为 SIP 服务器接收国标设备或下级平台的注册、处理心跳保活、响应目录查询、下发 INVITE 邀请设备推流、处理录像回放的信令交互、管理设备与通道的业务数据、对外提供 Web 页面和 REST 接口。你可以把它理解成一个"调度中心",它自己不碰视频数据,只管发号施令和记账。

ZLMediaKit 则是纯粹的流媒体处理引擎。它接收来自设备的 RTP 流、完成解复用和转封装,对外输出 RTSP、RTMP、HTTP-FLV、HLS、WebRTC 等多种播放协议,同时负责录像存储、按需拉流、流媒体转发。国标设备推流过来的时候,实际落到 ZLMediaKit 的 RTP 收流端口上;播放器播放的时候,实际是从 ZLMediaKit 的 HTTP 或 RTSP 端口取流。wvp 只是在中间告诉 ZLMediaKit"有这么一路流要开个端口收",以及告诉播放器"去这个地址取流"。

两者之间靠Hook 回调REST API打通。wvp 在配置文件里声明 ZLMediaKit 的地址和密钥,ZLMediaKit 在config.ini里把 hook 地址指向 wvp。当有流注册上来、流无人观看、录像完成、需要鉴权的时候,ZLMediaKit 主动回调 wvp;当 wvp 需要创建 RTP 接收端口、关闭某路流、查询在线流的时候,调用 ZLMediaKit 的 HTTP API。这条双向通道是整套系统能不能跑起来的命脉,后面配置里任何一个环节写错,表现都是"设备在线但点播失败",非常隐蔽。

1.1 为什么不干脆用一个组件搞定

很多人会问,网上有些单体方案,一个进程既做信令又做流媒体,为什么还要拆成两个。我的实际体会是,拆分带来的好处在规模上来之后才明显。流媒体处理是 CPU 和带宽密集型的工作,一个 ZLMediaKit 实例扛不住了可以直接再起一个,wvp 通过多媒体节点配置把负载分散出去;而信令和业务逻辑相对轻量,稳定运行即可。反过来如果耦合在一起,扩流媒体就得连业务一起复制,数据库连接、定时任务全都会重复执行,运维会很难受。

另外 ZLMediaKit 是 C++ 写的,性能和并发连接数上有天然优势,单独维护它的编译和调优,比把它塞进 Java 进程里要省心。所以这套组合的分工不是设计冗余,而是各自做自己最擅长的事。理解这一点之后,你在排查问题时就有一把尺子:播放相关的画面问题优先查 ZLMediaKit 侧,注册、目录、云台控制、录像检索这类问题优先查 wvp 侧

1.2 适用的场景与规模边界

这套方案最典型的使用场景是中小规模的视频监控汇聚平台:几十到几百路设备接入,需要国标级联、需要 Web 端和移动端播放、需要基础的录像和回放。它不像大型商业平台那样有完善的集群和容灾,但胜在开源、可控、能改。我见过不少项目拿它做园区的监控整合、做行业设备的统一接入、做产品原型验证,都是合适的。

不适合的场景也要说清楚:如果你需要上千路并发、需要跨机房容灾、需要商业级的 SLA 保障,那这套东西的运维成本会快速上升,你要自己补的东西很多,比如媒体节点的健康检查、流的自动恢复、数据库的高可用。评估的时候心里要有数,别指望开箱即用能顶住生产级别的压力。

2. CentOS 7 基础环境准备:绕不开的换源与依赖坑

CentOS 7 现在部署这套系统,第一个拦路虎不是技术问题,而是系统本身已经停止维护了。官方 YUM 源在 2024 年年中之后逐步下线,你如果拿一个原版镜像装完直接yum install,大概率会卡在报错上下不来。所以这一步我建议直接把换源做掉,用 vault 归档源或者国内镜像站,别在源的问题上浪费时间。我踩过一次坑:装了一半发现某个依赖死活拉不下来,查了半天才发现是源失效,不是依赖冲突,白白折腾了一个下午。

2.1 系统初始化的几个必做项

安装系统的时候我一般选最小化安装(Minimal Install),把不必要的服务砍掉,减少后续排查干扰。装完之后先做几件事:更新系统补丁(在源可用前提下)、设置静态 IP、同步时间、关闭或者正确配置防火墙和 SELinux。

时间同步这个事看着小,但对 SIP 信令影响不小。国标设备注册的时候会带时间戳,有些设备对时间偏差敏感,服务器时间和设备时间差太多会导致注册失败或者鉴权异常。用chronyd配置 NTP 同步即可,内网没有 NTP 服务器的话至少保证服务器本身时间准确。

SELinux 我一般设成permissive而不是直接关掉。直接disabled在有些环境里是合规问题,而permissive只会记录警告不会真正拦截,排查阶段够用了。等系统稳定运行一段时间,确认没有 AVC 拒绝日志,再考虑收紧策略。防火墙方面,如果用 firewalld,那就老老实实按端口开放;如果是纯内网测试环境,图省事直接停掉也行,但生产上别这么干。

2.2 CentOS 7 的编译工具链问题

这是一个必须重点说的坑。CentOS 7 自带的 GCC 是 4.8.5,CMake 是 2.8.12,而 ZLMediaKit 的编译需要较新的 CMake(至少 3.1,实践中我建议 3.13 以上)和能完整支持 C++11/14 的编译器。直接用系统默认工具链去编译,十有八九在 CMake 检测阶段就报版本不够,或者编译过程中报一堆语法错误。

解决办法是用 SCL(Software Collections)装devtoolset。装完之后通过scl enable devtoolset-9 bash切换环境,或者干脆在脚本里把新工具链加到 PATH 前面。这一步做完,编译基本就顺了。另外 CMake 我倾向于单独下载官方预编译包解压使用,比从源码编译省事得多。

提醒一句,devtoolset只是临时切换环境变量,新开一个终端就失效了。如果你按照某个教程编译成功,换了窗口再执行同样的命令却失败,先想想是不是忘了切环境。

2.3 端口规划要提前做

端口冲突是部署阶段最常见的低级事故。这套系统涉及的端口比较多,我建议先在纸上列一张表,把每个端口分配给谁、用什么协议、是否对外暴露都想清楚,再动手。下面是我常用的端口规划,你可以按自己环境调整。

端口协议归属组件用途
5060UDP/TCPwvpSIP 信令监听
8080 或自定义TCPwvpWeb 页面与 REST 接口
80 / 443TCPZLMediaKitHTTP 播放、HLS 分发
554TCPZLMediaKitRTSP 播放
1935TCPZLMediaKitRTMP 播放与推流
30000-30500UDPwvp/ZLMediaKitRTP 收流端口段
6379TCPRedis缓存与会话(仅本机)
3306TCPMySQL数据库(仅本机)

RTP 端口段需要特别注意两点:一是要留够数量,每路实时流会占用一个端口,路数多了端口不够用;二是端口段要和 wvp 配置、ZLMediaKit 配置、防火墙三处保持一致,任何一处对不上都会导致收流失败。这个端口段通常是设备推流的目标端口,由 wvp 在 INVITE 的 SDP 里告诉设备,所以它必须和 ZLMediaKit 实际监听的端口范围匹配,错一位都不行。

2.4 数据库和缓存的准备

MySQL 我一般用 5.7 或 8.0,用 8.0 的话注意 JDBC 驱动和连接串要匹配,时区参数也要加上,否则可能出现时间存进去差 8 小时的问题。字符集统一用utf8mb4,因为设备名称、通道名称里可能有生僻字或者特殊符号。数据库的max_connections适当调高一点,wvp 的连接池加上后续可能的多实例,默认的 151 有时候会不够。

Redis 主要用来存会话、流状态、设备订阅关系这些东西。单机部署的话默认配置基本够用,但要设个密码并只监听本机,别裸奔在公网上。我见过有人的 Redis 直接暴露在外网,没设密码,被写进去一堆垃圾键,虽然不影响业务,但这是个安全隐患,顺手配上密码成本几乎为零。

3. ZLMediaKit 部署:从编译到收流参数调优

ZLMediaKit 是整套系统里最"挑环境"的部分,也是决定画质和稳定性的核心。我的建议是能编译就编译,不要随便下别人打包的二进制。原因很简单:编译过程会检测你的系统依赖,编出来的版本和你的库版本是一致的;而随手拿来的二进制可能链接了不同版本的 OpenSSL 或 ffmpeg 库,运行时报一堆symbol lookup error,排查起来很折磨。

3.1 编译流程与关键依赖

标准流程是克隆源码、初始化子模块、CMake 配置、编译安装。这里有几个细节值得强调。第一,一定要用git clone --recursive或者克隆后执行子模块初始化,因为 ZLMediaKit 依赖 ZLToolKit 等子模块,漏了会编译失败。第二,如果机器内存比较小(比如 2G),并行编译容易 OOM,把make -j的并发数降下来,-j2甚至单线程,慢一点但稳。第三,编译前确认 OpenSSL 开发包已安装,否则 HTTPS、WebRTC 相关功能会缺失。

编译完成之后,产物是MediaServer可执行文件,默认配置文件是config.ini。建议不要直接在源码目录跑,把可执行文件和配置拷到一个独立的运行目录,配好 systemd 服务,这样升级和备份都清爽。

# 编译 ZLMediaKit 的大致流程(在已切换 devtoolset 环境的终端中执行) git clone --depth 1 https://github.com/ZLMediaKit/ZLMediaKit.git cd ZLMediaKit git submodule update --init mkdir -p build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_WEBRTC=ON make -j4 sudo make install

3.2 config.ini 里和国标最相关的几项

config.ini项目很多,但真正决定国标能不能跑通的就那么几项。我把它拆开讲,每一项都说说为什么这么设。

[general]段的enable保持默认,mediaServerId建议自定义一个有意义的名字,比如media-01。这个 ID 会出现在 wvp 的媒体节点配置里,两边必须一致。用默认的随机值也能跑,但你以后加第二个媒体节点的时候会分不清谁是谁。

[http]段设置 HTTP 监听端口和rootPath。这个端口是播放器取 FLV/HLS 的地方,也是 wvp 回调 ZLMediaKit 的部分接口地址所在,要保证 wvp 能访问到。

[rtsp][rtmp]段按需开启,端口保持默认 554 和 1935 即可。如果你不需要某一种协议,关掉能省点资源。

[rtp_proxy]段是重点。port设置 RTP 收流的默认端口,但真正收流时用的端口是由 wvp 通过 API 动态创建的,落在 wvp 配置的端口段里。这里要理解一个机制:wvp 在收到播放请求后,会调用 ZLMediaKit 的/index/api/openRtpServer接口,指定一个端口让 ZLMediaKit 开始监听,然后把这个端口写进 SDP 发给设备。设备往这个端口推流,ZLMediaKit 收到后回调 wvp 通知"流来了"。

[hook]段是双向通道的关键。enable=1打开 hook,on_flow_reporton_publishon_playon_stream_changedon_stream_none_readeron_server_started这些回调地址统统指向 wvp 的/index/hook接口。admin_secret要设一个强密码,wvp 侧配置的 media secret 必须和它一模一样,否则 wvp 调用 ZLMediaKit API 时会一直返回鉴权失败。

[ffmpeg]段如果你需要 HLS 切片或者录制转 MP4,bin路径要指向正确的 ffmpeg 可执行文件。CentOS 7 自带的 ffmpeg 版本老,我一般自己装一个新版本,写绝对路径。

3.3 收流缓冲与性能相关的内核参数

国标设备推流是 UDP 的,UDP 的特点是丢了就丢了,不重传。如果服务器的 socket 接收缓冲区太小,或者内核网络参数设置保守,在高码率或者网络抖动的情况下就会丢包,表现是画面卡顿、花屏、马赛克。这不是 ZLMediaKit 的 bug,是系统层面的问题。

我一般会调整几个参数:把net.core.rmem_maxnet.core.rmem_default调大,允许 socket 使用更大的接收缓冲;把net.core.netdev_max_backlog提高,应对瞬间的包洪峰;对于大量并发的场景,net.ipv4.udp_mem也可以适当放宽。这些改动写到/etc/sysctl.conf里,sysctl -p生效。

注意:这些参数不是越大越好,盲目调大只是把内存压力往后推。调完之后要用实际业务压一压,观察丢包和延迟,根据数据微调,别照抄一个数字就完事。

另外就是 CPU 亲和性和网卡多队列。如果服务器网卡支持多队列,而流量又集中在少数几个队列上,可能出现单核跑满而其他核空闲的情况。可以通过ethtool查看队列分布,必要时调整 RSS 或给 MediaServer 进程绑核。这一步属于优化范畴,流量不大的时候可以先不管,等真的遇到瓶颈再处理。

4. wvp-GB28181-pro 部署:信令配置与国标对接

wvp 这块的部署,说到底就是编译打包、配数据库、改配置文件、跑起来。真正花时间的不是敲命令,而是理解配置文件里那几个 IP 到底该填什么。我见过太多人卡在这里:设备注册上了,但点播地址是内网 IP,播放器在外网取不到流;或者 hook 地址填的是127.0.0.1,结果 ZLMediaKit 在另一台机器上,回调永远发不过来。

4.1 数据库初始化与后端启动

先把 MySQL 建库建表,导入项目提供的 SQL 脚本。然后改application.yml(或按版本对应的application-dev.yml)里的数据库连接、Redis 连接、服务端口。这些配置项比较直白,照着改就行。要留意的是数据库账号的权限,别用 root 跑业务,单开一个账号只授权这个库的增删改查。

后端打包用 Maven,构建产物是一个可执行的 jar。启动方式我建议用nohup java -jar或者直接写 systemd 服务。systemd 的好处是能配自动重启、能统一管理日志、开机自启也方便,比裸nohup规范得多。

# 以 systemd 服务方式启动 wvp 后端 [Unit] Description=WVP GB28181 Platform After=network.target mysqld.service redis.service [Service] Type=simple WorkingDirectory=/opt/wvp ExecStart=/usr/bin/java -jar /opt/wvp/wvp.jar --spring.config.location=/opt/wvp/application.yml Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

4.2 配置文件里的 IP 到底怎么填

这是全篇我认为最值得反复强调的部分。wvp 的配置文件里有好几个 IP 字段,名字相似但用途完全不同,填错了就是"能注册不能播放"或者"内网能播外网不能播"。我按用途把它们分清楚。

SIP 相关配置里的ip是 SIP 信令服务器监听的地址,一般是本机内网 IP。domain是 SIP 域,通常填一个 10 位的行政区划编码,符合国标规范。id是平台自身的国标编码,同样是 10 位数字。password是平台和下级设备或平台之间鉴权用的密码,要保证双方一致。

媒体节点配置里的ip是 ZLMediaKit 的地址,hook-ip是 ZLMediaKit 主动回调 wvp 时用的地址——这个特别容易填错。如果 wvp 和 ZLMediaKit 在同一台机器,hook-ip127.0.0.1没问题;如果分开部署,就要填 wvp 所在机器的、ZLMediaKit 能访问到的 IP。sdp-ip是写进 SDP 里告诉设备往哪个 IP 推流的地址,必须是设备能访问到的 ZLMediaKit 地址。stream-ip是播放时拼给播放器的地址,必须是播放器能访问到的地址。secret要和 ZLMediaKit 的admin_secret一致。

这四个 IP 字段在很多部署环境里其实是同一个值,导致大家以为它们无所谓,一旦网络环境复杂起来(比如多网卡、NAT、内外网分离)就暴露出区别了。我的建议是先在单网卡环境跑通,理解每个字段的作用,再去处理复杂网络。

配置项作用单机常见填法复杂网络注意事项
SIP ipwvp 信令监听地址本机内网 IP多网卡时选设备可达的网卡
media ipZLMediaKit 地址127.0.0.1 或本机 IPwvp 能访问到即可
hook-ipZLMediaKit 回调地址127.0.0.1需 ZLMediaKit 可达
sdp-ip设备推流目标地址本机 IP必须是设备可达地址
stream-ip播放地址前缀本机 IP必须是播放器可达地址
secretAPI 鉴权密钥自定义强密码与 ZLMediaKit 一致

4.3 前端打包与访问

前端是 Vue 项目,打包后是静态文件,通常由后端托管或者交给 Nginx。用后端托管最省事,打包好的文件放到指定目录即可,访问http://服务器IP:端口就能打开。用 Nginx 托管的好处是可以配 HTTPS、可以做静态资源的缓存优化,生产环境我倾向于用 Nginx。

打包的时候注意后端的接口地址配置,别把localhost打进生产包里,否则前端在浏览器里请求localhost肯定失败。这个坑很经典,本地开发不觉得有问题,一上线就白屏或者接口全 404。

4.4 设备接入与级联配置

设备接入有几种情况:设备主动注册到 wvp,或者 wvp 主动去注册到设备。前者是最常见的,设备里配置平台的 SIP 服务器 IP、端口、域、平台 ID 和密码,设备上线后就会周期性地发注册和心跳。wvp 侧只要保证这些参数和配置一致,设备就会显示在线。

级联是另一回事,指的是平台和平台之间对接。wvp 可以配置为上级平台,接收下级平台的注册;也可以配置为下级平台,主动注册到上级。配置级联的时候要特别留意两个方向:注册方向决定了谁主动发注册请求,媒体方向决定了点播请求从哪边发起。

这里回答一个搜得比较多的问题:上级平台想主动向下级联拉取资源,为什么有时候不行。核心在于级联方向的定义和注册机制。如果下级平台配置的是"主动注册到上级",那么下级会定期向上级发送注册和目录推送,上级侧能看到设备目录;如果下级配置成"被动接收上级注册",那上级需要主动去连下级,此时上级才能掌握主动性。实际使用中出现的"上级拉不到下级资源",很多时候是下级没有把目录推上去,或者上级的 SIP 域、编码和下级对不上,导致目录查询响应被丢弃。排查这类问题,先从抓包看双方 SIP 消息的交互情况入手,比在页面上瞎点有效率得多。

5. 全流程联调与故障排查实战

部署完不等于能用,联调才是真正见功力的地方。我把它分成"能不能注册""能不能看到目录""能不能点播""能不能回放"四个递进的关卡,一关一关过,出问题的时候也能快速定位到是在哪个环节断的。

5.1 用抓包定位信令问题

信令问题的排查,抓包几乎是最有效的手段。因为 SIP 交互有固定的消息序列,注册是 REGISTER 加 401 挑战加带鉴权的 REGISTER,心跳是 MESSAGE,目录查询是 MESSAGE 带 XML body,点播是 INVITE 加 ACK。你抓一段包,看到消息停在哪一步,基本就知道问题在哪。比如只看到 REGISTER 没看到 401,说明请求根本没到 wvp,可能是端口不对或者防火墙拦了;看到 401 但设备不再发第二次 REGISTER,说明设备的鉴权算法或者密码有问题。

抓包建议在服务器上抓,用tcpdump指定 5060 端口,抓下来的包拖到 Wireshark 里,用sip过滤器一筛,交互序列一目了然。如果设备侧也能抓,两边对照着看更清楚,能判断是包没出去还是没回来。

5.2 常见问题速查

我把这几年遇到的高频问题整理成一张表,排查的时候可以按表现对号入座。表里的每一项都是在实际环境里验证过的,不是凭空列出来的。

现象常见原因排查方向
设备一直离线注册失败或心跳超时抓包看 REGISTER 与 401,核对域和密码
设备在线但无目录目录查询未响应或 XML 解析失败看 MESSAGE 的 XML,检查编码格式
点播黑屏无画面收流端口不通或 sdp-ip 填错确认 ZLMediaKit 是否收到 RTP,检查 firewall
画面卡顿花屏UDP 丢包或缓冲不足调内核缓冲,检查网络质量与码率
播放地址外网打不开stream-ip 填了内网地址改为公网可达地址或用转发
wvp 调用媒体接口失败secret 不一致核对两侧密钥,看 ZLMediaKit 日志
回放检索不到设备不支持或时间范围错误确认设备能力,检查查询时间段
级联目录为空注册方向或域配置不匹配抓包看双方 MESSAGE 与目录推送

5.3 语音对讲和录像回放的验证

语音对讲是国标里比较容易出问题的功能,因为它涉及双向的音频流。平台要下发一个带音频的 INVITE,设备要能接收来自平台的 RTP 音频并且播放。配置上一次典型的坑是设备的音频编码和平台不一致,比如设备只支持 G.711A 而平台协商成了别的格式,结果就是听不到声音。验证的时候先在 wvp 页面上操作对讲,同时抓 RTP 包看有没有音频数据从平台发向设备,再确认设备的扬声器设备是否被占用。

录像回放走的是另一套信令:平台下发带回放标识的 INVITE,SDP 里指定回放时间和倍速,设备用 RTSP 或 RTP 回传历史视频。回放不成功的时候,先确认设备本身支不支持录像回放(有些设备只支持实时不支回放),再看时间段的格式对不对,国标里时间是带时区的,格式写错设备会直接拒绝。

6. 长期运行要做的几件事

系统跑起来只是开始,能不能稳定运行半年一年,取决于你在运维上做了多少准备。这一块我不讲大道理,只讲我自己在实际项目里会做的具体动作。

6.1 日志与监控

日志分散在三个地方:wvp 的 Java 日志、ZLMediaKit 的运行日志、MySQL 和 Redis 的日志。我一般用 systemd 把前两者的标准输出收集起来,配合 logrotate 做轮转,避免日志把磁盘写满——这个事我真实遇到过,ZLMediaKit 日志没有轮转,跑了一个月磁盘告警,排查半天发现是日志占了几十 G。

监控方面,至少要盯几个指标:媒体节点的 CPU 和内存、网络带宽、当前在线流数量、设备在线率。ZLMediaKit 提供了获取服务器统计信息的 API,可以定时拉取写到监控系统里。设备在线率这个指标特别有用,它能在用户投诉之前告诉你哪台设备掉线了。

6.2 容量规划和扩展

一个 ZLMediaKit 实例能扛多少路,取决于分辨率和码率。1080P 4Mbps 的流和 720P 1Mbps 的流,承载能力差好几倍。评估的时候别只看路数,要看总带宽和转封装的开销。如果开了 HLS 切片,CPU 消耗会明显上升,因为要持续转封装和切片。

扩展的思路是按需增加媒体节点。wvp 支持配置多个媒体节点,新设备接入的时候可以指定用哪个节点。这里的关键是流媒体节点之间能不能互相转发——如果多节点之间需要转发流,那节点之间的网络带宽就是瓶颈,规划的时候要算进去。

6.3 备份和升级

数据库要定期备份,wvp 的设备配置、通道信息、用户权限都在里面,丢了重建很麻烦。备份用mysqldump定时跑就行,存到异地或者对象存储里。配置文件(wvp 的 yml、ZLMediaKit 的 config.ini)也要纳入版本管理,改之前先备份,别直接在服务器上手改不留底。

升级的时候先在测试环境验证,尤其 ZLMediaKit 和 wvp 之间有版本匹配关系,某些 API 或 hook 参数在不同版本之间有变化,跨版本升级可能导致功能异常。升级前把关键的配置项列一个清单,升级后逐项比对,避免新版配置项默认值变了把原有功能搞坏。

这套系统说白了就是"信令加媒体"两件事,理解了这层,剩下的都是配置和排查的活儿。我个人最大的体会是:别急于一次把功能全开,先把注册和实时点播跑通,再加回放、加级联、加对讲,一步一步来,每加一个功能就验证一遍。这样出问题的时候,范围永远是新加的那部分,排查成本非常低。反过来,一上来全配齐,结果几十个配置项同时可能是错的,那才是真正的噩梦。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询