# Owl(GoWVP)开源视频监控管理平台 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": "" } ``` - 加密前内容示例:`{"username":"admin","password":"admin"}` - 公钥获取:`GET /login/key`,返回 Base64 编码的 RSA 公钥(PEM 格式) - **响应示例**: ```json { "token": "eyJhbGciOiJIUzI1NiIs...", "user": "admin" } ``` - **Token 有效期**: 3 天 - **使用方式**: 请求头携带 `Authorization: Bearer ` ### 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 定时保活(默认 10s),Owl 更新节点在线状态 | | `/webhook/on_publish` | POST | 推流鉴权(RTMP/RTSP/RTP),Owl 校验是否允许推流 | | `/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` | 重启 ZLM(rtc 端口变更时) | ### 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 后端架构设计参考。*