|
|
package com.inspect.nvr.hik.controller;
|
|
|
|
|
|
import com.inspect.nvr.dahua.DahuaIvsVendorService;
|
|
|
import com.inspect.nvr.domain.Infrared.NvrInfo;
|
|
|
import com.inspect.nvr.hik.domain.HikNvrLiveStreamResponse;
|
|
|
import com.inspect.nvr.hik.domain.HikNvrPlaybackErrorResponse;
|
|
|
import com.inspect.nvr.hik.domain.HikNvrPresetListResponse;
|
|
|
import com.inspect.nvr.hik.domain.HikNvrUiActionRequest;
|
|
|
import com.inspect.nvr.hik.domain.HikNvrUiActionResponse;
|
|
|
import com.inspect.nvr.hik.exception.HikNvrPlaybackException;
|
|
|
import com.inspect.nvr.hik.service.HikNvrCredentialService;
|
|
|
import com.inspect.nvr.hik.service.HikNvrLivePublicBaseUrlResolver;
|
|
|
import com.inspect.nvr.hik.service.HikNvrLiveStreamService;
|
|
|
import com.inspect.nvr.hik.service.HikNvrUiActionService;
|
|
|
import org.springframework.http.HttpHeaders;
|
|
|
import org.springframework.http.MediaType;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
|
@RestController
|
|
|
@CrossOrigin
|
|
|
@RequestMapping("/hik/nvr/playback/hls/ui/actions")
|
|
|
public class HikNvrUiActionController {
|
|
|
|
|
|
/** 海康云台、预置位和抓图操作服务。 */
|
|
|
@Resource
|
|
|
private HikNvrUiActionService actionService;
|
|
|
/** 海康实时拉流服务。 */
|
|
|
@Resource
|
|
|
private HikNvrLiveStreamService liveStreamService;
|
|
|
/** 按请求来源地址生成实时预览对外播放地址。 */
|
|
|
@Resource
|
|
|
private HikNvrLivePublicBaseUrlResolver publicBaseUrlResolver;
|
|
|
/** 按页面选择的 NVR IP 从数据库读取服务端凭据。 */
|
|
|
@Resource
|
|
|
private HikNvrCredentialService credentialService;
|
|
|
/** 大华工作台预置位保存能力。 */
|
|
|
@Resource
|
|
|
private DahuaIvsVendorService dahuaVendorService;
|
|
|
/**
|
|
|
* 接收海康 NVR 云台控制指令。
|
|
|
*/
|
|
|
@PostMapping("/ptz")
|
|
|
/** 代理海康云台动作请求。 */
|
|
|
public ResponseEntity<HikNvrUiActionResponse> ptz(@RequestBody HikNvrUiActionRequest request) {
|
|
|
// 绑定页面所选 NVR 后执行云台动作,避免相同通道号被发送到默认设备。
|
|
|
return ResponseEntity.ok(actionServiceFor(request).ptz(request));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 调用指定通道的海康预置位。
|
|
|
*/
|
|
|
@PostMapping("/preset")
|
|
|
/** 代理海康预置位调用请求。 */
|
|
|
public ResponseEntity<HikNvrUiActionResponse> preset(@RequestBody HikNvrUiActionRequest request) {
|
|
|
// 绑定页面所选 NVR 后执行预置位。
|
|
|
return ResponseEntity.ok(actionServiceFor(request).preset(request));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 保存当前云台位置为指定的海康预置位。
|
|
|
*/
|
|
|
@PostMapping("/preset/create")
|
|
|
/** 代理海康预置位保存请求。 */
|
|
|
public ResponseEntity<HikNvrUiActionResponse> createPreset(@RequestBody HikNvrUiActionRequest request) {
|
|
|
// 先按页面 NVR IP 读取数据库厂商,浏览器不能自行选择 SDK。
|
|
|
NvrInfo nvr = nvrInfoFor(request);
|
|
|
// 大华工作台复用同一路由,由 NetSDK 保存页面指定编号的预置位。
|
|
|
if (isDahua(nvr.getNvrCompany())) {
|
|
|
return ResponseEntity.ok(dahuaVendorService.createPreset(request));
|
|
|
}
|
|
|
// 海康设备继续沿用原有 SDK/ISAPI 保存实现。
|
|
|
return ResponseEntity.ok(actionService.forNvr(nvr).createPreset(request));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 读取指定通道的海康预置位列表。
|
|
|
*/
|
|
|
@GetMapping("/presets")
|
|
|
public ResponseEntity<HikNvrPresetListResponse> listPresets(
|
|
|
@RequestParam("channel") Integer channel,
|
|
|
@RequestParam("nvrIp") String nvrIp) {
|
|
|
// 创建内部请求对象并读取 NVR 当前预置位列表。
|
|
|
HikNvrUiActionRequest request = new HikNvrUiActionRequest();
|
|
|
request.setNvrIp(nvrIp);
|
|
|
request.setChannel(channel);
|
|
|
// 绑定页面所选 NVR 后委托海康 ISAPI 列表读取能力。
|
|
|
return ResponseEntity.ok(actionServiceFor(request).listPresets(request));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 抓取海康 NVR 当前通道的 JPEG 图片。
|
|
|
*/
|
|
|
@PostMapping(value = "/snapshot", produces = MediaType.IMAGE_JPEG_VALUE)
|
|
|
/** 代理海康 JPEG 抓图请求。 */
|
|
|
public ResponseEntity<byte[]> snapshot(@RequestBody HikNvrUiActionRequest request) {
|
|
|
// 绑定页面所选 NVR 后调用海康抓图服务获取 JPEG 字节。
|
|
|
byte[] image = actionServiceFor(request).snapshot(request);
|
|
|
// 从请求中提取通道号用于响应文件名和响应头。
|
|
|
int channel = request == null || request.getChannel() == null ? 0 : request.getChannel();
|
|
|
String filename = "nvr-ch" + channel + "-" + System.currentTimeMillis() + ".jpg";
|
|
|
return ResponseEntity.ok()
|
|
|
.contentType(MediaType.IMAGE_JPEG)
|
|
|
.contentLength(image.length)
|
|
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
|
|
.header("X-Capture-Channel", String.valueOf(channel))
|
|
|
.body(image);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 启动指定通道的实时拉流任务。
|
|
|
*/
|
|
|
@PostMapping("/live/start")
|
|
|
/** 代理实时拉流启动请求。 */
|
|
|
public ResponseEntity<HikNvrLiveStreamResponse> startLive(
|
|
|
@RequestBody HikNvrUiActionRequest request,
|
|
|
HttpServletRequest httpRequest) {
|
|
|
// 从页面请求提取设备树完整 cameraCode 和可选码流类型。
|
|
|
String cameraCode = request == null ? null : request.getCameraCode();
|
|
|
Integer streamType = request == null ? null : request.getStreamType();
|
|
|
// 内网与外网两个 IP 共用本服务时,flvUrl/hlsUrl 必须指向本次请求实际访问的地址。
|
|
|
String publicBaseUrl = publicBaseUrlResolver.resolve(httpRequest);
|
|
|
// 实时服务通过现有 IVS 媒体接口解析 cameraCode 并取得 RTSP 源地址。
|
|
|
return ResponseEntity.ok(
|
|
|
liveStreamService.start(cameraCode, streamType, publicBaseUrl));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 停止指定通道的实时拉流任务。
|
|
|
*/
|
|
|
@PostMapping("/live/stop")
|
|
|
/** 代理实时拉流停止请求。 */
|
|
|
public ResponseEntity<HikNvrLiveStreamResponse> stopLive(@RequestBody HikNvrUiActionRequest request) {
|
|
|
// 从页面请求提取要停止的完整 cameraCode。
|
|
|
String cameraCode = request == null ? null : request.getCameraCode();
|
|
|
// 只停止该 cameraCode 对应的流,不影响设备树中的其他摄像机。
|
|
|
return ResponseEntity.ok(liveStreamService.stop(cameraCode));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 停止本服务创建的全部实时拉流任务。
|
|
|
*/
|
|
|
@PostMapping("/live/stop-all")
|
|
|
/** 运维与批量任务收尾使用:一次释放所有 NVR 实时会话。 */
|
|
|
public ResponseEntity<HikNvrLiveStreamResponse> stopAllLive() {
|
|
|
// 批量清理不需要 cameraCode,直接释放全部会话和排队任务。
|
|
|
return ResponseEntity.ok(liveStreamService.stopAll());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询指定通道的实时拉流状态。
|
|
|
*/
|
|
|
@GetMapping("/live/status")
|
|
|
public ResponseEntity<HikNvrLiveStreamResponse> liveStatus(
|
|
|
@RequestParam("cameraCode") String cameraCode,
|
|
|
HttpServletRequest httpRequest) {
|
|
|
// 按设备树完整 cameraCode 查询唯一实时拉流状态。
|
|
|
// 状态轮询同样按请求来源返回播放地址,保证与外网/内网访问方一致。
|
|
|
return ResponseEntity.ok(liveStreamService.status(
|
|
|
cameraCode, publicBaseUrlResolver.resolve(httpRequest)));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 为页面请求绑定数据库中已启用的目标 NVR。
|
|
|
*
|
|
|
* @param request 页面操作请求,必须包含所选 NVR IP
|
|
|
* @return 绑定数据库目标 NVR 的海康操作服务
|
|
|
*/
|
|
|
private HikNvrUiActionService actionServiceFor(HikNvrUiActionRequest request) {
|
|
|
return actionService.forNvr(nvrInfoFor(request));
|
|
|
}
|
|
|
|
|
|
/** 按页面请求读取并校验数据库中的目标 NVR。 */
|
|
|
private NvrInfo nvrInfoFor(HikNvrUiActionRequest request) {
|
|
|
// 多 NVR 环境必须明确目标 IP,禁止按固定配置或隐式默认设备执行控制。
|
|
|
if (request == null || !hasText(request.getNvrIp())) {
|
|
|
throw HikNvrPlaybackException.badRequest("NVR IP不能为空");
|
|
|
}
|
|
|
// 只允许绑定数据库中已启用的 NVR,页面无法直接提供账号密码。
|
|
|
return credentialService.resolveByIp(request.getNvrIp().trim());
|
|
|
}
|
|
|
|
|
|
/** 判断数据库厂商字段是否表示大华。 */
|
|
|
private boolean isDahua(String vendor) {
|
|
|
return vendor != null
|
|
|
&& (vendor.trim().toUpperCase().contains("DAHUA")
|
|
|
|| vendor.contains("大华"));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 判断页面提交的 NVR IP 是否包含有效文本。
|
|
|
*
|
|
|
* @param value 待检查文本
|
|
|
* @return 非空且不全为空白时返回 true
|
|
|
*/
|
|
|
private boolean hasText(String value) {
|
|
|
// 空文本不能定位数据库 NVR。
|
|
|
return value != null && !value.trim().isEmpty();
|
|
|
}
|
|
|
}
|