# hkCameraServer **Repository Path**: wujialong1212_admin/hk-camera-server ## Basic Information - **Project Name**: hkCameraServer - **Description**: 海康威视 ISUP 5.0 摄像头接入与直播流服务器。 设备通过 Hikvision ISUP 5.0 协议注册到本服务器,服务器将视频流转推至 ZLMediaKit,对外提供 HTTP REST API 用于设备管理、实时预览和云台控制 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-03-29 - **Last Updated**: 2026-07-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # hkCameraServer 海康威视 ISUP 5.0 摄像头接入与直播流服务器。 设备通过 Hikvision ISUP 5.0 协议注册到本服务器,服务器将视频流转推至 ZLMediaKit,对外提供 HTTP REST API 用于设备管理、实时预览和云台控制。 ## 系统架构 ``` 摄像头/NVR │ ISUP 5.0 (TCP:7660) ▼ hkCameraServer (本服务) ├─ CMS 接入层 设备注册、鉴权、心跳 ├─ Stream 接入层 视频流接收 (TCP:7661) ├─ Redis 设备在线状态持久化 ├─ RTMP 推流 fork ffmpeg → ZLMediaKit (TCP:1935) └─ HTTP API REST 接口 (TCP:8080) ZLMediaKit (Docker) ├─ RTMP 接收 :1935 └─ HTTP-FLV 分发 :8088 Redis (本地) └─ localhost:6379 ``` ## 依赖 | 依赖 | 版本 | 说明 | |------|------|------| | CMake | ≥ 3.16 | 构建系统 | | GCC / Clang | C++17 | 编译器 | | Hikvision ISUP SDK | 内置 | `third_party/isupsdk/` | | OpenSSL | 1.0.x | SDK 要求(随 SDK 附带) | | cpp-httplib | v0.18.3 | HTTP 服务(FetchContent) | | nlohmann/json | v3.11.3 | JSON 解析(FetchContent) | | hiredis | v1.2.0 | Redis C 客户端(FetchContent) | | ffmpeg | 系统安装 | RTMP 推流 | | Docker | - | 运行 ZLMediaKit | | Redis | 7.x | 设备状态存储,本地服务 | ## 快速开始 ### 1. 启动基础设施 ```bash # 启动 ZLMediaKit sudo docker compose up -d # 确认 Redis 在运行 redis-cli ping # → PONG ``` ### 2. 编译 ```bash cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel 4 # 产物: bin/hkServer ``` ### 3. 配置 编辑项目根目录的 `config.json`: ```json { "cms": { "listen_ip": "0.0.0.0", "listen_port": 7660, "server_ip": "YOUR_PUBLIC_IP", "ehome_key": "YOUR_EHOME_KEY" }, "stream": { "listen_port": 7661 }, "http": { "port": 8080, "thread_pool_size": 8 }, "redis": { "host": "localhost", "port": 6379, "db": 0 }, "rtmp": { "server_url": "rtmp://localhost:1935" }, "zlm": { "http_base_url": "http://localhost:8088" }, "sdk": { "openssl_lib_path": "third_party/isupsdk/lib", "log_dir": "./logs", "log_level": 3 } } ``` - `cms.server_ip`:服务器公网 IP,设备会用它建立数据连接 - `cms.ehome_key`:与摄像头配置的 EHome 密钥一致 ### 4. 运行 ```bash ./run_server.sh # 前台运行(Ctrl-C 退出) ./run_server.sh start # 后台启动,日志写入 logs/hkServer.log ./run_server.sh stop # 停止 ./run_server.sh restart # 重启 ./run_server.sh status # 查看进程状态 ./run_server.sh logs # 实时跟踪日志(tail -f) ``` 启动脚本会清理 `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` 等进程代理变量,并为本机和内网地址设置 `NO_PROXY`,避免本机 Clash 系统代理影响摄像头、Redis、ZLMediaKit 等生产链路。 ## HTTP API 基础 URL:`http://localhost:8080` 所有响应格式: ```json { "code": 0, "msg": "ok", "data": { ... } } ``` 错误时 `code` 为 `-1`,`msg` 为错误描述,HTTP 状态码对应 4xx/5xx。 --- ### 设备 #### 获取在线设备列表 ``` GET /api/devices ``` 响应 `data`: ```json [ { "device_id": "FZ6526054", "serial_number": "DS-2DE4423DW-D/GL...", "device_name": "IPCamera", "ip_address": "39.144.xxx.xxx", "port": 7660, "firmware_version": 0 } ] ``` --- #### 开始预览(拉流并推送至 ZLMediaKit) ``` POST /api/devices/{device_id}/preview/start Content-Type: application/json { "channel": 1, "stream_type": 0, "link_mode": 0 } ``` | 字段 | 说明 | 默认 | |------|------|------| | `channel` | 通道号 | 1 | | `stream_type` | 0=主码流 1=子码流 | 0 | | `link_mode` | 0=TCP 1=UDP | 0 | 响应 `data`: ```json { "session_id": 1, "source_id": "src_1", "client_count": 1, "stream_key": "DS-2DE4423DW-D..._ch1_st0_lm0", "stream_url": "http://localhost:8088/live/DS-2DE4423DW-D..._ch1_st0_lm0.live.flv" } ``` `session_id` 是前端客户端会话 ID;同一摄像头、通道、码流和链路模式的多个客户端会共享同一个 `source_id`、`stream_key` 和 `stream_url`,不会互相顶掉底层 SDK 预览。`client_count` 表示当前共享该源的客户端数量。`stream_url` 可直接在 VLC / flv.js / mpegts.js 中播放。 --- #### 停止预览 ``` POST /api/devices/{device_id}/preview/stop Content-Type: application/json { "session_id": 1 } ``` --- #### 截图(返回 PNG) ``` POST /api/devices/{device_id}/snapshot Content-Type: application/json { "channel": 1, "stream_type": 0, "link_mode": 0, "timeout_sec": 10 } ``` 接口会临时启动一路 SDK 预览,从视频流中抽取一帧并转为 PNG,成功时响应体为 `image/png` 二进制,不包 JSON。 | 字段 | 说明 | 默认 | |------|------|------| | `channel` | 通道号 | 1 | | `stream_type` | 0=主码流 1=子码流 2=第三码流 | 0 | | `link_mode` | 0=TCP 1=UDP 2/8=设备支持的其他链路模式 | 0 | | `timeout_sec` | 等待截图超时时间,范围 1-30 秒 | 10 | --- ### 云台控制(PTZ) #### 开始转动 ``` POST /api/devices/{device_id}/ptz/move Content-Type: application/json { "direction": "up", "speed": 50, "channel": 1 } ``` `direction` 可选值:`up` `down` `left` `right` `up_left` `up_right` `down_left` `down_right` `speed`:1–100 #### 停止转动 ``` POST /api/devices/{device_id}/ptz/stop Content-Type: application/json { "channel": 1 } ``` #### 变焦 / 对焦 ``` POST /api/devices/{device_id}/ptz/lens Content-Type: application/json { "action": "zoom_in", "speed": 50, "channel": 1 } ``` `action` 可选值:`zoom_in` `zoom_out` `focus_near` `focus_far` #### 停止变焦 / 对焦 ``` POST /api/devices/{device_id}/ptz/lens/stop Content-Type: application/json { "action": "zoom_in", "channel": 1 } ``` #### 预置点操作 ``` POST /api/devices/{device_id}/ptz/preset Content-Type: application/json { "action": "goto", "index": 1, "channel": 1 } ``` `action` 可选值:`set`(保存)`goto`(调用)`clear`(删除),`index` 范围 1–255。 #### 查询当前 PTZ 值 ``` GET /api/devices/{device_id}/ptz/position?channel=1 ``` 响应 `data`: ```json { "channel": 1, "precision": "high", "source": "absoluteEx", "elevation": 12.345, "azimuth": 123.456, "absolute_zoom": 42.5, "focus": 100, "zoom_type": "absoluteZoom" } ``` 服务端优先通过 `/ISAPI/PTZCtrl/channels/{channel}/absoluteEx` 查询高精度 PTZ 值;设备不支持时降级到 `/status` 的 `AbsoluteHighEx` 或 `AbsoluteHigh`。`precision` 为 `high` 或 `standard`,`source` 表示实际采用的 ISAPI 端点。 --- ### 会话 #### 获取所有活跃流会话 ``` GET /api/sessions ``` 响应 `data`: ```json [ { "session_id": 1, "source_id": "src_1", "device_id": "FZ6526054", "channel": 1, "stream_type": 0, "link_mode": 0, "stream_key": "DS-2DE4423DW-D..._ch1_st0_lm0", "client_count": 2, "running": true, "bytes_fed": 10485760, "uptime_sec": 42, "client_uptime_sec": 30, "last_data_sec": 0 } ] ``` 列表按前端客户端会话返回;多个 `session_id` 可能指向同一个 `source_id`,表示它们复用同一路底层 SDK/RTMP 源。 --- ### ZLMediaKit Webhook(内部) ``` POST /api/hooks/on_stream_none_reader ``` 当 ZLMediaKit 检测到某路流已无人观看时回调,服务器按 `stream_key` 回收对应共享源,清理该源下所有残留客户端会话,并停止对应的 SDK 预览和 ffmpeg 进程。在 `zlmediakit/config.ini` 中已配置。 ## 代码结构 ``` hkCameraServer/ ├── config.json 运行时配置 ├── docker-compose.yml ZLMediaKit 容器 ├── zlmediakit/ │ └── config.ini ZLMediaKit 配置(Webhook、API secret) ├── run_server.sh 一键启停脚本 │ ├── server/ 生产服务器(与 SDK 解耦) │ ├── include/ │ │ ├── config.hpp 配置加载(JSON → ServerConfig) │ │ ├── logger.hpp 线程安全日志宏(LOG_INFO/WARN/ERROR) │ │ ├── rtmp_bridge.hpp ffmpeg RTMP 推流进程管理 │ │ ├── redis_client.hpp Redis RAII 封装 │ │ ├── session_manager.hpp 流会话生命周期管理 │ │ ├── device_service.hpp CMS 事件 → Redis 状态桥接 │ │ ├── api_service.hpp 业务逻辑门面 │ │ ├── http_server.hpp HTTP 传输层(仅依赖 ApiService) │ │ ├── application.hpp 顶层组件编排 │ │ └── schemas/ │ │ ├── common.hpp ApiError、ApiResponse │ │ ├── device.hpp DeviceDTO、PreviewStartRequest/Response │ │ ├── ptz.hpp PtzMoveRequest 等 │ │ └── session.hpp SessionDTO、WebhookNoneReaderRequest/Response │ └── src/ │ ├── rtmp_bridge.cpp │ ├── redis_client.cpp │ ├── session_manager.cpp │ ├── device_service.cpp │ ├── api_service.cpp │ ├── http_server.cpp │ ├── application.cpp │ └── main.cpp │ ├── src/hk/ SDK C++ 封装库(hkcamera 静态库) │ ├── cms/ CMS 设备管理 │ └── stream/ 视频流接收 ├── include/hk/ 公共头文件 ├── third_party/isupsdk/ Hikvision ISUP SDK(.so 不入 git) │ ├── demo/ 轻量演示服务(live_demo,勿改动) └── examples/ simple 示例 ``` ## 端口说明 | 端口 | 协议 | 用途 | |------|------|------| | 7660 | TCP | ISUP CMS,摄像头注册 | | 7661 | TCP | 视频流接收 | | 8080 | HTTP | REST API | | 1935 | TCP | ZLMediaKit RTMP 接收(Docker) | | 8088 | HTTP | ZLMediaKit HTTP-FLV 分发(Docker) | | 6379 | TCP | Redis | ## 防火墙 需要对外开放: ```bash # 摄像头注册和数据连接 ufw allow 7660/tcp ufw allow 7661/tcp # HTTP API(可按需限制来源) ufw allow 8080/tcp # ZLMediaKit 直播分发(可按需限制) ufw allow 8088/tcp ``` ## Redis 键说明 | 键 | 类型 | 内容 | |----|------|------| | `hk:discovered:{sn}` | String (JSON) | 设备首次发现信息(永久) | | `hk:online:{sn}` | String | device_id,TTL=300s,心跳续期 |