Initial_commit_SecMPS_v2

This commit is contained in:
2026-05-15 23:22:48 +08:00
commit 23ea4fe05f
13830 changed files with 298675 additions and 0 deletions

594
owl_api_research.md Normal file
View File

@@ -0,0 +1,594 @@
# OwlGoWVP开源视频监控管理平台 API 调研报告
> 调研目标:为 Vol.Pro 后端对接 Owl 提供技术方案依据
> 仓库地址https://github.com/gowvp/owl
> 调研日期2026-05-06
---
## 一、API 认证方式
Owl 采用 **JWT Token** 认证机制,所有业务 API除登录、健康检查、Webhook 回调外)均需在请求头中携带 Token。
### 1.1 登录获取 Token
- **URL**: `POST /login`
- **请求参数**:
```json
{
"data": "<RSA加密后的JSON字符串>"
}
```
- 加密前内容示例:`{"username":"admin","password":"admin"}`
- 公钥获取:`GET /login/key`,返回 Base64 编码的 RSA 公钥PEM 格式)
- **响应示例**:
```json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": "admin"
}
```
- **Token 有效期**: 3 天
- **使用方式**: 请求头携带 `Authorization: Bearer <token>`
### 1.2 修改凭据
- **URL**: `PUT /users`
- **说明**: 需先校验旧密码,同样使用 RSA 加密传输
---
## 二、设备管理 API
> 设备类型支持:`GB28181`、`ONVIF`、`RTMP`、`RTSP`
### 2.1 设备列表查询
- **URL**: `GET /devices`
- **认证**: 需 JWT
- **请求参数** (Query):
| 参数 | 类型 | 说明 |
|------|------|------|
| `page` | int | 页码,默认 1 |
| `size` | int | 每页大小 |
| `key` | string | 关键词模糊搜索(名称/国标编号) |
- **响应**:
```json
{
"items": [
{
"id": "gb_34020000001320000001",
"type": "GB28181",
"device_id": "34020000001320000001",
"name": "NVR-01",
"transport": "UDP",
"stream_mode": 1,
"ip": "192.168.1.100",
"port": 5060,
"is_online": true,
"registered_at": "2026-05-06T10:00:00Z",
"keepalive_at": "2026-05-06T10:05:00Z",
"keepalives": 30,
"expires": 3600,
"channels": 4,
"address": "192.168.1.100:5060",
"password": "",
"username": "",
"ext": {}
}
],
"total": 100
}
```
### 2.2 设备详情
- **URL**: `GET /devices/:id`
- **响应**: 单条 Device 对象(同上)
### 2.3 添加设备
- **URL**: `POST /devices`
- **认证**: 需 JWT
- **请求体**:
```json
// GB28181 设备
{
"type": "GB28181",
"device_id": "34020000001320000001",
"name": "摄像头1",
"password": "123456"
}
// ONVIF 设备
{
"type": "ONVIF",
"ip": "192.168.1.100",
"port": 80,
"username": "admin",
"password": "12345",
"name": "ONVIF摄像头"
}
```
- **说明**: GB28181 设备实际注册靠设备主动 SIP 注册,此接口用于预录入设备信息
### 2.4 修改设备
- **URL**: `PUT /devices/:id`
- **请求体** (`EditDeviceInput`):
| 字段 | 类型 | 说明 |
|------|------|------|
| `device_id` | string | 20 位国标编号 |
| `name` | string | 设备名称 |
| `password` | string | 注册密码 |
| `stream_mode` | int | 0:UDP, 1:TCP_PASSIVE, 2:TCP_ACTIVE |
| `username` | string | ONVIF 用户名 |
| `ip` | string | ONVIF IP |
| `port` | int | ONVIF 端口 |
### 2.5 删除设备
- **URL**: `DELETE /devices/:id`
### 2.6 查询设备目录(刷新通道)
- **URL**: `POST /devices/:id/catalog`
- **说明**: 向 GB28181 设备发送 Catalog 查询指令,拉取通道列表
- **响应**: `{"msg": "ok"}`(异步执行)
### 2.7 ONVIF 设备发现
- **URL**: `GET /onvif/discover`
- **说明**: SSE 流式返回发现的 ONVIF 设备
---
## 三、通道管理 API
### 3.1 通道列表查询
- **URL**: `GET /channels`
- **认证**: 需 JWT
- **请求参数** (Query):
| 参数 | 类型 | 说明 |
|------|------|------|
| `page` | int | 页码 |
| `size` | int | 每页大小 |
| `did` | string | 父设备 ID |
| `device_id` | string | 国标编码 |
| `key` | string | 名称/国标编码模糊搜索ID 精确搜索 |
| `is_online` | string | 是否在线过滤 |
| `type` | string | 通道类型GB28181/ONVIF/RTMP/RTSP |
| `app` | string | 应用名 |
| `stream` | string | 流 ID |
- **响应**:
```json
{
"items": [
{
"id": "gb_34020000001320000001_34020000001320000001",
"did": "gb_34020000001320000001",
"device_id": "34020000001320000001",
"channel_id": "34020000001320000001",
"name": "通道01",
"ptztype": 1,
"is_online": true,
"is_playing": false,
"type": "GB28181",
"app": "rtp",
"stream": "gb_34020000001320000001_34020000001320000001",
"has_recording": true,
"config": {}
}
],
"total": 100
}
```
### 3.2 设备与通道关联列表
- **URL**: `GET /devices/channels`
- **说明**: 返回设备列表,每个设备包含 `children`(通道数组),并按在线状态排序
- **响应**:
```json
{
"items": [
{
"id": "gb_xxx",
"name": "NVR-01",
"is_online": true,
"children": [
{
"id": "gb_xxx_xxx",
"name": "通道01",
"is_online": true,
"has_recording": true
}
]
}
],
"total": 10
}
```
### 3.3 添加通道RTMP/RTSP
- **URL**: `POST /channels`
- **请求体** (`AddChannelInput`):
```json
{
"type": "RTMP",
"name": "推流通道1",
"device_id": "",
"device_name": "",
"app": "live",
"stream": "stream01",
"config": {
"is_auth_disabled": false,
"session": "",
"media_server_id": "",
"push_addr": ""
}
}
```
### 3.4 修改通道
- **URL**: `PUT /channels/:id`
- **请求体** (`EditChannelInput`):
| 字段 | 类型 | 说明 |
|------|------|------|
| `device_id` | string | 国标编码 |
| `name` | string | 通道名称 |
| `ptztype` | int | 云台类型 |
| `is_online` | bool | 是否在线 |
| `ext` | object | 扩展属性 |
| `app` | string | 应用名RTMP/RTSP |
| `stream` | string | 流 ID |
| `config` | StreamConfig | 流配置 |
### 3.5 删除通道
- **URL**: `DELETE /channels/:id`
- **限制**: 仅允许删除 RTMP/RTSP 类型通道
---
## 四、实时流地址获取 API
### 4.1 获取播放地址
- **URL**: `POST /channels/:id/play`
- **认证**: 需 JWT
- **说明**: 根据通道类型自动处理拉流逻辑
- **GB28181**: Owl 通过 SIP 邀请设备推流到 ZLM自动开启 RTP Server
- **RTMP**: 检查是否已推流,直接从 ZLM 获取播放地址
- **RTSP**: 通过 ZLM 添加流代理AddStreamProxy再返回地址
- **ONVIF**: 直接从 ZLM 获取播放地址
- **响应** (`playOutput`):
```json
{
"app": "rtp",
"stream": "gb_34020000001320000001_34020000001320000001",
"items": [
{
"label": "ZLM",
"ws_flv": "ws://host:port/proxy/sms/rtp/gb_xxx.live.flv",
"http_flv": "http://host:port/proxy/sms/rtp/gb_xxx.live.flv",
"rtmp": "rtmp://host:port/rtp/gb_xxx",
"rtsp": "rtsp://host:port/rtp/gb_xxx",
"webrtc": "webrtc://host:port/proxy/sms/index/api/webrtc?app=rtp&stream=gb_xxx&type=play",
"hls": "http://host:port/proxy/sms/rtp/gb_xxx/hls.fmp4.m3u8"
}
]
}
```
### 4.2 停止播放
- **URL**: `POST /channels/:id/stop`
- **说明**: 幂等操作,始终返回成功
- GB28181: 发送 SIP BYE + 关闭 ZLM RTP Server
- ONVIF/RTSP/RTMP: 调用 ZLM `close_streams` 关闭流
- **响应**: `{"msg": "ok"}`
### 4.3 快照相关
- **刷新快照**: `POST /channels/:id/snapshot`
- 请求体可指定 `within_seconds`(秒内有效则返回缓存)和 `url`(指定取流地址)
- 响应: `{"link": "http://host/channels/:id/snapshot?token=xxx"}`
- **获取快照图片**: `GET /channels/:id/snapshot?token=xxx`
- 直接返回 JPEG 图片流
---
## 五、回放流地址获取 API云录像
Owl 的录像回放功能由 Owl 自身管理(非设备 SD 卡录像),底层依赖 ZLM 的 MP4 录制。
### 5.1 查询录像列表
- **URL**: `GET /recordings`
- **认证**: 需 JWT
- **请求参数** (Query):
| 参数 | 类型 | 说明 |
|------|------|------|
| `page` | int | 页码 |
| `size` | int | 每页大小 |
| `cid` | string | 通道 ID |
| `app` | string | 应用名 |
| `stream` | string | 流 ID |
| `start_ms` | int64 | 开始时间戳(毫秒) |
| `end_ms` | int64 | 结束时间戳(毫秒) |
- **响应**:
```json
{
"items": [
{
"id": 1,
"cid": "gb_xxx_xxx",
"app": "rtp",
"stream": "gb_xxx_xxx",
"started_at": "2026-05-06T10:00:00Z",
"ended_at": "2026-05-06T10:05:00Z",
"duration": 300.5,
"path": "/recordings/rtp/gb_xxx_xxx/20260506/xxx.mp4",
"size": 10485760
}
],
"total": 50
}
```
### 5.2 获取时间轴
- **URL**: `GET /recordings/timeline?cid=xxx&start_ms=xxx&end_ms=xxx`
- **响应**:
```json
{
"items": [
{
"id": 1,
"start_ms": 1714982400000,
"end_ms": 1714982700000,
"duration": 300.5
}
]
}
```
### 5.3 获取月度统计
- **URL**: `GET /recordings/monthly?cid=xxx&year=2026&month=5`
- **响应**:
```json
{
"year": 2026,
"month": 5,
"days": 31,
"has_video": "101010..." // 每天是否有录像,'1'有/'0'无
}
```
### 5.4 HLS 回放播放
- **URL**: `GET /recordings/channels/:cid/index.m3u8?start_ms=xxx&end_ms=xxx&token=xxx`
- **说明**: 动态生成 m3u8 播放列表,将指定时间范围内的多个 MP4 片段拼接为 HLS (VOD)
- **Content-Type**: `application/vnd.apple.mpegurl`
### 5.5 下载录像
- **URL**: `GET /recordings/:id/download`
- **说明**: 返回 MP4 文件流,支持 HTTP Range 请求(边下载边播放)
### 5.6 静态文件播放
- **URL**: `GET /static/recordings/{path}?token=xxx`
- **说明**: 直接访问 MP4/fMP4 文件,同样支持 Range 请求
---
## 六、云台控制 API
### 6.1 PTZ 控制
- **URL**: `POST /channels/:id/ptz/control`
- **认证**: 需 JWT
- **请求体** (`ptzControlInput`):
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `action` | string | 是 | 动作类型:`continuous` / `stop` / `absolute` / `relative` / `preset` |
| `direction` | string | 否 | 方向continuous 时用):`up` / `down` / `left` / `right` / `zoom_in` / `zoom_out` |
| `speed` | float64 | 否 | 速度 0-1 |
| `x` | float64 | 否 | X 轴位置absolute/relative |
| `y` | float64 | 否 | Y 轴位置 |
| `zoom` | float64 | 否 | 缩放值 |
| `preset_id` | string | 否 | 预置位 ID |
| `preset_op` | string | 否 | 预置位操作:`goto` / `set` / `remove` |
- **当前实现限制**: 代码中**仅实现了 `continuous` 和 `stop`**,其他动作类型会返回错误
- **响应**:
```json
{
"msg": "PTZ 控制指令已发送"
}
```
- **注意**: 目前代码中直接调用 GB28181 Server 的 PTZControl 方法未区分协议类型ONVIF PTZ 支持标记为 TODO
---
## 七、设备状态查询 API
### 7.1 在线状态
Owl 没有单独的"设备状态查询"接口,设备状态包含在设备/通道列表查询中:
- **设备在线状态**: `GET /devices` 或 `GET /devices/:id` → `is_online`
- **通道在线状态**: `GET /channels` 或 `GET /devices/channels` → `is_online`
- **通道播放状态**: `is_playing`
### 7.2 设备模型关键状态字段
| 字段 | 说明 |
|------|------|
| `is_online` | 是否在线SIP 注册成功且心跳正常) |
| `registered_at` | 最近一次注册时间 |
| `keepalive_at` | 最近一次心跳时间 |
| `keepalives` | 心跳间隔(秒) |
| `expires` | 注册有效期(秒) |
| `address` | 设备网络地址(如 `192.168.1.100:5060` |
| `transport` | 信令传输协议(`UDP` / `TCP` |
| `channels` | 通道数量 |
### 7.3 注册与心跳机制SIP 层)
- **注册**: GB28181 设备主动向 Owl 的 SIP 端口(默认 5060发送 REGISTER 请求
- Owl 支持 Digest 鉴权,密码可设备单独配置或使用全局默认密码
- 注册成功后 Owl 自动查询 DeviceInfo 和 Catalog
- **心跳**: 设备按 `Keepalives` 间隔发送 MESSAGE 心跳
- Owl 通过 `keepalive_at` 和 `expires` 判断设备是否离线
- 例如:设备侧超时 3 秒 × 3 次,则约 9+x 秒收不到心跳认为离线
- **注销**: 设备发送 Expires=0 的 REGISTER 请求
> **重要**: 注册和心跳是 **SIP 协议层**自动完成的,没有对应的 HTTP API。Vol.Pro 只需通过 HTTP API 查询设备列表即可获知在线状态。
---
## 八、Owl 与 ZLMediaKit 联动机制
### 8.1 架构关系
```
摄像机/NVR --(SIP/RTP)--> Owl --(HTTP API/Webhook)--> ZLMediaKit
^
| (反向代理播放)
前端播放器 --(HTTP/WebSocket)---> Owl /proxy/sms/xxx
```
### 8.2 Owl 配置 ZLM 的 Hook 回调
Owl 启动时会自动连接 ZLM并通过 `SetServerConfig` 接口设置以下 Webhook 地址(前缀为 `http://{owl_host}:{owl_port}/webhook`
| Hook 接口 | 方法 | 说明 |
|-----------|------|------|
| `/webhook/on_server_started` | POST | ZLM 启动时触发Owl 将所有 RTMP 通道置为离线 |
| `/webhook/on_server_keepalive` | POST | ZLM 定时保活(默认 10sOwl 更新节点在线状态 |
| `/webhook/on_publish` | POST | 推流鉴权RTMP/RTSP/RTPOwl 校验是否允许推流 |
| `/webhook/on_play` | POST | 播放鉴权Owl 更新通道播放状态为 playing |
| `/webhook/on_stream_changed` | POST | 流注册/注销时触发Owl 根据录像模式决定是否启动录制 |
| `/webhook/on_stream_none_reader` | POST | 无人观看时触发Owl 根据录像模式决定是否关闭流 |
| `/webhook/on_stream_not_found` | POST | 播放不存在的流时触发Owl 触发按需拉流ONVIF/RTSP |
| `/webhook/on_rtp_server_timeout` | POST | RTP Server 超时未收到数据 |
| `/webhook/on_record_mp4` | POST | MP4 切片录制完成Owl 将录像信息入库 |
### 8.3 Owl 调用 ZLM 的 API
通过 `internal/core/sms/driver_zlm.go` 封装调用:
| ZLM API | Owl 封装方法 | 说明 |
|---------|-------------|------|
| `openRtpServer` | `OpenRTPServer` | 开启 RTP 收流端口GB28181 用) |
| `closeRtpServer` | `CloseRTPServer` | 关闭 RTP 端口 |
| `close_streams` | `CloseStreams` | 强制关闭流 |
| `addStreamProxy` | `AddStreamProxy` | 添加 RTSP 拉流代理 |
| `getSnap` | `GetSnapshot` | 获取流截图 |
| `startRecord` | `StartRecord` | 开始 MP4 录制 |
| `stopRecord` | `StopRecord` | 停止 MP4 录制 |
| `getServerConfig` | `GetServerConfig` | 获取配置(含端口信息) |
| `setServerConfig` | `SetServerConfig` | 设置配置(含 Hook 地址) |
| `restartServer` | `RestartServer` | 重启 ZLMrtc 端口变更时) |
### 8.4 播放地址生成规则
Owl 通过反向代理 `/proxy/sms/*path` 将所有播放请求转发到 ZLM前端无需直接访问 ZLM。
生成逻辑(`GetStreamLiveAddr`
| 协议 | URL 格式示例 |
|------|-------------|
| **WS-FLV** | `ws://owl_host/proxy/sms/{app}/{stream}.live.flv` |
| **HTTP-FLV** | `http://owl_host/proxy/sms/{app}/{stream}.live.flv` |
| **HLS(fMP4)** | `http://owl_host/proxy/sms/{app}/{stream}/hls.fmp4.m3u8` |
| **WebRTC** | `webrtc://owl_host/proxy/sms/index/api/webrtc?app={app}&stream={stream}&type=play` |
| **RTMP** | `rtmp://owl_host:1935/{app}/{stream}` |
| **RTSP** | `rtsp://owl_host:554/{app}/{stream}` |
### 8.5 联动流程示例GB28181 实时播放)
1. 前端调用 `POST /channels/:id/play`
2. Owl 向 ZLM 调用 `openRtpServer` 开启 UDP/TCP 收流端口
3. Owl 通过 SIP 发送 INVITE 给设备SDP 中携带 ZLM 的收流地址
4. 设备通过 RTP 向 ZLM 推流
5. ZLM 触发 `on_publish` → `on_stream_changed` Hook
6. Owl 返回包含播放地址的 JSON 给前端
7. 前端通过 `/proxy/sms/...` 播放流
8. 无人观看时ZLM 触发 `on_stream_none_reader`
9. Owl 判断无需录像 → 返回 `Close: true` → ZLM 关闭流
10. Owl 发送 SIP BYE 结束会话
---
## 九、其他相关 API
### 9.1 流媒体服务器管理
- `GET /media_servers` - 查询媒体服务器列表
- `PUT /media_servers/:id` - 修改媒体服务器配置
### 9.2 推流管理
- `GET /stream_pushs` - 查询推流列表(含 RTMP 推流地址)
- `POST /stream_pushs` - 添加推流
- `PUT /stream_pushs/:id` - 修改推流
- `DELETE /stream_pushs/:id` - 删除推流
### 9.3 事件/告警管理
- `GET /events` - 分页查询 AI 检测事件
- `GET /events/:id` - 事件详情
- `GET /events/image/*path` - 获取事件快照图片(无需认证)
### 9.4 配置管理
- `GET /configs/info` - 获取 SIP 等配置信息
- `PUT /configs/info/sip` - 修改 SIP 配置
### 9.5 系统接口
- `GET /health` - 健康检查
- `GET /app/metrics/api` - API 实时监控指标
- `GET /app/version/check` - 检查新版本
---
## 十、Vol.Pro 对接建议
### 10.1 对接方式
Vol.Pro 后端直接通过 **HTTP REST API** 调用 Owl无需处理 SIP 协议。
### 10.2 关键对接流程
```
1. 登录 Owl → 获取 JWT Token缓存
2. 定时同步设备列表 → GET /devices
3. 定时同步通道列表 → GET /channels 或 GET /devices/channels
4. 用户点击播放 → POST /channels/:id/play → 获取播放地址 → 前端播放
5. 用户停止播放 → POST /channels/:id/stop
6. 回放查询 → GET /recordings?cid=xxx&start_ms=xxx&end_ms=xxx
7. 回放播放 → GET /recordings/channels/:cid/index.m3u8?start_ms=xxx&end_ms=xxx
8. 云台控制 → POST /channels/:id/ptz/control
```
### 10.3 注意事项
1. **Token 管理**: JWT 有效期 3 天,建议在 Vol.Pro 中缓存并处理过期刷新
2. **播放地址代理**: 前端播放地址建议走 Owl 的 `/proxy/sms/...` 路径,无需暴露 ZLM 直接地址
3. **按需拉流**: GB28181 实时播放是按需拉流,首次播放可能有 1-3 秒延迟SIP 信令 + ZLM 准备)
4. **录像模式**: 通道支持 `always` / `ai` / `none` 三种录像模式,通过 `POST /channels/:id/record_mode` 设置
5. **PTZ 限制**: 当前 Owl 仅支持 `continuous`(持续移动)和 `stop`(停止),预置位等功能未完整实现
6. **心跳与注册**: 不要尝试通过 HTTP 模拟设备注册,这是 SIP 层自动完成的
7. **ONVIF PTZ**: README 中标记为"未支持"`[ ] ONVIF PTZ control support`
---
*报告整理完毕,供 Vol.Pro 后端架构设计参考。*