在 com.inspect.nvr.hik 下新增海康 NVR 历史录像查询接口。调用方传入 NVR 登录信息、通道、码流类型以及北京时间范围,服务端通过海康网络 SDK 查询该范围内实际存在的录像,返回实际连续录像区间及每个区间对应的 RTSP 回放地址。
本功能只查询录像并生成回放地址,不代理、转码或下载视频流。
POST/hik/nvr/playback/searchapplication/json海康NVR回放{
"nvrIp": "192.168.1.100",
"sdkPort": 8000,
"rtspPort": 554,
"channel": 1,
"streamType": 1,
"username": "admin",
"password": "123456",
"startTime": "2026-07-13 12:00:00",
"endTime": "2026-07-13 13:00:00"
}
字段规则:
nvrIp、username、password 必填。sdkPort 范围为 1~65535,用于 SDK 登录,通常为 8000。rtspPort 范围为 1~65535,通常为 554。channel 必须大于 0,使用海康 NVR 实际 SDK 通道号。streamType 取值 1 或 2;1 表示主码流,2 表示子码流。startTime、endTime 格式固定为 yyyy-MM-dd HH:mm:ss,语义固定为 Asia/Shanghai。endTime 必须晚于 startTime。有录像:
{
"code": 0,
"message": "success",
"hasRecord": true,
"records": [
{
"timeRange": [
"20260713123000",
"20260713130000"
],
"rtspUrl": "rtsp://admin:123456@192.168.1.100:554/Streaming/tracks/101?starttime=20260713T043000Z&endtime=20260713T050000Z"
}
]
}
无录像:
{
"code": 0,
"message": "success",
"hasRecord": false,
"records": []
}
timeRange 始终包含两个北京时间字符串,顺序为 [开始时间, 结束时间],格式固定为 yyyyMMddHHmmss。
代码全部位于 src/main/java/com/inspect/nvr/hik:
controller/HikNvrPlaybackController:接收 HTTP 请求、调用服务并返回响应。domain/HikNvrPlaybackRequest:请求模型和 Swagger 字段说明;密码标记为只写字段。domain/HikNvrPlaybackResponse:查询状态、是否存在录像和录像区间列表。domain/HikNvrPlaybackRecord:单个连续录像区间及 RTSP 地址。service/HikNvrPlaybackService:参数校验、SDK 查询、区间裁剪与合并、SDK 通道到 RTSP 通道的衔接。service/HikPlaybackUrlBuilder:使用已解析的 RTSP 通道号和设备本地北京时间生成回放 URL。com.inspect.nvr.service.HikLoginSession:保存登录句柄及设备返回的模拟/数字通道布局,并提供 SDK 通道到 RTSP 通道的映射。exception/HikNvrPlaybackException:携带 SDK 错误码或参数错误信息。controller/HikNvrPlaybackExceptionHandler:将参数错误转换为 HTTP 400,将 NVR 登录或查询错误转换为 HTTP 502。服务复用项目现有 HikLoginService 管理海康登录句柄,不重复创建独立登录缓存。登录缓存的值从单独的用户句柄扩展为 HikLoginSession,缓存时长、IP 键和登出策略保持不变。
NvrInfo,通过 HikLoginService.loginSession 获取复用的登录句柄和设备通道布局。NET_DVR_FILECOND_V40:
lChannel 使用请求通道号。dwFileType=0xff,查询所有录像类型。dwIsLocked=0xff,查询锁定和未锁定录像。byFindType=0,查询普通录像卷。byQuickSearch=0,获取完整文件信息。byStreamType=3,子码流请求映射为 SDK byStreamType=1。NET_DVR_TIME。NET_DVR_FindFile_V40 获取查询句柄;返回负数时读取 NET_DVR_GetLastError 并报错。NET_DVR_FindNextFile_V40:
NET_DVR_FILE_SUCCESS:读取录像文件起止时间。NET_DVR_ISFINDING:短暂等待后继续,整体设置有限等待次数,避免请求无限阻塞。NET_DVR_FILE_NOFIND 或 NET_DVR_NOMOREFILE:正常结束查询。NET_DVR_FILE_EXCEPTION 或 -1:读取 SDK 错误码并报错。finally 中调用 NET_DVR_FindClose_V30 关闭查询句柄。yyyyMMddHHmmss。yyyyMMdd'T'HHmmss'Z'。目标 NVR 固件实测把参数数字按设备本地时间解释;将北京时间转换为 UTC 会建立会话但收不到录像数据。例如用户查询 12:00:00~13:00:00,NVR 只存在 12:30:00~13:00:00,返回:
"timeRange": ["20260713123000", "20260713130000"]
录像查询继续使用 SDK 通道号。RTSP 轨道使用设备展示通道序号,两者不能直接混用。登录返回的 NET_DVR_DEVICEINFO_V30 提供模拟通道数、模拟起始通道、数字通道数和数字起始通道,映射规则为:
数字通道:rtspChannel = analogChannelCount + (sdkChannel - digitalStartChannel) + 1
模拟通道:rtspChannel = (sdkChannel - analogStartChannel) + 1
trackId = rtspChannel * 100 + streamType
例如纯 NVR 的模拟通道数为 0、数字起始通道为 33,则 SDK 通道 33 映射为 RTSP 通道 1,主码流轨道为 101。如果设备通道元数据无法覆盖请求通道,则保留原通道号作为兼容回退。
地址格式:
rtsp://<编码后的用户名>:<编码后的密码>@<NVR地址>:<RTSP端口>/Streaming/tracks/<trackId>?starttime=<北京时间开始>&endtime=<北京时间结束>
账号和密码按照 URL user-info 规则进行百分号编码。服务端日志禁止输出完整请求对象、密码或最终 RTSP URL。
hasRecord=false,records=[]。测试先于生产代码编写,覆盖: