|
@@ -0,0 +1,389 @@
|
|
|
+package com.persagy.cameractl.service;
|
|
|
+
|
|
|
+import com.hikvision.artemis.sdk.ArtemisHttpUtil;
|
|
|
+import com.hikvision.artemis.sdk.config.ArtemisConfig;
|
|
|
+import com.persagy.cameractl.conf.CameraApiConfig;
|
|
|
+import com.persagy.cameractl.utils.Camera;
|
|
|
+import com.persagy.cameractl.utils.CmdStreamThread;
|
|
|
+import com.persagy.cameractl.utils.EnumTools;
|
|
|
+import com.persagy.cameractl.utils.ResultClass;
|
|
|
+import com.persagy.cameractl.utils.JsonTools;
|
|
|
+import com.persagy.cameractl.utils.OtherTools;
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+
|
|
|
+import java.io.File;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import net.sf.json.JSONObject;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class HkCameraExecuteApi {
|
|
|
+ Logger logger = LoggerFactory.getLogger(HkCameraExecuteApi.class);
|
|
|
+ @Autowired
|
|
|
+ private CameraApiConfig cameraApiConfig;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP2:设置OpenAPI接口的上下文
|
|
|
+ */
|
|
|
+ final String ARTEMIS_PATH = "/artemis/api/";
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 解析返回结果
|
|
|
+ * */
|
|
|
+ private ResultClass parseResult(String resultStr) {
|
|
|
+ ResultClass resultClass = new ResultClass();
|
|
|
+ resultClass.name = false;
|
|
|
+ try {
|
|
|
+ if (resultStr == null || resultStr.equals("")) {
|
|
|
+ resultClass.reason = "结果为空";
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+ // org.springframework.boot.configurationprocessor.json.JSONObject resultMap=new
|
|
|
+ // org.springframework.boot.configurationprocessor.json.JSONObject(resultStr);
|
|
|
+ Map<String, Object> mapResult = JsonTools.str2Map(resultStr);
|
|
|
+ String msgStr = String.valueOf(mapResult.get("msg"));
|
|
|
+ if (msgStr == null || !msgStr.equals("success")) {
|
|
|
+ resultClass.reason = "错误码:" + String.valueOf(mapResult.get("code")) + ",错误原因:"
|
|
|
+ + String.valueOf(mapResult.get("msg"));
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+ resultClass.name = true;
|
|
|
+ resultClass.resultData = mapResult.get("data");
|
|
|
+ return resultClass;
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康中心平台返回结果非法:" + resultStr);
|
|
|
+ resultClass.reason = "海康中心平台返回结果非法,请查看后台日志";
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 发送请求并解析返回结果
|
|
|
+ * */
|
|
|
+ private ResultClass SendRequest(String url, String paramString) {
|
|
|
+ /**
|
|
|
+ * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数.
|
|
|
+ */
|
|
|
+ ArtemisConfig.host = cameraApiConfig.getHkApiHost(); // artemis网关服务器ip端口
|
|
|
+ ArtemisConfig.appKey = cameraApiConfig.getHkApiKey(); // 秘钥appkey
|
|
|
+ ArtemisConfig.appSecret = cameraApiConfig.getHkApiSecret();// 秘钥appSecret
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP3:设置接口的URI地址
|
|
|
+ */
|
|
|
+ String previewURLsApi = ARTEMIS_PATH + url;
|
|
|
+ Map<String, String> path = new HashMap<String, String>(2) {
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ private static final long serialVersionUID = 1L;
|
|
|
+
|
|
|
+ {
|
|
|
+ put("https://", previewURLsApi);// 根据现场环境部署确认是http还是https
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP4:设置参数提交方式
|
|
|
+ */
|
|
|
+ String contentType = "application/json";
|
|
|
+ /**
|
|
|
+ * STEP6:调用接口
|
|
|
+ */
|
|
|
+ String result = ArtemisHttpUtil.doPostStringArtemis(path, paramString, null, null, contentType, null);// post请求application/json类型参数
|
|
|
+ return parseResult(result);
|
|
|
+ };
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 根据监控点获取预览流
|
|
|
+ */
|
|
|
+ public ResultClass GetCameraPreviewURL(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String url = "video/v2/cameras/previewURLs";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ */
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ jsonBody.put("cameraIndexCode", hkCamera.cameraIndexCode);
|
|
|
+ jsonBody.put("streamType", hkCamera.streamType);
|
|
|
+ jsonBody.put("protocol", hkCamera.protocol);
|
|
|
+ // 传输协议(传输层协议),0:UDP 1:TCP 默认是TCP 注:GB28181 2011及以前版本只支持UDP传输
|
|
|
+// jsonBody.put("transmode", 1);
|
|
|
+ String expandStr = "transcode=" + hkCamera.transcode + "&resolution=" + hkCamera.resolution + "&bitrate="
|
|
|
+ + hkCamera.bitrate + "&videotype=" + hkCamera.videotype;
|
|
|
+ jsonBody.put("expand", expandStr);
|
|
|
+ String body = jsonBody.toString();
|
|
|
+ return SendRequest(url, body);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康调用实时预览API出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用实时预览API出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 根据监控点获取回放流
|
|
|
+ */
|
|
|
+ public ResultClass GetCameraPlayBackURL(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String reqUrl = "video/v2/cameras/playbackURLs";
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ * 因为香港置地返回的回放rtsp流总是在剩余大概15秒的时候卡顿,所以传过去的结束时间要加30秒,所以ffmpeg命令里要用-t控制实际读取时长
|
|
|
+ */
|
|
|
+
|
|
|
+ Date dateStart = DateUtil.parse(hkCamera.startDateStr);
|
|
|
+ long startMillSeconds = dateStart.getTime();
|
|
|
+
|
|
|
+ Date dateEnd = DateUtil.parse(hkCamera.endDateStr);
|
|
|
+ long endMillSeconds = dateEnd.getTime();
|
|
|
+
|
|
|
+ // 先求得真实的回放时长
|
|
|
+ long temp = endMillSeconds - startMillSeconds;
|
|
|
+ long hourCount = 0;
|
|
|
+ long minuteCount = 0;
|
|
|
+ long secondCount = temp / 1000;
|
|
|
+ long millSecondCount = temp % 1000;
|
|
|
+ // 到了分钟级
|
|
|
+ if (secondCount >= 60) {
|
|
|
+ minuteCount = secondCount / 60;
|
|
|
+ secondCount = secondCount % 60;
|
|
|
+ // 到了小时级
|
|
|
+ if (minuteCount >= 60) {
|
|
|
+ hourCount = minuteCount / 60;
|
|
|
+ minuteCount = minuteCount % 60;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ long endMillSeconds2 = endMillSeconds + 30 * 1000;
|
|
|
+ Date enDate2 = DateUtil.date(endMillSeconds2);
|
|
|
+ String endString2 = DateUtil.format(enDate2, "yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ jsonBody.put("cameraIndexCode", hkCamera.cameraIndexCode);
|
|
|
+ jsonBody.put("protocol", hkCamera.protocol);
|
|
|
+ jsonBody.put("beginTime", hkCamera.startDateStr.replace(" ", "T") + ".000+08:00");
|
|
|
+ jsonBody.put("endTime", endString2.replace(" ", "T") + ".000+08:00");
|
|
|
+
|
|
|
+ // 传输协议(传输层协议),0:UDP 1:TCP 默认是TCP 注:GB28181 2011及以前版本只支持UDP传输
|
|
|
+// jsonBody.put("transmode", 1);
|
|
|
+ String expandStr = "transcode=" + hkCamera.transcode + "&resolution=" + hkCamera.resolution + "&bitrate="
|
|
|
+ + hkCamera.bitrate + "&videotype=" + hkCamera.videotype;
|
|
|
+ jsonBody.put("expand", expandStr);
|
|
|
+ String body = jsonBody.toString();
|
|
|
+
|
|
|
+ ResultClass resultClass = SendRequest(reqUrl, body);
|
|
|
+// return resultClass;
|
|
|
+ // hls格式回放时,直接返回
|
|
|
+// if (hkCamera.protocol.equals("hls"))
|
|
|
+// return resultClass;
|
|
|
+ /*
|
|
|
+ * 如果不是hls格式,那么即为rtsp格式,此时需抓取rtsp流并转成mp4
|
|
|
+ * */
|
|
|
+ // 调用失败直接返回
|
|
|
+ if (!resultClass.name)
|
|
|
+ return resultClass;
|
|
|
+ String resultDataStr = JsonTools.obj2Str(resultClass.resultData);
|
|
|
+ Map<String, Object> resultDataMap = JsonTools.str2Map(resultDataStr);
|
|
|
+ String videoUrl = String.valueOf(resultDataMap.get("url"));
|
|
|
+ if (videoUrl == null || videoUrl.equals(""))
|
|
|
+ return new ResultClass(false, "未获取到视频源地址");
|
|
|
+
|
|
|
+ String playFilePath = OtherTools.getVideoFilePath();
|
|
|
+ if (playFilePath.equals("")) {
|
|
|
+ return new ResultClass(false, "回放文件名称生成失败");
|
|
|
+ }
|
|
|
+ // 抓取视频流转为页面上可播放的MP4
|
|
|
+ Runtime run = Runtime.getRuntime();
|
|
|
+ /*
|
|
|
+ * stimeout、rw_timeout的单位为微秒 1秒=1000000微秒
|
|
|
+ * 因为香港置地返回的回放rtsp流总是在剩余大概15秒的时候卡顿,所以传过去的结束时间要加30秒,所以ffmpeg命令里要用-t控制实际读取时长
|
|
|
+ * */
|
|
|
+ String timeDuration = hourCount + ":" + minuteCount + ":" + secondCount
|
|
|
+ + (millSecondCount > 0 ? "." + millSecondCount : "");
|
|
|
+ String cmdStr = hkCamera.protocol.equals("hls")
|
|
|
+ ? ("ffmpeg -i \"" + videoUrl + "\" -c copy -y \"" + playFilePath + "\"")
|
|
|
+ : ("ffmpeg -t " + timeDuration + " -rtsp_transport tcp -i \"" + videoUrl + "\" -y \"" + playFilePath
|
|
|
+ + "\"");
|
|
|
+ Process p = run.exec(cmdStr);
|
|
|
+
|
|
|
+ // 读取标准输入流、输出流,防止进程阻塞
|
|
|
+ CmdStreamThread cmdStreamThread = new CmdStreamThread(p.getInputStream(), p.getErrorStream());
|
|
|
+
|
|
|
+ int retCode = p.waitFor();
|
|
|
+ p.destroy();
|
|
|
+ if (retCode == 0) {
|
|
|
+// 正常转换结束
|
|
|
+ File mp4File = new File(playFilePath);
|
|
|
+ String playFileName = mp4File.getName();
|
|
|
+ String token = OtherTools.getMp4NamePrefix(playFileName);
|
|
|
+ String url = OtherTools.playMp4RootUrl + token;
|
|
|
+ Map<String, String> dataMap = new HashMap<String, String>();
|
|
|
+ dataMap.put("url", url);
|
|
|
+ dataMap.put("videoUrl", videoUrl);
|
|
|
+ return new ResultClass(true, dataMap);
|
|
|
+ } else {
|
|
|
+// 转换出错
|
|
|
+ String errStr = cmdStreamThread.errString != null ? cmdStreamThread.errString
|
|
|
+ : cmdStreamThread.outString;
|
|
|
+ logger.error("ffmpeg抓取rtsp转为mp4时失败,错误信息:" + errStr);
|
|
|
+ return new ResultClass(false, "ffmpeg抓取rtsp转为mp4时失败");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康调用回调API出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用回调API出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 控制命令入口
|
|
|
+ * */
|
|
|
+ public ResultClass PtzControllingMain(Camera hkCamera) {
|
|
|
+ // 开始控制命令
|
|
|
+ ResultClass startResultClass = PtzControlling(hkCamera, 0);
|
|
|
+ // 控制命令执行成功后要调用停止命令
|
|
|
+ if (startResultClass.name) {
|
|
|
+ // 停止控制
|
|
|
+ ResultClass endResultClass = PtzControlling(hkCamera, 1);
|
|
|
+ return endResultClass;
|
|
|
+ }
|
|
|
+ return startResultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 根据监控点进行云台控制
|
|
|
+ */
|
|
|
+ private ResultClass PtzControlling(Camera hkCamera, int action) {
|
|
|
+ try {
|
|
|
+ String url = "video/v1/ptzs/controlling";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ */
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ jsonBody.put("cameraIndexCode", hkCamera.cameraIndexCode);
|
|
|
+ // 0 开始 1 停止
|
|
|
+ jsonBody.put("action", action);
|
|
|
+
|
|
|
+ int sdkCommandIndex = EnumTools.listSdkCommand.indexOf(hkCamera.command);
|
|
|
+ if (sdkCommandIndex == -1)
|
|
|
+ return new ResultClass(false, "无效的控制命令");
|
|
|
+ String commandStr = EnumTools.arrHkvisionCommand[sdkCommandIndex];
|
|
|
+
|
|
|
+ jsonBody.put("command", commandStr);
|
|
|
+ jsonBody.put("speed", hkCamera.speed);
|
|
|
+ String body = jsonBody.toString();
|
|
|
+
|
|
|
+ ResultClass resultClass = SendRequest(url, body);
|
|
|
+ return resultClass;
|
|
|
+ } catch (Exception e) {
|
|
|
+ String str = action == 0 ? "开始时" : "停止时";
|
|
|
+ logger.error("海康调用控制API," + str + "出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用控制API" + str + "出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 分页获取监控点 */
|
|
|
+ public ResultClass GetCameraPointsByPage(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String url = "resource/v1/cameras";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ */
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ jsonBody.put("pageNo", hkCamera.pageNo);
|
|
|
+ jsonBody.put("pageSize", hkCamera.pageSize);
|
|
|
+ String body = jsonBody.toString();
|
|
|
+
|
|
|
+ return SendRequest(url, body);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康调用分页获取监控点API出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用分页获取监控点API出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 获取access_token
|
|
|
+ */
|
|
|
+ public ResultClass GetAccessToken(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String url = "v1/oauth/token";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ */
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ String body = jsonBody.toString();
|
|
|
+ return SendRequest(url, body);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康调用access_token API出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用access_token API出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 获取电视墙大屏信息
|
|
|
+ */
|
|
|
+ public ResultClass GetDianshiQiang(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String url = "tvms/v1/tvwall/allResources";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * STEP5:组装请求参数
|
|
|
+ */
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ String body = jsonBody.toString();
|
|
|
+ ResultClass resultClass = SendRequest(url, body);
|
|
|
+ return resultClass;
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康调用电视墙大屏API出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用电视墙大屏API出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 传入接口名称和参数,方便客户端调用测试
|
|
|
+ */
|
|
|
+ public ResultClass GetOtherInfo(Camera hkCamera) {
|
|
|
+ try {
|
|
|
+ String paramStr = "";
|
|
|
+ if (hkCamera.paramStr != null && !"".equals(hkCamera.paramStr)) {
|
|
|
+ JSONObject jsonBody = new JSONObject();
|
|
|
+ Map<String, Object> tempMap = JsonTools.str2Map(hkCamera.paramStr);
|
|
|
+
|
|
|
+ for (Map.Entry<String, Object> entry : tempMap.entrySet()) {
|
|
|
+ jsonBody.put(entry.getKey(), entry.getValue());
|
|
|
+ }
|
|
|
+ paramStr = jsonBody.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ ResultClass resultClass = SendRequest(hkCamera.url, paramStr);
|
|
|
+ return resultClass;
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("海康" + hkCamera.url + "出错:", e);
|
|
|
+ ResultClass resultClass = new ResultClass(false, "海康调用" + hkCamera.url + "出错");
|
|
|
+ return resultClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|