GB28181流媒体服务器实战:用WVP+ZLMediakit构建企业级监控中枢

最近在帮一个朋友的公司搭建一套内部安防监控系统,他们原有的设备品牌混杂,管理起来非常头疼。经过一番调研,最终我们决定基于GB28181标准来统一整合。这个过程中,WVP和ZLMediakit的组合让我们少走了很多弯路。今天我就把从零开始搭建这套系统的完整过程,以及踩过的坑和优化心得,毫无保留地分享出来。无论你是负责运维的工程师,还是需要快速实现监控平台对接的开发者,这篇文章都能给你提供一条清晰的路径。

GB28181这个标准,简单来说就是为视频监控设备制定的一套“普通话”。它让不同厂家生产的摄像头、NVR都能用同一种语言进行通信和管理。而WVP和ZLMediakit,就像是这套系统的大脑和心脏——一个负责指挥调度(信令控制与平台管理),一个负责血液输送(流媒体的接收、转码与分发)。把它们组合起来,你就能搭建出一个功能完整、性能稳定的国标流媒体服务器。

1. 环境准备与基础概念扫盲

在动手搭建之前,我们需要先理清几个核心概念,并准备好相应的运行环境。这就像盖房子前要先看图纸和备料一样,能避免后续很多不必要的麻烦。

首先明确一下我们这套技术栈的分工:

  • WVP (Web Video Platform): 它是整个系统的“管理平台”和“信令服务器”。负责接收国标设备的注册、处理设备控制指令(如云台控制)、发起视频流请求(Invite),并提供Web界面供用户预览、回放录像。
  • ZLMediaKit: 它是专业的“流媒体服务器”。负责接收来自摄像头通过RTP协议推送过来的音视频流(PS流),并转换成RTSP、RTMP、HLS、HTTP-FLV等多种协议,分发给不同的客户端(如VLC、浏览器、移动端APP)。

它们之间的协作流程可以概括为:摄像头向WVP注册 -> 用户在WVP界面上点击预览 -> WVP向摄像头发起SIP Invite请求 -> 摄像头同意后,开始向ZLMediaKit指定的端口推送RTP流 -> ZLMediaKit接收流并转换 -> 用户通过WVP界面获取到ZLMediaKit分发出来的流地址进行播放。

提示:建议将WVP和ZLMediaKit部署在同一内网环境中,以减少网络延迟和复杂度。生产环境可以考虑容器化部署。

接下来是基础环境。我将以主流的Ubuntu 20.04 LTS系统为例。你需要一台至少有2核CPU、4GB内存和20GB磁盘空间的服务器。首先,更新系统并安装必要的依赖。

# 更新软件包列表并升级现有软件
sudo apt update && sudo apt upgrade -y

# 安装编译工具和基础依赖
sudo apt install -y openjdk-11-jdk maven git curl wget net-tools
sudo apt install -y ffmpeg libssl-dev libsdl-dev libavcodec-dev libavutil-dev libavformat-dev

Java环境是运行WVP所必需的,而FFmpeg和相关开发库则是ZLMediaKit编译和运行的基础。确保Java版本为11。

# 验证Java安装
java -version
# 输出应包含 "openjdk version \"11.x.x\""

2. ZLMediaKit流媒体服务器的编译与部署

我们先部署流媒体引擎ZLMediaKit,因为它相对独立,且是视频流数据的最终归宿。

2.1 获取源码与编译

ZLMediaKit的编译过程比较标准,但有一些选项需要注意。

# 1. 克隆代码仓库(使用国内Gitee镜像速度更快)
git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit.git
cd ZLMediaKit

# 2. 别忘了更新子模块
git submodule update --init

# 3. 创建并进入编译目录
mkdir build
cd build

# 4. 使用CMake生成编译配置
# -DENABLE_WEBRTC=ON 如果你需要WebRTC支持可以开启,但会引入额外依赖
cmake .. -DCMAKE_BUILD_TYPE=Release

# 5. 开始编译,-j参数根据你的CPU核心数调整,可以加快速度
make -j4

编译过程可能需要几分钟。完成后,在build/release/linux/Debug或build/release/linux/Release目录下(取决于你的CMake配置),你会找到关键的二进制文件MediaServer。

2.2 配置与启动

ZLMediaKit的配置文件是config.ini,通常位于与MediaServer相同的目录。我们首先复制一份默认配置,然后进行关键修改。

# 假设你在build目录下
cd release/linux/Debug
cp config.ini.default config.ini

现在,用文本编辑器(如vim或nano)打开config.ini,重点关注以下几个部分:

[api]
# 是否启用HTTP API,WVP需要通过API与ZLMediaKit交互,必须开启
apiSecret=你的API密钥,例如:035c73f7-bb6b-4889-a715-d9eb2d1925cc
# 记住这个密钥,WVP配置时需要

[rtp]
# 国标RTP接收端口,摄像头推流的目标端口
port=10000
# 如果端口被占用,可以修改为其他值,如50600

[hls]
# HLS切片时长,影响点播延迟
segDur=2.0

[rtsp]
# RTSP服务端口
port=554

[http]
# HTTP API、HTTP-FLV、HLS等服务的端口
port=80
# SSL端口,如果启用HTTPS则配置
sslPort=443

一个最简化的关键配置示例如上。其中apiSecret需要自定义一个复杂的字符串,rtp.port是接收国标PS流的端口,务必确保防火墙开放此端口。

配置完成后,就可以启动ZLMediaKit了。

# 前台启动,方便查看日志
./MediaServer -c config.ini

# 或者使用后台启动,并输出日志到文件
nohup ./MediaServer -c config.ini > zlm.log 2>&1 &

启动成功后,你可以通过访问 http://你的服务器IP:80/index/api 来验证API是否正常工作(需要带上apiSecret参数),或者用netstat -tunlp | grep MediaServer查看相关端口是否监听。

3. WVP-GB28181-pro 信令服务器的部署与配置

WVP是项目的核心管理平台,它基于Spring Boot开发。我们使用目前最活跃的 wvp-GB28181-pro 分支。

3.1 数据库初始化与源码获取

WVP需要MySQL数据库。如果你还没有安装MySQL,可以快速安装一个。

sudo apt install -y mysql-server
sudo mysql_secure_installation # 运行安全初始化脚本,设置root密码等

登录MySQL,为WVP创建数据库和用户。

CREATE DATABASE IF NOT EXISTS wvp CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'wvp_user'@'%' IDENTIFIED BY '你的强密码';
GRANT ALL PRIVILEGES ON wvp.* TO 'wvp_user'@'%';
FLUSH PRIVILEGES;

接下来获取WVP源码并编译。

# 克隆项目
git clone https://github.com/648540858/wvp-GB28181-pro.git
cd wvp-GB28181-pro

# 编译打包。如果下载依赖慢,可以配置Maven国内镜像
mvn package -DskipTests

编译成功后,在 target 目录下会生成 wvp-pro-*.jar 文件。

3.2 关键配置文件详解

WVP的配置文件是 src/main/resources/application.yml。我们需要将其复制到jar包同级目录,并做针对性修改。这里我提炼出最关键的几个部分。

首先,复制配置文件:

# 假设在target目录
cp ../src/main/resources/application.yml ./application.yml

然后编辑 application.yml:

1. 数据库连接配置:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
    username: wvp_user
    username: 你上面设置的密码

2. ZLMediaKit连接配置(重中之重):

# 流媒体服务配置,关联ZLMediaKit
media:
  id: 你的ZLM唯一ID,如‘your_zlm_id_01’
  ip: 你的ZLMediaKit服务器内网IP
  http-port: 80 # ZLM的HTTP API端口
  http-ssl-port: 443
  rtmp-port: 1935
  rtmp-ssl-port: 1936
  rtp-proxy-port: 10000 # ZLM的国标RTP接收端口,必须与ZLM配置一致
  secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc # 必须与ZLM的apiSecret完全一致

3. SIP服务器配置(WVP自身作为SIP Server):

sip:
  ip: 你的WVP服务器IP
  port: 5060 # SIP信令端口
  domain: 3402000000 # SIP域,可自定义,通常用行政区划码
  id: 34020000002000000001 # SIP服务器ID
  password: admin123 # SIP认证密码,设备注册时会用到

4. 其他网络配置:

# 服务器IP,用于生成流地址等
server-ip: 你的WVP服务器公网IP或内网IP(视访问方式定)

# WVP自身Web服务端口
server:
  port: 18080

3.3 启动WVP与初始化

配置完成后,就可以启动WVP了。

# 在target目录下
java -jar wvp-pro-*.jar

首次启动时,WVP会自动执行数据库迁移脚本,创建所需的表结构。启动成功后,控制台会输出类似“Started WvpApplication in x seconds”的信息。

此时,打开浏览器访问 http://你的服务器IP:18080,应该能看到WVP的登录界面。默认用户名和密码是 admin/admin。

登录后,你需要先进行一项关键配置:在“系统管理”->“流媒体服务”中,添加我们部署的ZLMediaKit。填写的信息(ID, IP, 端口, Secret)必须与 application.yml 中的 media 配置以及ZLM的实际配置完全一致。添加成功后,状态应显示为“在线”。

4. 设备接入与全流程调试

平台搭好了,现在让我们接入一个真实的GB28181摄像头,完成从注册到预览的完整闭环。

4.1 摄像头端配置

不同品牌的摄像头配置界面略有差异,但核心的GB28181客户端配置项大同小异。你需要在摄像头的网络设置或平台接入设置中找到“GB28181”或“国标协议”选项。

以下是一份通用的配置清单,你需要在摄像头网页中填写:

配置项填写说明示例值
启用开启GB28181功能是/True
SIP服务器地址WVP服务器的IP地址192.168.1.100
SIP服务器端口WVP的SIP端口5060
SIP服务器域与WVP配置的sip.domain一致3402000000
设备ID摄像头唯一标识,需符合国标编码规则34020000001320000001
设备认证密码与WVP配置的sip.password一致admin123
本地SIP端口摄像头自身SIP端口,通常自动分配5060
注册有效期心跳间隔,默认3600秒3600
心跳间隔保持注册状态的间隔60

保存配置后,摄像头会主动向WVP发起注册请求。

4.2 WVP平台侧验证

回到WVP的Web界面,进入“设备管理”->“设备列表”。如果网络连通且配置正确,你应该能在几分钟内看到新设备上线,状态由“未注册”变为“在线”。

常见问题排查:

  • 设备一直离线:检查WVP服务器5060端口是否开放(sudo ufw allow 5060 或对应防火墙规则)。在WVP服务器上用 tcpdump -i any port 5060 -vv 抓包,看是否能收到来自摄像头的SIP REGISTER报文。
  • 注册后很快又离线:检查心跳配置。确保WVP和摄像头之间的网络没有阻断UDP包(SIP和RTP都使用UDP)。检查WVP的sip.ip配置是否为摄像头能访问到的地址。
  • WVP显示ZLM离线:检查WVP中流媒体服务的配置,特别是secret是否与ZLM的apiSecret一字不差。访问 http://ZLM_IP:80/index/api/getStat?secret=你的密钥 看ZLM API是否正常响应。

4.3 视频预览与协议转换

当设备在线后,点击设备列表操作栏的“播放”按钮。WVP后台会执行一系列SIP信令交互:

  1. WVP向摄像头发送SIP INVITE消息,其中SDP体包含了ZLMediaKit的RTP接收地址和端口。
  2. 摄像头回复200 OK,确认SDP。
  3. WVP回复ACK。
  4. 摄像头开始向ZLMediaKit的RTP端口(如10000)推送PS格式的音视频流。
  5. ZLMediaKit收到流后,会生成一个唯一的流ID(stream_id),并通过HTTP API通知WVP。
  6. WVP根据流ID,拼装出多种协议的播放地址,返回给前端播放器。

在WVP的播放界面上,你通常可以看到类似以下的地址选项:

  • HTTP-FLV: http://ZLM_IP:80/rtp/流ID.live.flv
  • HLS: http://ZLM_IP:80/rtp/流ID/hls.m3u8
  • WebRTC (如果已开启): webrtc://ZLM_IP:80/rtp/流ID

你可以用VLC播放器打开RTSP地址(rtsp://ZLM_IP:554/rtp/流ID),或者在支持HTTP-FLV的播放器(如前端使用flv.js)中播放FLV地址,来验证流是否成功分发。

注意:第一次发起播放可能会有几秒到十几秒的延迟,这是国标协议信令交互和流建立的过程。后续的停止、PTZ云台控制等操作,也都是通过WVP发送SIP消息到设备来完成的。

5. 性能调优与生产环境考量

让系统跑起来只是第一步,要稳定支撑生产环境,还需要进行一系列优化。

5.1 ZLMediaKit性能调优

ZLM的 config.ini 中有大量可调参数,针对高并发场景,建议关注以下几点:

[general]
# 媒体源无人观看时,自动关闭的等待时间,单位秒。减少可节省资源,但会增加频繁开流的开销。
stream-none-reader-delay-ms=20000

[rtp]
# 单个RTP端口可接收的最大流数量。如果摄像头很多,可能需要增加。
# 也可以启用端口范围,让ZLM自动分配多个RTP端口。
port=10000
# 启用端口范围示例:port_range=30000-30500

[hls]
# HLS切片数量和时长,影响点播延迟和存储开销。
segNum=3
segDur=2.0

[hook]
# 启用hook,WVP依赖于此。确保admin_params中secret配置正确。
enable_flow_report=1
on_stream_changed=1
...

对于内存和CPU,ZLM本身比较高效。主要瓶颈在于网络I/O和转码(如果开启了转码)。务必确保服务器有足够的上行带宽,特别是当需要对外分发多路高清视频流时。

5.2 WVP集群与高可用

单节点WVP存在单点故障风险。对于重要业务,可以考虑WVP集群部署。

  1. 共享数据库:所有WVP节点连接同一个MySQL数据库。
  2. Redis同步:WVP使用Redis来同步集群节点间的设备状态、流信息等。你需要部署一个Redis服务,并在每个WVP的 application.yml 中配置相同的Redis连接信息。
  3. 负载均衡:在前端用Nginx对WVP的Web端口(18080)做负载均衡。对于SIP信令(5060端口),可以采用DNS轮询或者专门的SIP负载均衡设备,但国标设备通常只注册到一个SIP服务器,这需要更复杂的主备切换逻辑。
  4. ZLM集群:WVP支持配置多个ZLM节点。可以在“流媒体服务”中添加多个ZLM,WVP会根据策略(如负载最低)选择不同的ZLM来接收新设备的推流,实现流媒体服务的横向扩展。

5.3 安全与监控

  • 防火墙:严格限制开放端口。仅对外暴露WVP的Web端口(18080)和ZLM的流播放端口(如80、443)。SIP端口(5060)和ZLM的RTP接收端口(如10000)应仅限于内网或可信网络访问。
  • API安全:ZLM的apiSecret和WVP的SIP密码password要使用强密码,并定期更换。
  • 日志与监控:将WVP和ZLM的日志接入ELK等日志系统。监控服务器的CPU、内存、磁盘I/O和网络带宽。特别关注ZLM的getStat API返回的流数量、CPU负载等信息。
  • 存储规划:如果开启了录像功能,需要规划好存储目录(ZLM的rootPath),并考虑使用高性能磁盘或网络存储(NAS/SAN),同时设置日志轮转和旧录像清理策略。

最后,这套系统在跑起来之后非常稳定,但初期调试确实需要耐心。最深的体会是,一定要保证WVP、ZLM、摄像头三者的配置项(尤其是IP、端口、密钥)像齿轮一样严丝合缝地对上。多利用WVP的日志(设置logging.level.org.webrtc=DEBUG可以看更详细的SIP信令日志)和网络抓包工具(Wireshark过滤sip or rtp),它们是定位问题最快的方式。当看到不同品牌的摄像头都在同一个平台上流畅播放时,那种统一管理带来的便捷感,会让你觉得前期的所有折腾都是值得的。

更多推荐