简介:本资源是基于GB28181国标开发的wvp-GB28181服务端平台完整源码工程,面向视频监控系统开发者、安防集成工程师及物联网协议学习者,解决GB28181标准在实际项目中的注册、心跳、Invite媒体协商等核心流程落地难题,适用于平安城市、智慧交通等需跨平台视频互联的实战场景。压缩包含169个文件,以58个Java业务逻辑文件(实现SIP信令处理、设备管理、流媒体控制等)、78个XML配置与协议模板文件(含国标消息体定义、设备目录结构、SIP头字段映射)为主干,辅以YML配置、日志与权限管理相关脚本,整体仅579KB,轻量易部署。已有2710人学习下载,提供可直接编译运行的完整工程结构、标准化的模块划分(如sip、device、stream、auth)、关键流程注释详尽的源码及配套说明文档,助读者快速掌握GB28181服务端开发范式与协议调试要点。
1. WVP-GB28181 不是“又一个国标平台”,它是能当天部署、次日接入海康/大华/宇视设备的轻量级流媒体中枢
你手头有一台刚出厂的海康IPC,设备已按GB/T 28181-2016配置好SIP注册参数,但用Wireshark抓包发现REGISTER一直超时;或者你正被甲方催着“三天内把37路老款大华NVR拉进统一视频平台”,而现成的商用平台动辄要配K8s集群+Redis哨兵+专用GPU转码卡——这时候,WVP-GB28181 就不是“开源项目”那么简单了。它是一套不依赖Java容器、不强制MySQL、单机4核8G即可承载200路国标信令+50路H.265实时流转发的精简型GB28181服务端,核心逻辑全由Spring Boot + Netty实现,信令层与媒体流层解耦清晰,连wvp-gb28181这个仓库名都透着一股“只干GB28181这一件事”的狠劲。它不提供Web管理界面(得配配套的wvp-web),也不打包FFmpeg(得自己装),但正因如此,它成了安防集成商、边缘AI盒子厂商、甚至高校实验室做国标协议教学时最常被“抄底复用”的那个底层服务模块。如果你需要的是可嵌入、可裁剪、可调试、能看清每条SIP消息字段含义的GB28181服务,而不是一个黑匣子式的“开箱即用平台”,那WVP就是你该从源码编译的第一站。
2. 从零构建 WVP-GB28181:编译、配置、启动三步闭环
WVP 的官方发布包(如wvp-4.3.0.jar)虽带application.yml模板,但实际部署中90%的翻车都源于配置项与真实网络拓扑错位。我建议跳过jar包直跑,坚持源码编译——不是为了炫技,而是因为pom.xml里藏着三个关键开关:netty-sctp是否启用(影响华为/部分定制IPC兼容性)、spring-boot-starter-webflux是否排除(避免与旧版Netty冲突)、以及ffmpeg路径是否硬编码为/usr/bin/ffmpeg(这在Docker里必炸)。下面以Ubuntu 22.04 + OpenJDK 17 + FFmpeg 6.1为基准环境,走一遍真正能落地的构建链。
2.1 源码拉取与基础依赖校验
先确认Java和Maven版本必须严格匹配:WVP主干分支(v4.x)要求JDK 17+,且Maven 3.8.6以上。低版本会导致spring-boot-maven-plugin:3.1.0插件解析失败,报错Plugin execution not covered by lifecycle configuration——这不是IDE问题,是Maven本身对Spring Boot 3.x生命周期绑定不兼容。
# 验证Java版本(必须输出17.x) java -version # 输出应类似:openjdk version "17.0.8" 2023-07-18 # 验证Maven(必须3.8.6+) mvn -v # 输出应含:Apache Maven 3.8.7 # 拉取官方源码(注意:不要用GitHub页面上的zip下载,会缺.gitmodules) git clone https://gitee.com/610546279/wvp-GB28181.git cd wvp-GB28181 git checkout v4.3.0 # 稳定分支,v4.4.0尚有SIP ACK重传bug提示:WVP使用Git Submodule管理
wvp-common和wvp-protocol两个核心模块。若git clone后ls -la看不到common/和protocol/目录,请立即执行git submodule update --init --recursive,否则编译时会报package com.genersoft.wvp.common.exception does not exist。
22.2 编译前的关键配置预埋
WVP的pom.xml默认开启netty-sctp支持,但Ubuntu默认不装SCTP内核模块,强行启用会导致启动时报java.lang.UnsatisfiedLinkError: no netty_transport_native_sctp in java.library.path。解决方案不是装sctp,而是在编译前关闭它:
<!-- 修改 wvp-GB28181/pom.xml,找到 <profiles> 标签下 id="default" 的 profile --> <profile> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <!-- 在此处新增一行,禁用SCTP --> <netty.sctp.enabled>false</netty.sctp.enabled> </properties> </profile>同时,检查wvp-GB28181/wvp-server/src/main/resources/application.yml中的ffmpeg路径。默认值/usr/bin/ffmpeg在Docker容器里大概率不存在,但不要直接改成ffmpeg(会因PATH未生效而找不到),而应改为绝对路径并确保容器内已挂载:
# application.yml 片段 media: ffmpeg-path: "/opt/ffmpeg/bin/ffmpeg" # 后续Dockerfile中需对应挂载 ffprobe-path: "/opt/ffmpeg/bin/ffprobe"2.3 执行编译并验证jar结构
执行编译命令时,务必加-Dmaven.test.skip=true跳过单元测试——WVP的测试用例依赖本地Redis和MySQL,而我们目标是快速产出可运行jar:
mvn clean package -Dmaven.test.skip=true -P default # 成功后,生成物在 wvp-GB28181/wvp-server/target/wvp-server-4.3.0.jar验证jar是否包含全部依赖(这是WVP能“单jar运行”的前提):
jar -tf wvp-server/target/wvp-server-4.3.0.jar | grep -E "(netty|spring|ffmpeg)" | head -10你应该看到类似输出:
BOOT-INF/lib/netty-transport-4.1.94.Final.jar BOOT-INF/lib/spring-boot-starter-web-3.1.0.jar BOOT-INF/lib/ffmpeg-cli-wrapper-1.12.jar若无ffmpeg-cli-wrapper,说明wvp-common子模块未正确拉取或编译失败,需回退检查Submodule步骤。
2.4 启动前的最小化配置清单
WVP启动不依赖数据库,但必须配置三项网络参数才能完成SIP注册。以下是最小application.yml改写模板(仅保留必需字段):
server: port: 1080 address: 0.0.0.0 sip: ip: 192.168.1.100 # 本机对外IP,非127.0.0.1!必须与IPC配置的“平台IP”一致 port: 5060 transport: UDP expires: 3600 media: ip: 192.168.1.100 # 与sip.ip必须相同,GB28181媒体流RTP地址由此生成 port-range: 30000-30100 ffmpeg-path: "/usr/bin/ffmpeg" redis: host: 127.0.0.1 port: 6379 database: 0注意:
sip.ip和media.ip填错是新手第一大坑。很多教程写成localhost或127.0.0.1,结果IPC能发REGISTER但收不到200 OK——因为WVP回复的Contact头里带127.0.0.1:5060,IPC根本无法反向连接。务必填服务器真实局域网IP。
3. 设备接入实战:海康IPC注册、音视频拉流、语音对讲三连通
WVP本身不提供设备管理UI,所有操作靠HTTP API或直接改数据库。但别慌——它的API设计极度克制,只有6个核心接口就覆盖全部设备生命周期。我们以海康DS-2CD3T86G2-LIU(固件V5.6.10)为例,走通从注册到对讲的全链路。
3.1 设备注册:用curl触发SIP REGISTER
海康IPC的GB28181配置入口在:配置 > 网络 > 高级设置 > 平台对接 > GB28181。关键参数如下:
- 平台ID:
31011500991320000001(示例,需与WVP配置的platform.id一致) - 平台IP:
192.168.1.100(即WVP的sip.ip) - 平台端口:
5060 - 本地端口:
5060(IPC自身SIP监听端口) - 心跳间隔:
60(秒)
配置保存后,IPC会自动发送REGISTER。此时在WVP服务器执行:
# 查看WVP日志,过滤SIP信令 tail -f logs/wvp.log | grep -i "register\|200 ok"正常应看到:
INFO c.g.w.s.s.SipMessageHandler - Received REGISTER from /192.168.1.200:5060 INFO c.g.w.s.s.SipMessageHandler - Send 200 OK to /192.168.1.200:5060若只看到REGISTER无200 OK,90%是防火墙拦截UDP 5060端口。用sudo ufw status检查,开放命令为:
sudo ufw allow 5060/udp sudo ufw allow 30000:30100/udp # RTP媒体端口范围3.2 视频拉流:通过WVP内置WebRTC网关
WVP不自带播放器,但提供标准WebRTC接口。假设设备编码为310115009913200000013101150000000001(平台ID+设备ID),拉流URL为:
http://192.168.1.100:1080/webrtc/player.html?app=live&stream=310115009913200000013101150000000001原理说明:
player.html是WVP内置的简易WebRTC播放页,它调用/api/webrtc/start接口发起拉流。WVP收到请求后,先向IPC发送SUBSCRIBE订阅视频流,IPC回送NOTIFY携带SDP,WVP再将SDP转给浏览器。整个过程无需额外信令服务器(如Janus),因为WVP自己实现了WebRTC信令中继。
3.3 语音对讲:GB28181 Annex D流程实操
GB28181语音对讲(Annex D)比视频复杂,需双向建立RTP通道。WVP通过/api/device/talk接口触发:
# 发起对讲(POST) curl -X POST "http://192.168.1.100:1080/api/device/talk" \ -H "Content-Type: application/json" \ -d '{ "deviceId": "310115009913200000013101150000000001", "streamType": 35, # 音频流类型,固定值 "mediaServerId": "default" }'成功返回:
{"code":0,"msg":"success","data":{"callId":"1234567890abcdef"}}此时WVP会向IPC发送INFO消息,IPC回送200 OK后,WVP开始监听本地UDP端口(如30001)接收音频流。关键点在于:浏览器端需用Web Audio API采集麦克风,并将PCM数据编码为G.711 A-law,再通过RTCPeerConnection发送到WVP分配的RTP端口——这部分逻辑由配套的wvp-web前端实现,若自行开发,必须严格遵循GB28181 Annex D的RTP payload type 8(PCMA)封装规范。
4. 避坑指南:五个让集成工程师凌晨三点还在抓包的真实问题
WVP文档简洁得近乎吝啬,但生产环境里的坑往往藏在参数组合的缝隙里。以下是我在37个现场项目中踩出的血泪经验,按现象→原因→解决三段式整理,拒绝模糊描述。
4.1 现象:IPC注册成功,但WVP日志持续刷Device not found: xxx,无法拉流
原因:WVP默认只接受platform.id与配置文件中sip.platform-id完全一致的设备。海康IPC的平台ID配置项名为“国标编号”,但部分固件(如V5.4.10)会自动在末尾补零,导致实际发送的REGISTER中To: <sip:310115009913200000010000@...>比配置多4个0。
解决:在application.yml中启用ID截断模式:
sip: platform-id: "31011500991320000001" platform-id-length: 20 # 强制截取前20位匹配4.2 现象:视频能拉,但画面卡顿严重,Wireshark显示大量RTP丢包
原因:WVP的media.port-range(如30000-30100)与Linux系统可用端口范围冲突。Ubuntu默认net.ipv4.ip_local_port_range = 32768 60999,当WVP随机选到32768以下端口时,内核拒绝绑定。
解决:扩大系统端口范围并重启WVP:
echo 'net.ipv4.ip_local_port_range = 10000 65535' | sudo tee -a /etc/sysctl.conf sudo sysctl -p4.3 现象:语音对讲发起后,IPC端无声音,WVP日志报Failed to start talk: media server not found
原因:WVP的语音对讲依赖独立的media-server模块,默认使用ffmpeg进行音频转码。若ffmpeg-path指向的二进制不支持libopus(如Ubuntu apt安装的ffmpeg),则无法生成WebRTC兼容的Opus流。
解决:编译带Opus支持的FFmpeg(关键参数):
./configure --enable-libopus --enable-gpl --enable-nonfree make -j$(nproc) sudo make install然后在application.yml中指定新路径:ffmpeg-path: "/usr/local/bin/ffmpeg"
4.4 现象:多台IPC注册后,WVP内存持续增长至OOM,jstat -gc显示Old Gen满
原因:WVP的DeviceManager缓存设备对象默认永不过期,且未实现LRU淘汰。200台设备长期运行后,每个设备维持约1.2MB内存(含SIP对话状态、媒体流元数据)。
解决:在application.yml中添加设备缓存策略:
device: cache: max-size: 100 # 最多缓存100台设备 expire-after-write: 30m # 写入30分钟后过期4.5 现象:Docker部署后,IPC能注册,但RTP流始终无法到达WVP容器
原因:Docker默认使用bridge网络,宿主机iptables会SNAT UDP包,导致IPC发往192.168.1.100:30000的RTP包被改源IP,WVP收包时校验失败。
解决:启动容器时添加--network host参数,让WVP直接使用宿主机网络栈:
docker run -d \ --network host \ -v /path/to/application.yml:/app/application.yml \ -v /path/to/ffmpeg:/opt/ffmpeg \ wvp-server:4.3.05. 进阶技巧:用Python脚本自动化设备批量注册与状态巡检
WVP的HTTP API虽简单,但手动调用50台设备的注册请求显然不现实。我写了一个轻量级Python巡检脚本(wvp_batch.py),它能:① 读取Excel设备列表自动生成注册请求;② 每5分钟轮询设备在线状态;③ 发现离线设备自动触发告警邮件。核心逻辑不在框架,而在如何绕过WVP的API限流和会话校验。
5.1 设备列表Excel格式定义
WVP不校验设备密码,但要求serialNumber字段唯一。Excel(devices.xlsx)必须含三列:
| deviceId | name | manufacturer |
|---|---|---|
| 310115009913200000013101150000000001 | 前门岗 | HIKVISION |
| 310115009913200000013101150000000002 | 后门岗 | DAHUA |
注意:
deviceId必须是20位平台ID+20位设备ID拼接(共40位),这是GB28181强制要求。海康IPC的设备ID可在Web界面“系统配置 > 系统 > 系统信息”中查看。
5.2 批量注册脚本核心逻辑
# wvp_batch.py import requests import pandas as pd from time import sleep WVP_URL = "http://192.168.1.100:1080" HEADERS = {"Content-Type": "application/json"} def register_device(device): """向WVP注册单台设备""" payload = { "deviceId": device["deviceId"], "name": device["name"], "manufacturer": device["manufacturer"], "model": "unknown", "firmware": "unknown", "registerWay": 0, # 0=主动注册,1=被动注册 "status": 1 # 1=在线 } try: resp = requests.post(f"{WVP_URL}/api/device", json=payload, timeout=5) if resp.status_code == 200: print(f"✓ {device['name']} 注册成功") else: print(f"✗ {device['name']} 注册失败: {resp.text}") except Exception as e: print(f"✗ {device['name']} 请求异常: {e}") # 主流程 df = pd.read_excel("devices.xlsx") for _, row in df.iterrows(): register_device(row.to_dict()) sleep(0.3) # 避免WVP API限流(默认10qps)5.3 状态巡检与告警机制
WVP的/api/device/status接口返回JSON数组,但不包含设备最后心跳时间。我们只能通过/api/device/{deviceId}获取单台详情,其中keepaliveTime字段即最后心跳时间戳(毫秒)。巡检逻辑如下:
| 字段 | 说明 | 判定逻辑 |
|---|---|---|
keepaliveTime | 最后心跳时间戳 | 若当前时间 - keepaliveTime > 120000(2分钟),判定离线 |
status | 设备状态码 | 0=离线,1=在线,2=报警中 |
def check_device_status(device_id): """检查单台设备在线状态""" try: resp = requests.get(f"{WVP_URL}/api/device/{device_id}", timeout=3) if resp.status_code != 200: return False, "API异常" data = resp.json().get("data", {}) last_heartbeat = data.get("keepaliveTime", 0) now_ms = int(time.time() * 1000) if now_ms - last_heartbeat > 120000: return False, f"心跳超时: {last_heartbeat}" return True, "在线" except Exception as e: return False, f"请求失败: {e}" # 巡检循环 while True: offline_devices = [] for device_id in device_list: is_online, reason = check_device_status(device_id) if not is_online: offline_devices.append((device_id, reason)) if offline_devices: send_alert_email(offline_devices) # 自行实现邮件函数 time.sleep(300) # 每5分钟巡检一次5.4 关键参数表:WVP API高频调用参数速查
| 接口 | 方法 | 必填参数 | 说明 | 调试技巧 |
|---|---|---|---|---|
/api/device | POST | deviceId,name | 设备注册 | registerWay=0时WVP主动向IPC发SUBSCRIBE |
/api/device/{id} | GET | 无 | 获取设备详情 | keepaliveTime单位为毫秒,需除1000转秒 |
/api/device/{id}/stream | POST | streamType=1(主码流) | 开启视频流 | streamType=2为子码流,35为音频流 |
/api/device/talk | POST | deviceId,streamType=35 | 启动语音对讲 | 必须先调用/stream开启音频流 |
/api/media_server | GET | 无 | 获取媒体服务器列表 | WVP默认只有一台default,无需额外配置 |
从那以后我每次部署WVP,都强制走一遍tcpdump -i any port 5060 -w sip.pcap抓包,对照RFC3261逐行比对ACK/200 OK的Via、Contact、Record-Route字段是否符合预期——不是因为信不过WVP,而是因为IPC厂商对GB28181的实现各有各的“理解”。希望帮到你。
本文还有配套的精品资源,点击获取