39 KiB
Vol.Pro + Owl + ZLMediaKit 视频监控整合方案
版本: v1.0 日期: 2026-04-29 编制: 浮浮酱 状态: 待确认(未实施)
一、项目概述
1.1 需求背景
现有项目包含四个模块:
| 模块 | 技术栈 | 用途 |
|---|---|---|
| api_sqlsugar | .NET 8 + SqlSugar + Vol.Pro框架 | 后端API服务 |
| web.vite | Vue3 + Vite + TypeScript | Vol.Pro管理端(管理后台) |
| warehouse | Vue3 + 自定义前端 | 用户端(大屏展示/业务端) |
| owl_zlmediakit | Docker + Owl + ZLMediaKit | GB28181视频监控平台 |
目标: 在Vol.Pro管理端中管理NVR和摄像机(增删改查),通过Vol.Pro后端对接Owl获取实时流/回放流地址,向两个前端(管理端+用户端)提供播放接口,实现浏览器中的实时监控、历史回放和云台控制。
1.2 核心问题
- warehouse已有视频UI框架(
Live.vue、History.vue、VideoWall.vue),但无实际播放器集成,数据为硬编码模拟数据 - Vol.Pro后端尚无摄像机/NVR相关实体和控制器
- Owl与Vol.Pro的数据模型不兼容,需要中间层转换
- 播放器选型(浏览器播放GB28181流的方案)
1.3 关键前提:所有修改独立于框架代码
经过对Vol.Pro框架源码和文档的调研,确认:所有自定义代码均可完全独立于框架自动生成代码进行开发和维护。
Vol.Pro框架的设计核心原则是:"自动生成代码不修改,自定义代码写Partial/扩展文件"。具体体现在:
| 层级 | 自动生成文件位置 | 扩展文件位置 | 扩展方式 |
|---|---|---|---|
| Controller | Controllers/模块/表名Controller.cs |
Controllers/模块/Partial/表名Controller.cs |
partial class |
| Service | Services/模块/表名Service.cs |
Services/模块/Partial/表名Service.cs |
partial class + override/委托钩子 |
| Entity | DomainModels/模块/表名.cs |
DomainModels/模块/partial/表名.cs |
partial class + [NotMapped] |
| 前端(Vue) | views/模块/表名.vue + options.js |
extension/模块/表名.jsx 或 .vue |
扩展组件/自定义逻辑 |
| 独立服务 | — | 新建文件,实现IDependency |
Autofac自动IOC注入 |
框架生成的文件中明确注释:
"代码由框架生成,任何更改都可能导致被代码生成器覆盖。如果要增加方法请在当前目录下Partial文件夹编写。"
结论:
- 代码生成器重新生成时,只会覆盖自动生成的文件,不会触碰
Partial/、partial/、extension/目录下的自定义代码 - 所有视频监控相关的业务代码(Owl对接、取流、回放、云台)将写在扩展文件中,与框架代码完全隔离
- 新增的服务(如
OwlApiService)通过实现IDependency接口由Autofac自动注册,无需修改框架的任何配置 - 框架升级时,只需重新运行代码生成器覆盖自动生成的CRUD文件,所有自定义业务逻辑完全保留
二、系统架构设计
2.1 整体架构
┌─────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ web.vite 管理端 │ │ warehouse 用户端 │ │
│ │ (设备管理+视频查看) │ │ (大屏+实时监控墙) │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ │
└─────────────┼──────────────────────────┼────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Vol.Pro 后端 (api_sqlsugar) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 自动生成CRUD模块 (VideoDevice / VideoChannel / VideoRecord) │ │
│ │ ├─ 设备管理 (增删改查) │ │
│ │ ├─ 通道管理 (增删改查) │ │
│ │ └─ 录像管理 (查询) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 扩展API模块 (VideoStreamController) │ │
│ │ ├─ POST /api/VideoStream/live → 获取实时流地址 │ │
│ │ ├─ POST /api/VideoStream/playback → 获取回放流地址 │ │
│ │ ├─ POST /api/VideoStream/ptz → 云台控制 │ │
│ │ ├─ POST /api/VideoStream/snapshot → 获取快照 │ │
│ │ └─ POST /api/VideoStream/stop → 停止播放 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Owl对接服务 (OwlApiService) │ │
│ │ ├─ 设备同步 (定时从Owl拉取设备列表) │ │
│ │ ├─ JWT Token管理 (缓存/刷新) │ │
│ │ ├─ 流地址获取 (调用Owl /channels/:id/play) │ │
│ │ ├─ 回放查询 (调用Owl /recordings) │ │
│ │ └─ 云台控制 (调用Owl /channels/:id/ptz/control) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────┬──────────────────────────────────────────┘
│ HTTP API
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Owl (GoWVP) 平台 │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ SIP信令层 (GB28181设备注册/心跳/控制) │ │
│ │ HTTP API层 (设备/通道/播放/回放/云台) │ │
│ │ WebHook层 (接收ZLM事件回调) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────┬──────────────────────────────────────────┘
│ HTTP API / WebHook
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ ZLMediaKit 流媒体服务器 │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ RTP收流 / RTMP推流 / RTSP拉流 │ │
│ │ 协议转换 (HLS/FLV/WebRTC/RTMP/RTSP) │ │
│ │ 录像存储 (MP4切片) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
2.2 关键设计原则
- Owl作为主设备源: Owl负责GB28181设备接入、SIP信令、流管理,Vol.Pro不直接对接设备,只对接Owl的HTTP API
- 数据双写: 设备在Owl中注册后,通过同步机制将设备信息写入Vol.Pro数据库,实现Vol.Pro管理端对设备的CRUD管理
- 播放地址代理: Vol.Pro后端作为统一入口,前端不直接访问Owl,所有取流请求经Vol.Pro转发(便于权限控制和日志记录)
- 播放器统一: 两个前端使用同一套播放器组件,支持FLV/HLS/WebRTC多协议
三、数据库表设计
3.1 表结构说明
设备统一管理设计:本方案与MC4.0采集网关方案共用统一设备主表
Base_Device,实现视频监控设备和采集网关设备的统一管理,并支持在三维地图上统一标记展示。
3.1.0 统一设备主表 (Base_Device) 【与MC4.0方案共用】
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| DeviceId | uniqueidentifier | ✅ | 主键,Vol.Pro系统内部ID |
| DeviceName | nvarchar(100) | ✅ | 设备名称 |
| DeviceCategory | int | ✅ | 设备大类:1=视频监控, 2=采集网关 |
| DeviceType | int | 设备细分类型(根据Category解释不同值) | |
| SourceId | nvarchar(64) | ✅ | 源系统设备ID(OwlDeviceId或Mc4DeviceId) |
| IpAddress | nvarchar(50) | IP地址 | |
| Port | int | 端口 | |
| IsOnline | int | 是否在线:0=离线, 1=在线 | |
| Location | nvarchar(200) | 安装位置描述 | |
| Lat | float | 纬度(WGS84坐标系) | |
| Lng | float | 经度(WGS84坐标系) | |
| MapModelId | nvarchar(100) | 三维地图模型ID(VgoMap中对应模型的唯一标识) | |
| MapModelScale | float | 三维模型缩放比例,默认1.0 | |
| MapModelRotation | nvarchar(100) | 三维模型旋转角度(JSON格式:{"x":0,"y":0,"z":0}) | |
| Enable | int | 启用状态:0=禁用, 1=启用 | |
| Remark | nvarchar(500) | 备注 | |
| CreateID / Creator / CreateDate | Vol.Pro标准审计字段 | ||
| ModifyID / Modifier / ModifyDate | Vol.Pro标准审计字段 |
MapModelId 字段说明:
- 用于在warehouse前端三维地图(VgoMap)中标记设备位置
- 值由三维地图系统提供,标识对应三维模型的唯一ID
- 管理端可在设备编辑页面中配置/修改此字段
- 三维地图加载时,根据MapModelId将设备图标/模型绑定到地图对应位置
3.1.1 视频设备扩展表 (Device_Video_Ext)
存储视频监控设备特有的扩展信息,通过DeviceId关联Base_Device主表。
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ExtId | uniqueidentifier | ✅ | 主键 |
| DeviceId | uniqueidentifier | ✅ | 关联Base_Device.DeviceId |
| OwlDeviceId | nvarchar(64) | ✅ | Owl系统设备ID(GB28181的DeviceID) |
| Protocol | int | 接入协议:1=GB28181, 2=ONVIF, 3=RTMP, 4=RTSP | |
| Manufacturer | nvarchar(100) | 厂商 | |
| Model | nvarchar(100) | 型号 | |
| ChannelCount | int | 通道数量 | |
| OwlStatus | nvarchar(50) | Owl中的原始状态JSON |
3.1.2 视频通道表 (Video_Channel)
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ChannelId | uniqueidentifier | ✅ | 主键,Vol.Pro系统内部ID |
| OwlChannelId | nvarchar(64) | ✅ | Owl系统通道ID |
| DeviceId | uniqueidentifier | ✅ | 关联Video_Device.DeviceId |
| ChannelName | nvarchar(100) | ✅ | 通道名称 |
| ChannelNo | int | 通道编号 | |
| OwlStreamApp | nvarchar(50) | Owl中的stream app(如rtp) | |
| OwlStreamName | nvarchar(100) | Owl中的stream name | |
| HasPtz | int | 是否支持云台:0=否, 1=是 | |
| HasRecording | int | 是否支持录像:0=否, 1=是 | |
| RecordMode | int | 录像模式:0=不录像, 1=设备录像(SD卡), 2=中心录像(Owl云录像) | |
| IsOnline | int | 是否在线 | |
| SnapshotUrl | nvarchar(500) | 快照图片URL | |
| Location | nvarchar(200) | 安装位置 | |
| Lat | float | 纬度 | |
| Lng | float | 经度 | |
| Enable | int | 启用状态 | |
| Remark | nvarchar(500) | 备注 | |
| CreateID / Creator / CreateDate | 标准审计字段 | ||
| ModifyID / Modifier / ModifyDate | 标准审计字段 |
3.1.3 云录像记录表 (Video_Record)
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| RecordId | uniqueidentifier | ✅ | 主键 |
| OwlRecordId | nvarchar(64) | ✅ | Owl录像记录ID |
| ChannelId | uniqueidentifier | ✅ | 关联Video_Channel.ChannelId |
| StartTime | datetime | ✅ | 开始时间 |
| EndTime | datetime | ✅ | 结束时间 |
| Duration | int | 时长(秒) | |
| FileSize | bigint | 文件大小(字节) | |
| FilePath | nvarchar(500) | 文件路径 | |
| RecordType | int | 录像类型:1=定时录像, 2=告警录像, 3=手动录像 | |
| ThumbUrl | nvarchar(500) | 缩略图URL | |
| CreateDate | datetime | 创建时间 |
3.2 建表SQL (SQL Server)
-- ============================================
-- 统一设备主表(与MC4.0采集网关方案共用)
-- ============================================
CREATE TABLE Base_Device (
DeviceId UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
DeviceName NVARCHAR(100) NOT NULL,
DeviceCategory INT NOT NULL, -- 1=视频监控, 2=采集网关
DeviceType INT, -- 细分类型(根据Category解释)
SourceId NVARCHAR(64) NOT NULL, -- 源系统设备ID(OwlDeviceId或Mc4DeviceId)
IpAddress NVARCHAR(50),
Port INT,
IsOnline INT DEFAULT 0,
Location NVARCHAR(200), -- 安装位置描述
Lat FLOAT, -- 纬度
Lng FLOAT, -- 经度
MapModelId NVARCHAR(100), -- 三维地图模型ID(VgoMap)
MapModelScale FLOAT DEFAULT 1.0, -- 三维模型缩放比例
MapModelRotation NVARCHAR(100), -- 三维模型旋转角度(JSON)
Enable INT DEFAULT 1,
Remark NVARCHAR(500),
CreateID INT,
Creator NVARCHAR(30),
CreateDate DATETIME DEFAULT GETDATE(),
ModifyID INT,
Modifier NVARCHAR(30),
ModifyDate DATETIME
);
CREATE INDEX IX_Base_Device_Category ON Base_Device(DeviceCategory);
CREATE INDEX IX_Base_Device_SourceId ON Base_Device(SourceId);
CREATE INDEX IX_Base_Device_IsOnline ON Base_Device(IsOnline);
CREATE INDEX IX_Base_Device_MapModelId ON Base_Device(MapModelId);
-- ============================================
-- 视频监控设备扩展表
-- ============================================
CREATE TABLE Device_Video_Ext (
ExtId UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
DeviceId UNIQUEIDENTIFIER NOT NULL,
OwlDeviceId NVARCHAR(64) NOT NULL,
Protocol INT DEFAULT 1, -- 1=GB28181, 2=ONVIF, 3=RTMP, 4=RTSP
Manufacturer NVARCHAR(100),
Model NVARCHAR(100),
ChannelCount INT DEFAULT 0,
OwlStatus NVARCHAR(50),
FOREIGN KEY (DeviceId) REFERENCES Base_Device(DeviceId)
);
CREATE INDEX IX_Device_Video_Ext_DeviceId ON Device_Video_Ext(DeviceId);
CREATE INDEX IX_Device_Video_Ext_OwlDeviceId ON Device_Video_Ext(OwlDeviceId);
-- 视频通道表
CREATE TABLE Video_Channel (
ChannelId UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
OwlChannelId NVARCHAR(64) NOT NULL,
DeviceId UNIQUEIDENTIFIER NOT NULL,
ChannelName NVARCHAR(100) NOT NULL,
ChannelNo INT DEFAULT 1,
OwlStreamApp NVARCHAR(50),
OwlStreamName NVARCHAR(100),
HasPtz INT DEFAULT 0,
HasRecording INT DEFAULT 0,
RecordMode INT DEFAULT 0,
IsOnline INT DEFAULT 0,
SnapshotUrl NVARCHAR(500),
Enable INT DEFAULT 1,
Remark NVARCHAR(500),
CreateID INT,
Creator NVARCHAR(30),
CreateDate DATETIME DEFAULT GETDATE(),
ModifyID INT,
Modifier NVARCHAR(30),
ModifyDate DATETIME,
FOREIGN KEY (DeviceId) REFERENCES Base_Device(DeviceId)
);
CREATE INDEX IX_Video_Channel_DeviceId ON Video_Channel(DeviceId);
CREATE INDEX IX_Video_Channel_OwlChannelId ON Video_Channel(OwlChannelId);
CREATE INDEX IX_Video_Channel_IsOnline ON Video_Channel(IsOnline);
-- 云录像记录表
CREATE TABLE Video_Record (
RecordId UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
OwlRecordId NVARCHAR(64) NOT NULL,
ChannelId UNIQUEIDENTIFIER NOT NULL,
StartTime DATETIME NOT NULL,
EndTime DATETIME NOT NULL,
Duration INT,
FileSize BIGINT,
FilePath NVARCHAR(500),
RecordType INT DEFAULT 1,
ThumbUrl NVARCHAR(500),
CreateDate DATETIME DEFAULT GETDATE()
);
CREATE INDEX IX_Video_Record_ChannelId ON Video_Record(ChannelId);
CREATE INDEX IX_Video_Record_StartTime ON Video_Record(StartTime);
CREATE INDEX IX_Video_Record_EndTime ON Video_Record(EndTime);
四、Vol.Pro后端扩展方案
4.1 代码生成步骤
使用Vol.Pro代码生成器自动生成3个模块的CRUD代码:
- Base_Device(统一设备主表,DeviceCategory=1表示视频监控设备)
- Device_Video_Ext(视频设备扩展信息)
- Video_Channel(视频通道)
- Video_Record(云录像)
生成后获得:
Base_DeviceController/Base_DeviceService/IBase_DeviceService/Base_Device实体Device_Video_ExtController/Device_Video_ExtService/IDevice_Video_ExtService/Device_Video_Ext实体Video_ChannelController/Video_ChannelService/IVideo_ChannelService/Video_Channel实体Video_RecordController/Video_RecordService/IVideo_RecordService/Video_Record实体
4.2 Owl对接服务 (OwlApiService)
在Vol.Pro后端创建完全独立的自定义服务,封装对Owl的所有HTTP调用:
// 文件位置:api_sqlsugar/Warehouse/Services/Owl/OwlApiService.cs
// (新建文件,不修改任何框架代码)
/// <summary>
/// Owl API 对接服务
/// </summary>
public interface IOwlApiService : IDependency
{
/// <summary>获取Owl JWT Token(带缓存)</summary>
Task<string> GetTokenAsync();
/// <summary>同步Owl设备列表到本地</summary>
Task<WebResponseContent> SyncDevicesAsync();
/// <summary>同步Owl通道列表到本地</summary>
Task<WebResponseContent> SyncChannelsAsync();
/// <summary>获取实时流播放地址</summary>
Task<StreamPlayResult> GetLiveUrlAsync(string owlChannelId, string protocol = "ws_flv");
/// <summary>获取回放流地址</summary>
Task<StreamPlaybackResult> GetPlaybackUrlAsync(string owlChannelId, DateTime start, DateTime end);
/// <summary>云台控制</summary>
Task<WebResponseContent> PtzControlAsync(string owlChannelId, PtzCommand command);
/// <summary>获取快照</summary>
Task<byte[]> GetSnapshotAsync(string owlChannelId);
/// <summary>停止播放</summary>
Task<WebResponseContent> StopPlayAsync(string owlChannelId);
}
实现要点:
- 新建文件实现
IOwlApiService,不修改任何框架代码 - 实现
IDependency接口,Autofac自动注册到IOC容器 - 使用
IHttpClientFactory创建HTTP客户端 - Token缓存到内存/Redis,过期前自动刷新
- 所有方法内部处理Owl的JWT认证(自动附加Authorization header)
- 返回值统一转换为Vol.Pro的
WebResponseContent格式
4.3 扩展API控制器 (VideoStreamController)
在Vol.Pro中新增独立的Controller文件(不修改任何框架代码),提供取流、回放、云台等扩展接口:
// 文件位置:api_sqlsugar/Warehouse/Controllers/VideoStreamController.cs
// (全新文件,与框架代码无任何耦合)
namespace Warehouse.Controllers
{
[Route("api/VideoStream")]
[JWTAuthorize]
public class VideoStreamController : VolController
{
private readonly IOwlApiService _owlApi;
private readonly IVideo_ChannelService _channelService;
public VideoStreamController(
IOwlApiService owlApi,
IVideo_ChannelService channelService)
{
_owlApi = owlApi;
_channelService = channelService;
}
/// <summary>
/// 获取实时流播放地址
/// </summary>
[HttpPost, Route("GetLiveUrl")]
public async Task<ActionResult> GetLiveUrl([FromBody] LiveUrlInput input)
{
var channel = await _channelService.GetChannelByIdAsync(input.ChannelId);
if (channel == null) return Json(new WebResponseContent().Error("通道不存在"));
var result = await _owlApi.GetLiveUrlAsync(channel.OwlChannelId, input.Protocol);
return Json(new WebResponseContent().OK(null, result));
}
/// <summary>
/// 获取回放流地址
/// </summary>
[HttpPost, Route("GetPlaybackUrl")]
public async Task<ActionResult> GetPlaybackUrl([FromBody] PlaybackUrlInput input)
{
var channel = await _channelService.GetChannelByIdAsync(input.ChannelId);
if (channel == null) return Json(new WebResponseContent().Error("通道不存在"));
var result = await _owlApi.GetPlaybackUrlAsync(
channel.OwlChannelId, input.StartTime, input.EndTime);
return Json(new WebResponseContent().OK(null, result));
}
/// <summary>
/// 云台控制
/// </summary>
[HttpPost, Route("PtzControl")]
public async Task<ActionResult> PtzControl([FromBody] PtzInput input)
{
var channel = await _channelService.GetChannelByIdAsync(input.ChannelId);
if (channel == null) return Json(new WebResponseContent().Error("通道不存在"));
if (channel.HasPtz != 1) return Json(new WebResponseContent().Error("该通道不支持云台"));
var result = await _owlApi.PtzControlAsync(channel.OwlChannelId, new PtzCommand
{
Action = input.Action,
Direction = input.Direction,
Speed = input.Speed
});
return Json(result);
}
/// <summary>
/// 获取通道快照
/// </summary>
[HttpGet, Route("GetSnapshot")]
public async Task<IActionResult> GetSnapshot(Guid channelId)
{
var channel = await _channelService.GetChannelByIdAsync(channelId);
if (channel == null) return Json(new WebResponseContent().Error("通道不存在"));
var bytes = await _owlApi.GetSnapshotAsync(channel.OwlChannelId);
return File(bytes, "image/jpeg");
}
/// <summary>
/// 停止播放
/// </summary>
[HttpPost, Route("StopPlay")]
public async Task<ActionResult> StopPlay([FromBody] StopPlayInput input)
{
var channel = await _channelService.GetChannelByIdAsync(input.ChannelId);
if (channel == null) return Json(new WebResponseContent().Error("通道不存在"));
var result = await _owlApi.StopPlayAsync(channel.OwlChannelId);
return Json(result);
}
}
}
独立性说明:
VideoStreamController是全新创建的文件,不继承任何自动生成的控制器,也不修改任何框架文件。它通过构造函数注入IOwlApiService和IVideo_ChannelService,完全依赖IOC容器解耦。
4.4 设备同步机制
由于Owl是设备的真实数据源(GB28181设备直接注册到Owl),Vol.Pro数据库中的设备需要通过定时同步或手动同步保持与Owl一致。
同步方案:
- 定时同步(推荐):使用Vol.Pro内置的Quartz定时任务,每5分钟执行一次设备同步
- 手动同步:管理端提供"同步设备"按钮,点击后立即拉取Owl设备列表
- Webhook同步(可选高级方案):Owl支持Webhook回调,当设备注册/注销时回调Vol.Pro接口
同步逻辑:
1. 调用 Owl GET /devices 获取设备列表
2. 调用 Owl GET /channels 获取通道列表
3. 对比本地数据库,执行增删改
4. 更新 Video_Device 和 Video_Channel 表
五、前端对接方案
5.1 播放器选型
浏览器播放GB28181视频流,需要支持以下协议:
| 协议 | 延迟 | 浏览器兼容性 | 推荐场景 |
|---|---|---|---|
| WebRTC | < 500ms | Chrome/Firefox/Edge | 实时监控首选 |
| WS-FLV | 1-3s | 全浏览器 | 监控备用 |
| HTTP-FLV | 1-3s | 全浏览器 | 简单场景 |
| HLS | 5-10s | 全浏览器 | 回放首选 |
| RTMP | 2-5s | 需Flash/插件 | 不推荐浏览器 |
推荐方案:
- 实时监控: WebRTC(延迟最低)+ WS-FLV(兼容性备用)
- 历史回放: HLS (m3u8)
- 播放器库:
- mpegts.js - 腾讯开源,支持FLV/HLS,纯JavaScript
- Jessibuca - 纯H5播放器,支持FLV/HLS/WebRTC,wasm解码
- webrtc-streamer - WebRTC专用
最终推荐:使用 Jessibuca 或 mpegts.js(两者都是纯前端,无需插件,支持多协议切换)
⚠️ WebRTC配置前置条件: ZLMediaKit默认不开启WebRTC,需在
zlm-config.ini中配置:[rtc] port=8000同时需要配置DTLS证书。如果部署环境不满足WebRTC条件,默认使用WS-FLV。
建议:联调阶段先验证WS-FLV可用,再按需开启WebRTC。
5.2 管理端 (web.vite) 对接
在Vol.Pro管理端中,通过代码生成器生成设备/通道/录像的CRUD页面后,需要扩展以下功能。所有扩展均写在独立的扩展文件中,不修改代码生成器生成的文件:
Vol.Pro前端扩展机制:
- 自动生成的代码:
views/模块/表名.vue+views/模块/表名/options.js - 自定义业务代码:
extension/模块/表名.jsx或.vue(通过import extend from "@/extension/..."引入) - 代码生成器重新生成时,不会覆盖
extension/目录下的扩展文件
-
设备列表页扩展(写在
extension/warehouse/Base_Device.jsx):- 添加"同步设备"按钮(调用后端同步接口,仅对DeviceCategory=1显示)
- 添加"在线状态"实时显示
- 添加"查看通道"操作列
- 添加
DeviceCategory筛选条件(默认筛选视频监控设备)
-
通道列表页扩展(写在
extension/warehouse/Video_Channel.jsx):- 添加"实时预览"按钮(弹窗播放视频)
- 添加"云台控制"按钮(在弹窗中显示云台面板)
- 添加"回放"按钮(跳转回放页面)
-
新增视频播放页面(
views/video/VideoPlayer.vue,全新文件):- 使用Jessibuca播放器组件
- 支持协议切换(WebRTC/FLV/HLS)
- 集成云台控制面板
-
新增回放查询页面(
views/video/VideoPlayback.vue,全新文件):- 日期时间选择器
- 时间轴组件
- HLS播放器
扩展方式:在代码生成后的 .jsx 扩展文件中编写自定义逻辑,参考Vol.Pro文档的 view-grid 扩展方式。
5.3 用户端 (warehouse) 对接
warehouse已有 Live.vue、History.vue、VideoWall.vue,但均为占位符。需要:
-
Live.vue 改造:
- 替换硬编码数据为真实API调用
- 集成Jessibuca播放器替换"正在加载..."占位符
- 摄像头列表从
Video_ChannelAPI获取 - 点击摄像头获取流地址并播放
-
History.vue 改造:
- 集成日期范围选择查询录像列表
- 调用回放API获取HLS地址
- 使用HLS播放器播放
-
VideoWall.vue 改造(最复杂):
- 将随机图片替换为真实视频播放器实例
- 每个格子是一个独立的Jessibuca实例
- 布局切换时动态创建/销毁播放器
- 云台控制按钮调用Vol.Pro后端PTZ API
API调用示例(warehouse前端):
// 获取通道列表
import http from '@/api/http'
// 获取通道列表(Vol.Pro标准分页接口)
const getChannels = () => {
return http.post('/api/Video_Channel/GetPageData', {
page: 1,
rows: 100,
sort: 'CreateDate',
order: 'desc'
})
}
// 获取实时流地址
const getLiveUrl = (channelId: string, protocol = 'ws_flv') => {
return http.post('/api/VideoStream/GetLiveUrl', {
channelId,
protocol
})
}
// 云台控制
const ptzControl = (channelId: string, direction: string, speed = 0.5) => {
return http.post('/api/VideoStream/PtzControl', {
channelId,
action: 'continuous',
direction,
speed
})
}
// 获取回放地址
const getPlaybackUrl = (channelId: string, startTime: string, endTime: string) => {
return http.post('/api/VideoStream/GetPlaybackUrl', {
channelId,
startTime,
endTime
})
}
六、关键API接口文档
6.1 Vol.Pro后端对外接口
6.1.1 获取实时流地址
POST /api/VideoStream/GetLiveUrl
Authorization: Bearer <Vol.Pro JWT Token>
Request:
{
"channelId": "guid", // Vol.Pro通道ID
"protocol": "ws_flv" // 可选: webrtc / ws_flv / http_flv / hls / rtmp / rtsp
}
Response:
{
"status": true,
"message": "ok",
"data": {
"channelId": "guid",
"channelName": "仓库入口",
"protocol": "ws_flv",
"url": "ws://192.168.3.108/proxy/sms/rtp/gb_xxx.live.flv",
"backupUrls": {
"webrtc": "webrtc://192.168.3.108/proxy/sms/...",
"hls": "http://192.168.3.108/proxy/sms/..."
}
}
}
6.1.2 获取回放流地址
POST /api/VideoStream/GetPlaybackUrl
Authorization: Bearer <Vol.Pro JWT Token>
Request:
{
"channelId": "guid",
"startTime": "2026-04-28 08:00:00",
"endTime": "2026-04-28 18:00:00"
}
Response:
{
"status": true,
"message": "ok",
"data": {
"channelId": "guid",
"playlistUrl": "http://192.168.3.108/api/VideoStream/playback.m3u8?cid=xxx&start=...",
"recordCount": 12,
"totalDuration": 36000
}
}
6.1.3 云台控制
POST /api/VideoStream/PtzControl
Authorization: Bearer <Vol.Pro JWT Token>
Request:
{
"channelId": "guid",
"action": "continuous", // continuous / stop
"direction": "up", // up / down / left / right / zoom_in / zoom_out
"speed": 0.5 // 0.0 ~ 1.0
}
Response:
{
"status": true,
"message": "ok"
}
6.1.4 获取快照
GET /api/VideoStream/GetSnapshot?channelId=guid
Authorization: Bearer <Vol.Pro JWT Token>
Response: image/jpeg (二进制图片数据)
6.2 Owl内部接口(Vol.Pro后端调用,不暴露给前端)
| 接口 | 说明 | 用途 |
|---|---|---|
GET /devices |
设备列表 | 同步设备 |
GET /channels |
通道列表 | 同步通道 |
POST /channels/:id/play |
获取流地址 | 取流 |
POST /channels/:id/stop |
停止播放 | 停止 |
POST /channels/:id/ptz/control |
云台控制 | PTZ |
GET /recordings?cid=xxx |
录像查询 | 回放 |
GET /channels/:id/snapshot |
获取快照 | 截图 |
七、实施计划
所有代码均写在独立文件中,不修改任何框架自动生成的代码。框架升级时只需重新运行代码生成器,自定义业务完全保留。
阶段一:数据库与代码生成(2天)
- 执行建表SQL创建4张表(
Base_Device、Device_Video_Ext、Video_Channel、Video_Record) - 使用Vol.Pro代码生成器生成4个模块的CRUD代码(实体、Service、Controller、前端页面)
- 验证生成后的增删改查功能正常
- 验证:确认
Partial/和extension/目录未被覆盖
阶段二:Owl对接服务(3天)
新建文件(完全不触碰框架代码):
- 创建
Warehouse/Services/Owl/IOwlApiService.cs(接口) - 创建
Warehouse/Services/Owl/OwlApiService.cs(实现) - 实现JWT Token获取与缓存(内存/Redis)
- 实现设备/通道同步逻辑(调用Owl
/devices、/channels) - 实现取流、回放、云台、快照接口
- 创建
Warehouse/Controllers/VideoStreamController.cs(全新Controller) - 添加Quartz定时同步任务(写在
Partial中扩展或新建Job)
阶段三:管理端前端扩展(2天)
扩展文件(不修改生成的.vue/options.js):
- 引入Jessibuca播放器组件(npm install,全局注册)
- 创建
web.vite/src/extension/warehouse/Base_Device.jsx(扩展设备列表页,添加DeviceCategory=1筛选) - 创建
web.vite/src/extension/warehouse/Video_Channel.jsx(扩展通道列表页) - 创建
web.vite/src/views/video/VideoPlayer.vue(视频播放弹窗,全新文件) - 创建
web.vite/src/views/video/VideoPlayback.vue(回放查询页面,全新文件)
阶段四:warehouse用户端改造(3天)
修改warehouse自有代码(与Vol.Pro框架无关):
- 修改
warehouse/src/view/video/Live.vue:替换模拟数据为真实API调用,集成Jessibuca播放器 - 修改
warehouse/src/view/video/History.vue:实现回放查询和HLS播放 - 修改
warehouse/src/view/video/VideoWall.vue:将图片占位符替换为真实视频播放器实例 - 对接云台控制API(调用Vol.Pro后端的
/api/VideoStream/PtzControl)
阶段五:联调测试(2天)
- 设备注册到Owl后同步到Vol.Pro
- 实时播放测试(WebRTC/FLV)
- 回放功能测试
- 云台控制测试
- 多路视频墙测试
- 框架升级测试:重新运行代码生成器,验证所有自定义代码未被覆盖
总计预计工期: 15个工作日(含3天联调缓冲)
代码独立性检查清单
实施过程中每完成一个模块,对照以下清单确认无框架代码被修改:
| 检查项 | 通过标准 |
|---|---|
| Controller扩展 | 新增接口写在Partial/或全新Controller文件中 |
| Service扩展 | 业务逻辑写在Services/模块/Partial/中 |
| Entity扩展 | 新增字段写在DomainModels/模块/partial/中,加[NotMapped] |
| 前端扩展 | 自定义逻辑写在extension/目录下的.jsx或.vue中 |
| 独立服务 | OwlApiService等新建服务实现IDependency,不修改框架文件 |
| 配置修改 | 仅在appsettings.json中添加Owl相关配置,不修改框架其他配置 |
八、风险与注意事项
8.1 已知限制
- Owl云台控制不完整: 仅支持
continuous和stop,预置位/绝对位置未实现 - GB28181首次播放延迟: SIP信令协商需要1-3秒,首次播放有延迟
- WebRTC兼容性: 旧版浏览器不支持WebRTC,必须准备FLV备用方案
- HLS回放延迟: HLS切片默认2秒,端到端延迟约5-10秒,不适合实时监控
8.2 安全建议
- Owl的JWT Token(3天有效期)需要安全存储,建议用Redis缓存
- 播放地址应通过Vol.Pro后端代理,不要暴露Owl直连地址给前端
- ZLMediaKit的
secret在生产环境必须修改 - 云台控制接口需要权限校验(Vol.Pro的
[ApiActionPermission])
8.3 性能建议
- 视频墙多路播放时,建议限制同时播放路数(如最大16路)
- 使用
stop接口及时释放不再观看的流,减少服务器压力 - 快照图片可做CDN缓存
- 录像查询建议分页,避免大时间范围查询
附录A:统一设备管理与三维地图标记
A.1 统一设备管理设计
本方案与MC4.0采集网关方案共用 Base_Device 统一设备主表,实现视频监控设备和采集网关设备的统一管理:
| 设备大类 (DeviceCategory) | 细分类型 (DeviceType) | 扩展表 | 对接系统 |
|---|---|---|---|
| 1 = 视频监控 | 1=IPC, 2=NVR, 3=Platform | Device_Video_Ext |
Owl + ZLMediaKit |
| 2 = 采集网关 | 1=区域, 2=设备 | Device_IoT_Ext |
MC4.0 采集网关 |
原 Video_Device 表已拆分为:
Base_Device:公共字段(设备名称、IP、位置、经纬度、MapModelId等)Device_Video_Ext:视频特有字段(OwlDeviceId、Protocol、ChannelCount等)
A.2 三维地图标记 (VgoMap)
所有设备通过 Base_Device 表中的 MapModelId 字段,在 warehouse 前端的 VgoMap 三维地图上进行统一标记。
| 字段 | 类型 | 说明 |
|---|---|---|
| MapModelId | nvarchar(100) | 三维地图模型唯一标识 |
| MapModelScale | float | 模型缩放比例 |
| MapModelRotation | nvarchar(100) | 模型旋转角度(JSON) |
前端集成要点:
// 加载所有设备到三维地图
const devices = await http.post('/api/Base_Device/GetPageData', { ... });
devices.forEach(d => {
if (d.MapModelId) {
window.$map.setModelData(d.MapModelId, {
deviceId: d.DeviceId,
deviceName: d.DeviceName,
isOnline: d.IsOnline
});
window.$map.setModelColor(d.MapModelId,
d.IsOnline === 1 ? '#67c23a' : '#f56c6c');
}
});
详见《Vol.Pro_MC4.0_整合方案_v1.0.md》附录A.2完整说明。
文档结束 版本: v1.0 最后更新: 2026-04-29 状态: 待主人确认后实施