Jelajahi Sumber

视频平台数据拉取和推流

root 3 tahun lalu
induk
melakukan
1058653d51
23 mengubah file dengan 1535 tambahan dan 89 penghapusan
  1. 52 1
      pom.xml
  2. 181 0
      src/main/java/com/zhiqiyun/open/camera/config/CameraConfig.java
  3. 200 0
      src/main/java/com/zhiqiyun/open/camera/rtsp/RtspPushSrs.java
  4. 71 0
      src/main/java/com/zhiqiyun/open/camera/rtsp/RtspTransfer.java
  5. 27 0
      src/main/java/com/zhiqiyun/open/config/BeanConfig.java
  6. 2 5
      src/main/java/com/zhiqiyun/open/config/WebMvcConfig.java
  7. 40 0
      src/main/java/com/zhiqiyun/open/core/models/hwVideo/DeviceList.java
  8. 32 0
      src/main/java/com/zhiqiyun/open/core/schedule/AutoRefreshVideoSchedule.java
  9. 3 3
      src/main/java/com/zhiqiyun/open/core/schedule/AutoSchedule.java
  10. 40 0
      src/main/java/com/zhiqiyun/open/core/video/VideoAnalysisService.java
  11. 42 6
      src/main/java/com/zhiqiyun/open/core/video/VideoDockingService.java
  12. 351 0
      src/main/java/com/zhiqiyun/open/core/video/impl/VideoAnalysisServiceImpl.java
  13. 188 71
      src/main/java/com/zhiqiyun/open/core/video/impl/VideoDockingServiceImpl.java
  14. 1 1
      src/main/java/com/zhiqiyun/open/mvc/controller/BzController.java
  15. 56 0
      src/main/java/com/zhiqiyun/open/mvc/controller/HwVideoController.java
  16. 44 0
      src/main/java/com/zhiqiyun/open/mvc/controller/RtspFlvPlayController.java
  17. 2 2
      src/main/java/com/zhiqiyun/open/router/apis/CategoryApi.java
  18. 94 0
      src/main/java/com/zhiqiyun/open/router/apis/HwVideoApi.java
  19. 24 0
      src/main/java/com/zhiqiyun/open/router/request/hwVideo/LoadVideoPageRequest.java
  20. 21 0
      src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoRtsUrlRequest.java
  21. 22 0
      src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoRtsUrlResponse.java
  22. 20 0
      src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoStopPushRequest.java
  23. 22 0
      src/main/java/com/zhiqiyun/open/utils/ServletContext.java

+ 52 - 1
pom.xml

@@ -28,7 +28,7 @@
         <mybatis.version>3.5.7</mybatis.version>
         <mybatis-plus-boot.version>3.5.1</mybatis-plus-boot.version>
 
-        <lombok.version>1.18.22</lombok.version>
+        <lombok.version>1.18.24</lombok.version>
         <javax.servlet.version>4.0.1</javax.servlet.version>
         <okhttp.version>4.9.3</okhttp.version>
         <webmagic.version>0.7.5</webmagic.version>
@@ -39,6 +39,10 @@
 
         <framework.version>1.0.20</framework.version>
         <db-migration.version>1.0.1</db-migration.version>
+
+        <javacv.version>1.5.7</javacv.version>
+
+        <hutool.version>5.8.9</hutool.version>
     </properties>
 
     <dependencies>
@@ -157,6 +161,53 @@
             <artifactId>phantomjsdriver</artifactId>
             <version>1.5.0</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>javacv</artifactId>
+            <version>${javacv.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>javacpp</artifactId>
+            <version>${javacv.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>opencv</artifactId>
+            <version>4.5.5-${javacv.version}</version>
+            <classifier>linux-x86_64</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>openblas</artifactId>
+            <version>0.3.19-${javacv.version}</version>
+            <classifier>linux-x86_64</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>ffmpeg</artifactId>
+            <version>5.0-${javacv.version}</version>
+            <classifier>linux-x86_64</classifier>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+
+        <!-- Optional GPL builds with (almost) everything enabled -->
+        <!--        <dependency>-->
+        <!--            <groupId>org.bytedeco</groupId>-->
+        <!--            <artifactId>ffmpeg-platform-gpl</artifactId>-->
+        <!--            <version>5.0-${javacv.version}</version>-->
+        <!--        </dependency>-->
+        <!--        <dependency>-->
+        <!--            <groupId>org.bytedeco</groupId>-->
+        <!--            <artifactId>javacv-platform</artifactId>-->
+        <!--            <version>${javacv.version}</version>-->
+        <!--        </dependency>-->
     </dependencies>
     <build>
         <plugins>

+ 181 - 0
src/main/java/com/zhiqiyun/open/camera/config/CameraConfig.java

@@ -0,0 +1,181 @@
+package com.zhiqiyun.open.camera.config;
+
+import cn.hutool.core.util.XmlUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.zhiqiyun.open.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+import org.w3c.dom.Document;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.xml.xpath.XPathConstants;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author xin yang
+ * @date 2022/10/19
+ */
+@Slf4j
+public class CameraConfig {
+
+    public final static String USER_NAME = "duijie01";
+    public final static String PASSWORD = "Huawei@12#$";
+    public final static String VCM_URI = "https://172.169.12.21:443";
+    public final static int SUCCESS_STATUS_CODE = 200;
+    public final static int SUCCESS_CODE = 0;
+
+    public final static String RESULT_CODE = "resultCode";
+
+    /**
+     * 创建默认的ssl链接
+     *
+     * @return CloseableHttpClient
+     */
+    public static CloseableHttpClient createSSLClientDefault() {
+        try {
+            // 信任所有
+            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();
+            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
+            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
+            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
+        } catch (Exception e) {
+            log.error("error", e);
+        }
+        return HttpClients.createDefault();
+
+    }
+
+    /**
+     * 组装GET方法参数
+     *
+     * @param uri         uri
+     * @param paramValues paramValues
+     * @return String
+     */
+    public static String getRequestGetUri(String uri, Map<String, String> paramValues) {
+        if (paramValues == null || paramValues.size() == 0) {
+            return uri;
+        }
+        Set<String> keySet = paramValues.keySet();
+        Iterator<String> iterator = keySet.iterator();
+        StringBuilder uriBuilder = new StringBuilder(uri);
+        while (iterator.hasNext()) {
+            String key = iterator.next();
+            String value = paramValues.get(key);
+            if (uriBuilder.toString().contains("?")) {
+                uriBuilder.append("&").append(key).append("=").append(value);
+            } else {
+                uriBuilder.append("?").append(key).append("=").append(value);
+            }
+        }
+        uri = uriBuilder.toString();
+        return uri;
+    }
+
+
+    public static String postXForm(Map<String, Object> paramValues) {
+        if (paramValues == null || paramValues.size() == 0) {
+            return "";
+        }
+        Set<String> keySet = paramValues.keySet();
+        Iterator<String> iterator = keySet.iterator();
+        StringBuilder uriBuilder = new StringBuilder();
+        while (iterator.hasNext()) {
+            String key = iterator.next();
+            Object value = paramValues.get(key);
+            if (StringUtils.isNotBlank(uriBuilder)) {
+                uriBuilder.append("&");
+            }
+            uriBuilder.append(key).append("=").append(value);
+        }
+        return uriBuilder.toString();
+    }
+
+    /**
+     * 获取返回数据
+     *
+     * @param response response
+     * @return String
+     * @throws IOException IOException
+     */
+    public static JSONObject getApacheBody(CloseableHttpResponse response) throws IOException {
+        HttpEntity entity = response.getEntity();
+        //用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
+        String result = EntityUtils.toString(entity, "UTF-8");
+        log.info("-------------------------" + result + "-------------");
+        log.info("CloseableHttpResponse msg:{}", JSONObject.toJSONString(response));
+        if (response.getStatusLine().getStatusCode() == SUCCESS_STATUS_CODE) {
+            log.info("-----------success------------------{}", result);
+            JSONObject jsonObject = JSONObject.parseObject(result);
+            int code = jsonObject.getIntValue(RESULT_CODE);
+            if (code == SUCCESS_CODE) {
+                return jsonObject;
+            }
+            String errorMsg = jsonObject.getString("error_description");
+            throw new ServiceException(StringUtils.isNotBlank(errorMsg) ? errorMsg : "数据加载失败 " + code);
+        } else {
+            throw new ServiceException("数据加载失败");
+        }
+    }
+
+    /**
+     * 获取XML返回数据
+     *
+     * @param response response
+     * @return String
+     * @throws IOException IOException
+     */
+    public static Map<String, Object> getApacheBodyXml(CloseableHttpResponse response) throws IOException {
+        HttpEntity entity = response.getEntity();
+        //用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
+        String result = EntityUtils.toString(entity, "UTF-8");
+        log.info("-------------------------" + result + "-------------");
+        log.info("CloseableHttpResponse msg:{}", JSONObject.toJSONString(response));
+        if (response.getStatusLine().getStatusCode() == SUCCESS_STATUS_CODE) {
+            log.info("-----------success------------------{}", result);
+            Document document = XmlUtil.readXML(result);
+            Map<String, Object> jsonObject = XmlUtil.xmlToMap(result);
+            Object code = XmlUtil.getByXPath("//response/result/code", document, XPathConstants.STRING);
+            if (Integer.parseInt(String.valueOf(code)) == SUCCESS_CODE) {
+                return jsonObject;
+            }
+            Object errmsg = XmlUtil.getByXPath("//response/result/errmsg", document, XPathConstants.STRING);
+            throw new ServiceException(StringUtils.isNotBlank(String.valueOf(errmsg)) ? (String) errmsg : "数据加载失败 " + code);
+        } else {
+            throw new ServiceException("数据加载失败");
+        }
+    }
+
+    /**
+     * 获取响应头参数
+     *
+     * @param response   response
+     * @param headerName headerName
+     * @return String
+     */
+    public static String getApacheHeader(CloseableHttpResponse response, String headerName) {
+        // 4. 获取所有响应头
+        org.apache.http.Header[] headerArray = response.getAllHeaders();
+        String cookie = "";
+        for (org.apache.http.Header header : headerArray) {
+            log.info("header name:{},value:{}", header.getName(), header.getValue());
+            if (headerName.contentEquals(header.getName())) {
+                cookie = header.getValue();
+            }
+        }
+        return cookie;
+    }
+}

+ 200 - 0
src/main/java/com/zhiqiyun/open/camera/rtsp/RtspPushSrs.java

@@ -0,0 +1,200 @@
+package com.zhiqiyun.open.camera.rtsp;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import lombok.extern.slf4j.Slf4j;
+import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
+import org.bytedeco.ffmpeg.avformat.AVFormatContext;
+import org.bytedeco.ffmpeg.avformat.AVStream;
+import org.bytedeco.ffmpeg.global.avcodec;
+import org.bytedeco.ffmpeg.global.avutil;
+import org.bytedeco.javacv.FFmpegFrameGrabber;
+import org.bytedeco.javacv.FFmpegFrameRecorder;
+import org.bytedeco.javacv.FFmpegLogCallback;
+import org.bytedeco.javacv.Frame;
+
+/**
+ * @author xin yang
+ * @date 2022/10/13
+ */
+@Slf4j
+public class RtspPushSrs {
+
+    //内网推流地址
+    public final static String INTRANET_SRS_PUSH_ADDRESS = "rtmp://116.8.106.156:7011/live/livestream/%s";
+
+    //外网拉流地址
+    public final static String SRS_PUSH_ADDRESS = "http://116.8.106.156:9800/live/livestream/%s.flv";
+
+    /**
+     * 读取指定的rtsp视频流,推送到SRS服务器
+     *
+     * @param rtspId 视频流ID
+     * @throws Exception
+     */
+    public static void grabberAndPush(String rtspId) throws Exception {
+
+        String rtspUrl = RtspTransfer.PATH_MAP.get(rtspId);
+
+        // ffmepg日志级别
+        avutil.av_log_set_level(avutil.AV_LOG_ERROR);
+        FFmpegLogCallback.set();
+
+        // 实例化帧抓取器对象,将文件路径传入
+        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
+        long startTime = System.currentTimeMillis();
+//        grabber.setFormat("hevc");
+        // 设置读取的最大数据,单位字节
+//        grabber.setOption("probesize", "10000");
+        // 设置分析的最长时间,单位微秒
+//        grabber.setOption("analyzeduration", "20000");
+        log.info("开始初始化帧抓取器");
+        grabber.setOption("rtsp_transport", "tcp");
+//        grabber.setOption("stimeout", "20000000");
+        // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
+        // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
+//        grabber.setImageWidth(640);
+//        grabber.setImageHeight(480);
+        try {
+            grabber.start();
+        } catch (Exception e) {
+            log.error("grabberAndPush error:" + e.getMessage(), e);
+        }
+        log.info("帧抓取器初始化完成,耗时[{}]毫秒", System.currentTimeMillis() - startTime);
+        // grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中
+        AVFormatContext avFormatContext = grabber.getFormatContext();
+
+        // 文件内有几个媒体流(一般是视频流+音频流)
+        int streamNum = avFormatContext.nb_streams();
+
+        // 没有媒体流就不用继续了
+        if (streamNum < 1) {
+            log.error("文件内不存在媒体流");
+            return;
+        }
+
+        // 取得视频的帧率
+        int frameRate = (int) grabber.getVideoFrameRate();
+
+        log.info("视频帧率[{}],视频时长[{}]秒,媒体流数量[{}]", frameRate, avFormatContext.duration() / 1000000, avFormatContext.nb_streams());
+
+        // 遍历每一个流,检查其类型
+        for (int i = 0; i < streamNum; i++) {
+            AVStream avStream = avFormatContext.streams(i);
+            AVCodecParameters avCodecParameters = avStream.codecpar();
+            log.info("流的索引[{}],编码器类型[{}],编码器ID[{}]", i, avCodecParameters.codec_type(), avCodecParameters.codec_id());
+        }
+
+        // 视频宽度
+        int frameWidth = grabber.getImageWidth();
+        // 视频高度
+        int frameHeight = grabber.getImageHeight();
+        // 音频通道数量
+        int audioChannels = grabber.getAudioChannels();
+        //编码格式
+//        int videoCodec = grabber.getVideoCodec();
+        //封装格式
+        String format = grabber.getFormat();
+
+        log.info("视频宽度[{}],视频高度[{}],音频通道数[{}],封装格式{{}]", frameWidth, frameHeight, audioChannels, format);
+
+        // 实例化FFmpegFrameRecorder,将SRS的推送地址传入
+        String rtmpUrl = String.format(INTRANET_SRS_PUSH_ADDRESS, rtspId);
+        log.info("流媒体服务地址:{}", rtmpUrl);
+        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, frameWidth, frameHeight, audioChannels);
+//        recorder.setInterleaved(true);
+//        recorder.setVideoOption("crf", "28");
+        // 设置编码格式
+        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
+        // 设置封装格式
+        recorder.setFormat("flv");
+        // 一秒内的帧数
+        recorder.setFrameRate(frameRate);
+//         两个关键帧之间的帧数
+        recorder.setGopSize(frameRate);
+        // 设置音频通道数,与视频源的通道数相等
+        recorder.setAudioChannels(audioChannels);
+        // yuv420p
+        recorder.setPixelFormat(0);
+        startTime = System.currentTimeMillis();
+        log.info("开始初始化帧抓取器");
+
+        // 初始化帧录制器,例如数据结构(音频流、视频流指针,编码器),
+        // 调用av_guess_format方法,确定视频输出时的封装方式,
+        // 媒体上下文对象的内存分配,
+        // 编码器的各项参数设置
+        recorder.start();
+
+        log.info("帧录制初始化完成,耗时[{}]毫秒", System.currentTimeMillis() - startTime);
+
+        Frame frame;
+
+        startTime = System.currentTimeMillis();
+
+        log.info("开始推流");
+
+        long videoTS = 0;
+
+        int videoFrameNum = 0;
+        int audioFrameNum = 0;
+        int dataFrameNum = 0;
+
+        // 假设一秒钟15帧,那么两帧间隔就是(1000/15)毫秒
+        int interVal = 1000 / frameRate;
+        // 发送完一帧后sleep的时间,不能完全等于(1000/frameRate),不然会卡顿,
+        // 要更小一些,这里取八分之一
+        interVal /= 8;
+
+        // 持续从视频源取帧
+        while (null != (frame = grabber.grab())) {
+            RtspTransfer.FRAME_GRABBER_CONCURRENT_HASH_MAP.put(rtspId, grabber);
+            RtspTransfer.FRAME_RECORDER_CONCURRENT_HASH_MAP.put(rtspId, recorder);
+
+
+            videoTS = 1000 * (System.currentTimeMillis() - startTime);
+//            log.info("持续从视频源取帧 videoTS:{}", videoTS);
+
+            // 时间戳
+            recorder.setTimestamp(videoTS);
+
+            // 有图像,就把视频帧加一
+            if (null != frame.image) {
+                videoFrameNum++;
+            }
+
+            // 有声音,就把音频帧加一
+            if (null != frame.samples) {
+                audioFrameNum++;
+            }
+
+            // 有数据,就把数据帧加一
+            if (null != frame.data) {
+                dataFrameNum++;
+            }
+
+            // 取出的每一帧,都推送到SRS
+            recorder.record(frame);
+
+            // 停顿一下再推送
+            Thread.sleep(interVal);
+        }
+
+        log.info("推送完成,视频帧[{}],音频帧[{}],数据帧[{}],耗时[{}]秒",
+                videoFrameNum,
+                audioFrameNum,
+                dataFrameNum,
+                (System.currentTimeMillis() - startTime) / 1000);
+
+        // 关闭帧录制器
+        recorder.close();
+        // 关闭帧抓取器
+        grabber.close();
+    }
+
+    public static void main(String[] args) throws Exception {
+        String rtspId = IdWorker.get32UUID();
+        System.out.println(rtspId);
+        RtspTransfer.PATH_MAP.put(rtspId, "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4");
+        // rtmp://121.40.185.233:1935/live/livestream/9ee705f6d05676b02d036088c2f09718
+        grabberAndPush(rtspId);
+    }
+}

+ 71 - 0
src/main/java/com/zhiqiyun/open/camera/rtsp/RtspTransfer.java

@@ -0,0 +1,71 @@
+package com.zhiqiyun.open.camera.rtsp;
+
+import lombok.extern.slf4j.Slf4j;
+import org.bytedeco.javacv.FFmpegFrameGrabber;
+import org.bytedeco.javacv.FFmpegFrameRecorder;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author xin yang
+ * @date 2022/10/12
+ */
+@Slf4j
+public class RtspTransfer {
+
+    public final static ConcurrentHashMap<String, String> PATH_MAP = new ConcurrentHashMap<>();
+    public final static ConcurrentHashMap<String, String> STREAM_MAP = new ConcurrentHashMap<>();
+    public final static ConcurrentHashMap<String, FFmpegFrameGrabber> FRAME_GRABBER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
+    public final static ConcurrentHashMap<String, FFmpegFrameRecorder> FRAME_RECORDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
+
+    /**
+     * 生成rtspId
+     *
+     * @param rtspUrl rtspUrl
+     * @return String
+     * @throws IOException IOException
+     */
+    public static String createRtspId(String rtspUrl, String rtspId) throws Exception {
+        PATH_MAP.put(rtspId, rtspUrl);
+        return rtspId;
+    }
+
+    /**
+     * 开始进行流推送
+     *
+     * @param rtspId rtspId
+     */
+    public static void startPushRtmp(String rtspId) throws Exception {
+        RtspPushSrs.grabberAndPush(rtspId);
+    }
+
+    /**
+     * 清除推流缓存
+     *
+     * @param rtspId rtspId
+     * @throws Exception Exception
+     */
+    public static void removeRtsp(String rtspId) {
+        RtspTransfer.PATH_MAP.remove(rtspId);
+        RtspTransfer.STREAM_MAP.remove(rtspId);
+        FFmpegFrameRecorder grabber = RtspTransfer.FRAME_RECORDER_CONCURRENT_HASH_MAP.get(rtspId);
+        if (grabber != null) {
+            RtspTransfer.FRAME_RECORDER_CONCURRENT_HASH_MAP.remove(rtspId);
+            try {
+                grabber.close();
+            } catch (Exception ex) {
+                log.info("removeRtsp============停止推流=======================");
+            }
+        }
+        FFmpegFrameGrabber recorder = RtspTransfer.FRAME_GRABBER_CONCURRENT_HASH_MAP.get(rtspId);
+        if (recorder != null) {
+            RtspTransfer.FRAME_GRABBER_CONCURRENT_HASH_MAP.remove(rtspId);
+            try {
+                recorder.close();
+            } catch (Exception ex) {
+                log.info("removeRtsp============停止推流=======================");
+            }
+        }
+    }
+}

+ 27 - 0
src/main/java/com/zhiqiyun/open/config/BeanConfig.java

@@ -1,5 +1,6 @@
 package com.zhiqiyun.open.config;
 
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
@@ -7,7 +8,11 @@ import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerIntercept
 import com.zhiqiyun.open.Application;
 import com.zhiqiyun.open.utils.SSLUtils;
 import lombok.extern.slf4j.Slf4j;
+import okhttp3.Cookie;
+import okhttp3.CookieJar;
+import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
+import org.jetbrains.annotations.NotNull;
 import org.lionsoul.ip2region.DbConfig;
 import org.lionsoul.ip2region.DbSearcher;
 import org.springframework.context.annotation.Bean;
@@ -15,6 +20,9 @@ import org.springframework.context.annotation.Configuration;
 
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 @Slf4j
@@ -36,6 +44,25 @@ public class BeanConfig {
         builder.connectTimeout(30, TimeUnit.SECONDS);
         builder.sslSocketFactory(SSLUtils.getSSLSocketFactory(), SSLUtils.getX509TrustManager());
         builder.hostnameVerifier(SSLUtils.getHostnameVerifier());
+        builder.cookieJar(new CookieJar() {
+            private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
+
+            @Override
+            public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> cookies) {
+                log.info("httpUrl.host():", httpUrl.host());
+                log.info("saveFromResponse cookies:", JSONArray.toJSONString(cookies));
+                cookieStore.put(httpUrl.host(), cookies);
+            }
+
+            @NotNull
+            @Override
+            public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
+                List<Cookie> cookies = cookieStore.get(httpUrl.host());
+                log.info("httpUrl.host():", httpUrl.host());
+                log.info("loadForRequest cookies:", JSONArray.toJSONString(cookies));
+                return cookies != null ? cookies : new ArrayList<Cookie>();
+            }
+        });
         return builder.build();
     }
 

+ 2 - 5
src/main/java/com/zhiqiyun/open/config/WebMvcConfig.java

@@ -17,7 +17,6 @@ import com.zhiqiyun.open.utils.ServletContext;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.MediaType;
 import org.springframework.http.converter.HttpMessageConverter;
@@ -34,10 +33,8 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.TimeZone;
 
 /**
  * 系统启动配置
@@ -101,8 +98,8 @@ public class WebMvcConfig extends DelegatingWebMvcConfiguration {
             public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) {
                 ServletContext.clean();
             }
-        }).addPathPatterns("/**").excludePathPatterns("/src/**", "/index.html");
-        registry.addInterceptor(this.oauthInterceptor).addPathPatterns("/**").excludePathPatterns("/src/**", "/index.html");
+        }).addPathPatterns("/**").excludePathPatterns("/src/**", "/index.html", "/rtsp/flv");
+        registry.addInterceptor(this.oauthInterceptor).addPathPatterns("/**").excludePathPatterns("/src/**", "/index.html", "/IntelligentData/**");
         super.addInterceptors(registry);
     }
 

+ 40 - 0
src/main/java/com/zhiqiyun/open/core/models/hwVideo/DeviceList.java

@@ -0,0 +1,40 @@
+package com.zhiqiyun.open.core.models.hwVideo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author xin yang
+ * @date 2022/10/12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class DeviceList implements Serializable {
+    private static final long serialVersionUID = -4761871355640226327L;
+    private String vendorType;
+    private String domainCode;
+    private String devFormType;
+    private String code;
+    private Integer cameraStatus;
+    private String devCreateTime;
+    private Integer netType;
+    private String latitude;
+    private String devGroupCode;
+    private Integer type;
+    private String devIp;
+    private String cameraLocation;
+    private Integer isExDomain;
+    private String parentCode;
+    private Integer supportIntelligent;
+    private String name;
+    private String nvrCode;
+    private Integer enableVoice;
+    private String devModelType;
+    private String status;
+    private String longitude;
+    private BigDecimal height;
+    private String rtspId;
+}

+ 32 - 0
src/main/java/com/zhiqiyun/open/core/schedule/AutoRefreshVideoSchedule.java

@@ -0,0 +1,32 @@
+package com.zhiqiyun.open.core.schedule;
+
+import com.zhiqiyun.open.core.video.VideoDockingService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * 刷新视频token进行用户登录信息保活
+ *
+ * @author NINGMEI
+ */
+@Slf4j
+@Component
+public class AutoRefreshVideoSchedule {
+
+    @Autowired
+    private VideoDockingService videoDockingService;
+
+    @Value("${spring.profiles.active}")
+    private String active;
+
+    @Scheduled(cron = "0 0/25 * * * ? ")
+    public void refreshValidToken() throws Exception {
+        if ("dev".equals(active)) {
+            return;
+        }
+//        videoDockingService.refreshValidToken();
+    }
+}

+ 3 - 3
src/main/java/com/zhiqiyun/open/core/schedule/AutoSchedule.java

@@ -96,7 +96,7 @@ public class AutoSchedule {
 		wrapper.lt("created_time", DateUtils.addDays(DateUtil.current(), -15));
 
 		List<EquipmentPassengerPeople> list = this.equipmentPassengerPeopleService.list(wrapper);
-		log.info("deleteExpireEquipmentPassengerPeople size>>>>{}", list.size());
+//		log.info("deleteExpireEquipmentPassengerPeople size>>>>{}", list.size());
 
 		for (EquipmentPassengerPeople people : list) {
 			this.equipmentPassengerPeopleService.removeById(people.getId());
@@ -109,7 +109,7 @@ public class AutoSchedule {
 		wrapper.inSql("face_id", "select face_id from equipment_passenger_people GROUP BY face_id HAVING count(face_id) = 1");
 		wrapper.eq("passenger_type", PassengerType.OUT);
 		List<EquipmentPassengerPeople> list = this.equipmentPassengerPeopleService.list(wrapper);
-		log.info("deleteOneFaceId size>>>>{}", list.size());
+//		log.info("deleteOneFaceId size>>>>{}", list.size());
 		for (EquipmentPassengerPeople people : list) {
 			this.equipmentPassengerPeopleService.removeById(people.getId());
 		}
@@ -121,7 +121,7 @@ public class AutoSchedule {
 		wrapper.lt("service_begin_time", DateUtils.addDays(DateUtil.current(), -30));
 
 		List<ApiRequestLog> list = this.apiRequestLogService.list(wrapper);
-		log.info("deleteExpireApiRequestLogs size>>>>{}", list.size());
+//		log.info("deleteExpireApiRequestLogs size>>>>{}", list.size());
 
 		for (ApiRequestLog log : list) {
 			this.apiRequestLogService.removeById(log.getId());

+ 40 - 0
src/main/java/com/zhiqiyun/open/core/video/VideoAnalysisService.java

@@ -0,0 +1,40 @@
+package com.zhiqiyun.open.core.video;
+
+import java.util.Map;
+
+/**
+ * 视频分析数据
+ *
+ * @author xin yang
+ * @date 2022/10/19
+ */
+public interface VideoAnalysisService {
+
+    /**
+     * 获取用户登录授权token
+     * 30分钟有效期
+     *
+     * @return String
+     * @throws Exception Exception
+     */
+    String getValidToken() throws Exception;
+
+    /**
+     * 刷新用户token,保持用户登录姿态
+     *
+     * @throws Exception Exception
+     */
+    void refreshValidToken() throws Exception;
+
+    /**
+     * 获取摄像机列表
+     *
+     * @param paramValues paramValues
+     */
+    void cameraList(Map<String, String> paramValues) throws Exception;
+
+    /**
+     * 批量创建智能分析任务
+     */
+    void startIntelligentAnalysis(String ids) throws Exception;
+}

+ 42 - 6
src/main/java/com/zhiqiyun/open/core/video/VideoDockingService.java

@@ -1,6 +1,9 @@
 package com.zhiqiyun.open.core.video;
 
+import com.alibaba.fastjson.JSONObject;
+
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * @author xin yang
@@ -13,21 +16,54 @@ public interface VideoDockingService {
      * 30分钟有效期
      *
      * @return String
-     * @throws IOException IOException
+     * @throws Exception Exception
      */
-    String getValidToken() throws IOException;
+    String getValidToken() throws Exception;
 
     /**
      * 刷新用户token,保持用户登录姿态
      *
-     * @throws IOException IOException
+     * @throws Exception Exception
      */
-    void refreshValidToken() throws IOException;
+    void refreshValidToken() throws Exception;
 
     /**
      * 获取设备列表信息
      *
-     * @throws IOException
+     * @param paramValues paramValues
+     * @return JSONObject
+     * @throws Exception Exception
+     */
+    JSONObject loadDeviceList(Map<String, String> paramValues) throws Exception;
+
+    /**
+     * 获取视频 URL 信息
+     *
+     * @param cameraCode 实时浏览或录像的摄像机编码
+     * @return JSONObject
+     * @throws Exception Exception
+     */
+    JSONObject loadVideoRtspUrl(String cameraCode) throws Exception;
+
+    /**
+     * 开始实时浏览
+     *
+     * @param cameraCode cameraCode
+     * @throws Exception Exception
+     */
+    void startRealPlayByIpex(String cameraCode) throws Exception;
+
+    /**
+     * 添加实时智能数据订阅
+     *
+     * @param ids ids
+     */
+    void startIntelligentDataSubscribes(String ids);
+
+    /**
+     * 删除实时智能数据订阅
+     *
+     * @param ids ids
      */
-    void loadDeviceList() throws IOException;
+    void deleteIntelligentDataSubscribes(String ids) throws Exception;
 }

+ 351 - 0
src/main/java/com/zhiqiyun/open/core/video/impl/VideoAnalysisServiceImpl.java

@@ -0,0 +1,351 @@
+package com.zhiqiyun.open.core.video.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Maps;
+import com.zhiqiyun.open.core.models.hwVideo.DeviceList;
+import com.zhiqiyun.open.core.video.VideoAnalysisService;
+import com.zhiqiyun.open.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.assertj.core.util.Lists;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static com.zhiqiyun.open.camera.config.CameraConfig.*;
+
+/**
+ * @author xin yang
+ * @date 2022/10/19
+ */
+@Slf4j
+@Service
+public class VideoAnalysisServiceImpl implements VideoAnalysisService {
+
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
+    private final static int pageSize = 100;
+
+    @Resource
+    private RedisTemplate<String, String> redisTemplate;
+
+    private final static String LINE_CROSSING = "<run_extradata>" +
+            "    <![CDATA[<vcm_beh_alg_config>" +
+            "        <BEH_GLOBAL_ALARM>" +
+            "            <minSizeWidth>0.01</minSizeWidth>" +
+            "            <minSizeHeight>0.01</minSizeHeight>" +
+            "            <maxSizeWidth>0.9</maxSizeWidth>" +
+            "            <maxSizeHeight>0.9</maxSizeHeight>" +
+            "            <detSensitivity>1</detSensitivity>" +
+            "            <backgroundUpdateRate>1</backgroundUpdateRate>" +
+            "            <shadowRemove>0</shadowRemove>" +
+            "        </BEH_GLOBAL_ALARM>" +
+            "        <BEH_RULE>" +
+            "            <ruleId>0</ruleId>" +
+            "            <ruleType>4</ruleType>" +
+            "            <focusedPart>0</focusedPart>" +
+            "            <alarmThreshold>10</alarmThreshold>" +
+            "            <outCntInitValue>0</outCntInitValue>" +
+            "            <inCntInitValue>0</inCntInitValue>" +
+            "            <autoClearFlag>1</autoClearFlag>" +
+            "            <lineNum>1</lineNum>" +
+            "            <line>" +
+            "                <lineType>1</lineType>" +
+            "                <direct>1</direct>" +
+            "                <point>0.25208,0.15926</point>" +
+            "                <!-- 相对坐标系 -->" +
+            "                <point>0.00000,0.38056</point>" +
+            "            </line>" +
+            "        </BEH_RULE>" +
+            "    </vcm_beh_alg_config>]]>" +
+            "</run_extradata>";
+
+    private final static String VCM_VIDEO_JSESSIONID_TOKEN = "VCM_VIDEO_JSESSIONID_TOKEN";
+
+    @Override
+    public String getValidToken() throws Exception {
+        BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(VCM_VIDEO_JSESSIONID_TOKEN);
+        String token = boundValueOperations.get();
+        if (StringUtils.isBlank(token) || !token.contains("JSESSIONID")) {
+            Map<String, Object> paramValues = new HashMap<>(2);
+            paramValues.put("account", "duijie");
+            paramValues.put("pwd", PASSWORD);
+            CloseableHttpResponse response = httpPost("/sdk_service/rest/users/login/v1.1", paramValues, null);
+            getApacheBodyXml(response);
+            String setCookie = getApacheHeader(response, "Set-Cookie");
+            //JSESSIONID=7F304CFA74C75A69DBC8F807A136529376F9F4BDFB2E8D94BA5231243BCAA6A4; Path=/; Secure; HttpOnly
+            log.info("Set-Cookie JSESSIONID:{}", setCookie);
+            if (StringUtils.isBlank(setCookie)) {
+                throw new ServiceException("COOKIE加载失败");
+            }
+            token = setCookie.split(";")[0];
+            log.info("cookie JSESSIONID:{}", token);
+            if (StringUtils.isNotBlank(token)) {
+                boundValueOperations.set(token, 30, TimeUnit.MINUTES);
+            }
+        }
+        return token;
+    }
+
+    @Override
+    public void refreshValidToken() throws Exception {
+        BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(VCM_VIDEO_JSESSIONID_TOKEN);
+        String cookie = boundValueOperations.get();
+        if (StringUtils.isBlank(cookie)) {
+            return;
+        }
+        CloseableHttpResponse response = httpPost("/common/keepAlive", Maps.newHashMap(), cookie);
+        JSONObject body = getApacheBody(response);
+        log.info("用户登录信息保活:{}", body.toJSONString());
+        if (StringUtils.isNotBlank(cookie)) {
+            boundValueOperations.set(cookie, 30, TimeUnit.MINUTES);
+        }
+    }
+
+    @Override
+    public void cameraList(Map<String, String> paramValues) throws Exception {
+        CloseableHttpResponse response = httpGet("/device/deviceList/v1.0?deviceType=32", paramValues, getValidToken());
+        JSONObject body = getApacheBody(response);
+        log.info("获取到的摄像机列表:{}", body.toJSONString());
+    }
+
+    @Override
+    public void startIntelligentAnalysis(String ids) throws Exception {
+//        Map<String, String> params = Maps.newHashMap();
+//        params.put("fromIndex", "1");
+//        params.put("toIndex", "100");
+//        JSONObject jsonObject = videoDockingService.loadDeviceList(params);
+//        JSONObject cameraBriefExInfos = jsonObject.getJSONObject("cameraBriefExInfos");
+//        JSONObject cameraBriefInfoExList = cameraBriefExInfos.getJSONObject("cameraBriefInfoExList");
+//        List<DeviceList> cameraBriefInfoExes = cameraBriefInfoExList.getJSONArray("cameraBriefInfoExes").toJavaList(DeviceList.class);
+        String builder = "<request>" +
+                "    <task_name>testPerson</task_name>" +
+                "    <type>0</type>" +
+                "    <camera_id>" + ids + "</camera_id>" +
+                "    <analyzeMode></analyzeMode>" +
+                "    <priority></priority>" +
+                "    <algorithms>" +
+                "       <algorithm></algorithm>" +
+                "    </algorithms>" +
+                "    <run_extradata>" +
+                "        <![CDATA[<vcm_beh_alg_config>" +
+                "    <BEH_GLOBAL_ALARM>" +
+                "        <minSizeWidth>0.01</minSizeWidth>" +
+                "        <minSizeHeight>0.01</minSizeHeight>" +
+                "        <maxSizeWidth>0.9</maxSizeWidth>" +
+                "        <maxSizeHeight>0.9</maxSizeHeight>" +
+                "        <detSensitivity>1</detSensitivity>" +
+                "        <backgroundUpdateRate>1</backgroundUpdateRate>" +
+                "        <shadowRemove>0</shadowRemove>" +
+                "    </BEH_GLOBAL_ALARM>" +
+                "    <BEH_RULE>" +
+                "        <ruleId>0</ruleId>" +
+                "        <ruleType>4</ruleType>" +
+                "        <focusedPart>0</focusedPart>" +
+                "        <alarmThreshold>10</alarmThreshold>" +
+                "        <outCntInitValue>0</outCntInitValue>" +
+                "        <inCntInitValue>0</inCntInitValue>" +
+                "        <autoClearFlag>1</autoClearFlag>" +
+                "        <lineNum>1</lineNum>" +
+                "        <line>" +
+                "            <lineType>1</lineType>" +
+                "            <direct>1</direct>" +
+                "            <point>0.25208,0.15926</point>" +
+                "            <point>0.00000,0.38056</point>" +
+                "        </line>" +
+                "    </BEH_RULE>" +
+                "</vcm_beh_alg_config>]]>" +
+                "    </run_extradata>" +
+                "</request>";
+        CloseableHttpResponse response = httpPostXml("/sdk_service/rest/video-analysis/start-intelligent-analysis/v1.1", builder, getValidToken());
+        Map<String, Object> body = getApacheBodyXml(response);
+        log.info("获取到的摄像机列表:{}", JSONObject.toJSONString(body));
+
+    }
+
+    /**
+     * 获取所有设备信息
+     *
+     * @param fromIndex   页数
+     * @param deviceLists 数据
+     * @return List<DeviceList>
+     * @throws Exception Exception
+     */
+    private List<DeviceList> loadAllDeviceList(int fromIndex, List<DeviceList> deviceLists) throws Exception {
+//        Map<String, String> params = Maps.newHashMap();
+//        params.put("fromIndex", String.valueOf((fromIndex - 1) * pageSize + 1));
+//        params.put("toIndex", String.valueOf(fromIndex * pageSize));
+//        JSONObject jsonObject = videoDockingService.loadDeviceList(params);
+//        JSONObject cameraBriefExInfos = jsonObject.getJSONObject("cameraBriefExInfos");
+//        JSONObject cameraBriefInfoExList = cameraBriefExInfos.getJSONObject("cameraBriefInfoExList");
+//        List<DeviceList> cameraBriefInfoExes = cameraBriefInfoExList.getJSONArray("cameraBriefInfoExes").toJavaList(DeviceList.class);
+//        if (cameraBriefInfoExes != null && cameraBriefInfoExes.size() > 0) {
+//            deviceLists.addAll(cameraBriefInfoExes);
+//            if (cameraBriefExInfos.size() == pageSize) {
+//                return loadAllDeviceList(fromIndex + 1, deviceLists);
+//            }
+//        }
+//        return deviceLists;
+        return null;
+    }
+
+    /**
+     * 统一POST请求方法
+     *
+     * @param uri         uri
+     * @param paramValues paramValues
+     * @return String
+     */
+    private static CloseableHttpResponse httpPost(String uri, Map<String, Object> paramValues, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
+        String url = String.format("%s" + uri, VCM_URI);
+        log.info("REQUEST URI:{},cooke:{}", url, cookie);
+//        url += "?" + URLEncoder.encode(postXForm(paramValues), "UTF-8");
+//        log.info("REQUEST getRequestGetUri URI:{}", url);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+        // 2. 创建POST请求
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
+        httpPost.setHeader("Connection", "keep-alive");
+        httpPost.setHeader("Accept", "text/plain;charset=utf-8");
+        httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpPost.setHeader("Sec-Fetch-Site", "same-origin");
+        httpPost.setHeader("Sec-Fetch-Mode", "cors");
+        httpPost.setHeader("Sec-Fetch-Dest", "empty");
+        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
+        if (StringUtils.isNotBlank(cookie)) {
+            httpPost.setHeader("Cookie", cookie);
+        }
+        httpPost.setConfig(defaultConfig);
+        if (paramValues != null && paramValues.size() > 0) {
+            String body = postXForm(paramValues);
+            log.info("http post body:{}", body);
+            httpPost.setEntity(new StringEntity(body));
+        }
+        return httpClient.execute(httpPost);
+    }
+
+    /**
+     * 统一GET请求方法
+     *
+     * @param uri         uri
+     * @param paramValues paramValues
+     * @return String
+     */
+    private static CloseableHttpResponse httpGet(String uri, Map<String, String> paramValues, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
+        String url = String.format("%s" + uri, VCM_URI);
+        log.info("REQUEST URI:{}", url);
+        url = getRequestGetUri(url, paramValues);
+        log.info("REQUEST getRequestGetUri URI:{}", url);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+        HttpGet httpGet = new HttpGet(url);
+        httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+        httpGet.setHeader("Connection", "keep-alive");
+        httpGet.setHeader("Accept", "application/json, text/plain, */*");
+        httpGet.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpGet.setHeader("Sec-Fetch-Site", "same-origin");
+        httpGet.setHeader("Sec-Fetch-Mode", "cors");
+        httpGet.setHeader("Sec-Fetch-Dest", "empty");
+        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
+        if (StringUtils.isNotBlank(cookie)) {
+            log.info("==============cookie=================={}", cookie);
+            httpGet.setHeader("Cookie", cookie);
+//            CookieStore cookieStore = new BasicCookieStore();
+//            //添加cookie
+//            BasicClientCookie basicClientCookie = new BasicClientCookie(cookies[0], cookies[1]);
+//            cookieStore.addCookie(basicClientCookie);
+        }
+
+        httpGet.setConfig(defaultConfig);
+        return httpClient.execute(httpGet);
+    }
+
+    /**
+     * 统一POST请求方法
+     *
+     * @param uri     uri
+     * @param postXml postXml
+     * @return String
+     */
+    public static CloseableHttpResponse httpPostXml(String uri, String postXml, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
+        String url = String.format("%s" + uri, VCM_URI);
+        log.info("REQUEST URI:{},cooke:{}", url, cookie);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+
+        // 2. 创建POST请求
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setHeader("Content-Type", "application/xml;charset=UTF-8");
+        httpPost.setHeader("Connection", "keep-alive");
+        httpPost.setHeader("Accept", "text/plain;charset=UTF-8");
+        httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpPost.setHeader("Sec-Fetch-Site", "same-origin");
+        httpPost.setHeader("Sec-Fetch-Mode", "cors");
+        httpPost.setHeader("Sec-Fetch-Dest", "empty");
+        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
+        if (StringUtils.isNotBlank(cookie)) {
+            httpPost.setHeader("Cookie", cookie);
+        }
+        httpPost.setConfig(defaultConfig);
+        if (StringUtils.isNotBlank(postXml)) {
+            log.info("http post body:{}", postXml);
+            httpPost.setEntity(new StringEntity(postXml));
+        }
+        return httpClient.execute(httpPost);
+    }
+
+    /**
+     * 统一DELETE请求方法
+     *
+     * @param uri         uri
+     * @param paramValues paramValues
+     * @return String
+     */
+    public static CloseableHttpResponse httpDelete(String uri, Map<String, String> paramValues, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
+        String url = String.format("%s" + uri, VCM_URI);
+        log.info("REQUEST URI:{},cooke:{}", url, cookie);
+        url = getRequestGetUri(url, paramValues);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+
+        // 2. 创建POST请求
+        HttpDelete httpDelete = new HttpDelete(url);
+        httpDelete.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
+        httpDelete.setHeader("Connection", "keep-alive");
+        httpDelete.setHeader("Accept", "application/json, text/plain, */*");
+        httpDelete.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpDelete.setHeader("Sec-Fetch-Site", "same-origin");
+        httpDelete.setHeader("Sec-Fetch-Mode", "cors");
+        httpDelete.setHeader("Sec-Fetch-Dest", "empty");
+        httpDelete.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
+        if (StringUtils.isNotBlank(cookie)) {
+            httpDelete.setHeader("Cookie", cookie);
+        }
+        httpDelete.setConfig(defaultConfig);
+        return httpClient.execute(httpDelete);
+    }
+}

+ 188 - 71
src/main/java/com/zhiqiyun/open/core/video/impl/VideoDockingServiceImpl.java

@@ -1,29 +1,33 @@
 package com.zhiqiyun.open.core.video.impl;
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Maps;
 import com.zhiqiyun.open.core.video.VideoDockingService;
 import com.zhiqiyun.open.exception.ServiceException;
 import lombok.extern.slf4j.Slf4j;
-import okhttp3.*;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.assertj.core.util.Lists;
 import org.springframework.data.redis.core.BoundValueOperations;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import javax.net.ssl.*;
 import java.io.IOException;
-import java.rmi.ServerException;
-import java.security.SecureRandom;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
+import static com.zhiqiyun.open.camera.config.CameraConfig.*;
+
 /**
  * @author xin yang
  * @date 2022/8/4
@@ -32,37 +36,36 @@ import java.util.concurrent.TimeUnit;
 @Service
 public class VideoDockingServiceImpl implements VideoDockingService {
 
-    private final static String USER_NAME = "duijie";
-    private final static String PASSWORD = "Huawei@12#$";
-    private final static String URI = "https://172.169.12.21:18531";
-    private final static String RESULT_CODE = "resultCode";
+    //    public final static String URI = "http://222.84.250.39:9800";
+    public final static String URI = "https://172.169.12.21:18531";
 
-    private final static String VIDEO_JSESSIONID_TOKEN = "VIDEO_JSESSIONID_TOKEN";
+    public final static String VIDEO_JSESSIONID_TOKEN = "VIDEO_JSESSIONID_TOKEN";
 
     @Resource
     private RedisTemplate<String, String> redisTemplate;
 
     @Resource
-    private OkHttpClient okHttpClient;
+    private ThreadPoolTaskExecutor taskExecutor;
 
     @Override
-    public String getValidToken() throws IOException {
+    public String getValidToken() throws Exception {
         BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(VIDEO_JSESSIONID_TOKEN);
         String token = boundValueOperations.get();
-        if (StringUtils.isBlank(token)) {
-            Map<String, String> paramValues = new HashMap<>(2);
+        if (StringUtils.isBlank(token) || !token.contains("JSESSIONID")) {
+            Map<String, Object> paramValues = new HashMap<>(2);
             paramValues.put("userName", USER_NAME);
             paramValues.put("password", PASSWORD);
 
-            Response response = okHttpPost("/loginInfo/login/v1.0", paramValues);
-            log.info(JSONObject.toJSONString(response));
-//            analysisRequestSuccess(response);
-            List<String> cookies = response.headers().values("Set-Cookie");
-            cookies.forEach(s -> {
-                log.info("name:{}", s);
-            });
-            token = response.header("Set-Cookie");
-            log.info("用户登录TOKEN:{}", token);
+            CloseableHttpResponse response = httpPost("/loginInfo/login/v1.0", paramValues, null);
+            getApacheBody(response);
+            String setCookie = getApacheHeader(response, "Set-Cookie");
+            //JSESSIONID=7F304CFA74C75A69DBC8F807A136529376F9F4BDFB2E8D94BA5231243BCAA6A4; Path=/; Secure; HttpOnly
+            log.info("Set-Cookie JSESSIONID:{}", setCookie);
+            if (StringUtils.isBlank(setCookie)) {
+                throw new ServiceException("COOKIE加载失败");
+            }
+            token = setCookie.split(";")[0];
+            log.info("cookie JSESSIONID:{}", token);
             if (StringUtils.isNotBlank(token)) {
                 boundValueOperations.set(token, 30, TimeUnit.MINUTES);
             }
@@ -71,20 +74,115 @@ public class VideoDockingServiceImpl implements VideoDockingService {
     }
 
     @Override
-    public void refreshValidToken() throws IOException {
-        Map<String, String> paramValues = new HashMap<>(1);
-        paramValues.put("Cookie", getValidToken());
-        Response response = okHttpPost("/common/keepAlive", paramValues);
-        analysisRequestSuccess(response);
+    public void refreshValidToken() throws Exception {
+        BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(VIDEO_JSESSIONID_TOKEN);
+        String cookie = boundValueOperations.get();
+        if (StringUtils.isBlank(cookie)) {
+            return;
+        }
+        CloseableHttpResponse response = httpPost("/common/keepAlive", Maps.newHashMap(), cookie);
+        JSONObject body = getApacheBody(response);
+        log.info("用户登录信息保活:{}", body.toJSONString());
+        if (StringUtils.isNotBlank(cookie)) {
+            boundValueOperations.set(cookie, 30, TimeUnit.MINUTES);
+        }
+    }
+
+    @Override
+    public JSONObject loadDeviceList(Map<String, String> paramValues) throws Exception {
+        paramValues.put("deviceType", "33");
+        CloseableHttpResponse response = httpGet("/device/deviceList/v1.0", paramValues, getValidToken());
+        JSONObject body = getApacheBody(response);
+        log.info("获取到的设备列表:{}", body.toJSONString());
+        return body;
     }
 
     @Override
-    public void loadDeviceList() throws IOException {
-        Map<String, String> paramValues = new HashMap<>(1);
-        paramValues.put("Cookie", getValidToken());
-        Response response = okHttpPost("/device/deviceList/v1.0?deviceType=33&fromIndex=1&toIndex=10", paramValues);
-        analysisRequestSuccess(response);
-        log.info("获取到的设备列表:" + Objects.requireNonNull(response.body()).string());
+    public JSONObject loadVideoRtspUrl(String cameraCode) throws Exception {
+        Map<String, Object> paramValues = Maps.newHashMap();
+        paramValues.put("cameraCode", cameraCode);
+        Map<String, Object> mediaURLParam = Maps.newHashMap();
+        mediaURLParam.put("broadCastType", 0);
+        mediaURLParam.put("packProtocolType", 1);
+        mediaURLParam.put("protocolType", 2);
+        mediaURLParam.put("serviceType", 1);
+        mediaURLParam.put("streamType", 1);
+        mediaURLParam.put("transMode", 0);
+        mediaURLParam.put("clientType", 5);
+        paramValues.put("mediaURLParam", mediaURLParam);
+        CloseableHttpResponse response = httpPost("/video/rtspurl/v1.0", paramValues, getValidToken());
+        return getApacheBody(response);
+    }
+
+    @Override
+    public void startRealPlayByIpex(String cameraCode) throws Exception {
+        Map<String, Object> paramValues = Maps.newHashMap();
+        paramValues.put("cameraCode", cameraCode);
+        Map<String, Object> realplayParam = Maps.newHashMap();
+        realplayParam.put("streamType", 1);
+        realplayParam.put("protocolType", 1);
+        realplayParam.put("directFirst", 0);
+        realplayParam.put("multiCast", 0);
+        paramValues.put("realplayParam", realplayParam);
+        Map<String, Object> mediaAddrDst = Maps.newHashMap();
+        Map<String, Object> dstIP = Maps.newHashMap();
+        dstIP.put("ipType", 0);
+        dstIP.put("ip", "192.168.0.15");
+        mediaAddrDst.put("dstIP", dstIP);
+        mediaAddrDst.put("audioPort", "40000");
+        mediaAddrDst.put("videoPort", "40000");
+        mediaAddrDst.put("reserve", cameraCode);
+        paramValues.put("mediaAddrDst", mediaAddrDst);
+        paramValues.put("clientType", 1);
+        CloseableHttpResponse response = httpPost("/device/startrealplaybyipex", paramValues, getValidToken());
+        getApacheBody(response);
+    }
+
+    @Override
+    public void startIntelligentDataSubscribes(String ids) {
+        taskExecutor.execute(() -> {
+            try {
+                Map<String, Object> params = Maps.newHashMap();
+                Map<String, Object> subscribeObject = Maps.newHashMap();
+                List<Map<String, String>> list = Lists.newArrayList();
+                Map<String, String> data = new HashMap<>(10);
+                data.put("Title", "实时智能数据订阅接口");
+                data.put("SubscribeDetail", "0");
+                data.put("ResourceClass", "1");
+                data.put("ResourceURI", ids);
+                data.put("ApplicantName", "yzx22");
+                data.put("ApplicantOrg", "hw");
+                data.put("BeginTime", "20210801100000");
+                data.put("EndTime", "20210830094059");
+                data.put("ReceiveAddr", "http://171.104.234.207:9800");
+                data.put("Reason", "hw");
+                data.put("OperateType", "0");
+                data.put("SubscribeStatus", "0");
+                data.put("ResultImageDeclare", "-1");
+                data.put("ResultFeatureDeclare", "-1");
+                data.put("SubscribeDataType", "1");
+                data.put("DeviceCodeType", "0");
+                list.add(data);
+                subscribeObject.put("SubscribeObject", list);
+                params.put("SubscribeListObject", subscribeObject);
+
+                log.info("添加实时智能数据订阅 params:{}", JSONObject.toJSONString(params));
+                CloseableHttpResponse response = httpPost("/IntelligentData/Subscribes", params, getValidToken());
+                JSONObject body = getApacheBody(response);
+                log.info("添加实时智能数据订阅:{}", body.toJSONString());
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        });
+    }
+
+    @Override
+    public void deleteIntelligentDataSubscribes(String ids) throws Exception {
+//        Map<String, String> params = Maps.newHashMap();
+//        params.put("IDList", ids);
+//        CloseableHttpResponse response = httpDelete("/IntelligentData/Subscribes", params, getValidToken());
+//        JSONObject body = getApacheBody(response);
+//        log.info("删除实时智能数据订阅:{}", body.toJSONString());
     }
 
     /**
@@ -94,49 +192,68 @@ public class VideoDockingServiceImpl implements VideoDockingService {
      * @param paramValues paramValues
      * @return String
      */
-    private Response okHttpPost(String uri, Map<String, String> paramValues) throws IOException {
-        String cookie = paramValues.get("Cookie");
-        if (StringUtils.isNotBlank(cookie)) {
-            paramValues.remove("Cookie");
-        }
-        FormBody.Builder formBuilder = new FormBody.Builder();
-        paramValues.forEach(formBuilder::add);
-
-        Request.Builder builder = new Request.Builder();
+    private static CloseableHttpResponse httpPost(String uri, Map<String, Object> paramValues, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
         String url = String.format("%s" + uri, URI);
-        log.info("URL:{}", url);
-        builder.url(url);
-        builder.header("Content-Type", "application/json;charset=UTF-8");
+        log.info("REQUEST URI:{},cooke:{}", url, cookie);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+
+        // 2. 创建POST请求
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
+        httpPost.setHeader("Connection", "keep-alive");
+        httpPost.setHeader("Accept", "application/json, text/plain, */*");
+        httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpPost.setHeader("Sec-Fetch-Site", "same-origin");
+        httpPost.setHeader("Sec-Fetch-Mode", "cors");
+        httpPost.setHeader("Sec-Fetch-Dest", "empty");
+        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
         if (StringUtils.isNotBlank(cookie)) {
-            builder.header("Cookie", cookie);
+            httpPost.setHeader("Cookie", cookie);
         }
-        builder.post(formBuilder.build());
-        Response response = this.okHttpClient.newCall(builder.build()).execute();
-        if (response.isSuccessful()) {
-            return response;
-        } else {
-            try {
-                String result = Objects.requireNonNull(response.body()).string();
-                log.error(result);
-                JSONObject jsonObject = JSON.parseObject(result);
-                throw new ServiceException(jsonObject.getString("error_description"));
-            } catch (Exception e) {
-                log.error("okHttpPost error:" + e.getMessage(), e);
-                throw new ServiceException("网络异常" + response.message());
-            }
+        httpPost.setConfig(defaultConfig);
+        if (paramValues != null && paramValues.size() > 0) {
+            log.info("http post body:", paramValues);
+            httpPost.setEntity(new StringEntity(JSONObject.toJSONString(paramValues)));
         }
+        return httpClient.execute(httpPost);
     }
 
     /**
-     * 解析请求是否成功
+     * 统一GET请求方法
      *
-     * @param response response
+     * @param uri         uri
+     * @param paramValues paramValues
+     * @return String
      */
-    private void analysisRequestSuccess(Response response) throws IOException {
-        String result = Objects.requireNonNull(response.body()).string();
-        JSONObject jsonObject = JSON.parseObject(result);
-        if (0 != jsonObject.getIntValue(RESULT_CODE)) {
-            throw new ServerException(jsonObject.getString(""));
+    private static CloseableHttpResponse httpGet(String uri, Map<String, String> paramValues, String cookie) throws IOException {
+        // 1. 创建HttpClient
+        CloseableHttpClient httpClient = createSSLClientDefault();
+        String url = String.format("%s" + uri, URI);
+        log.info("REQUEST URI:{}", url);
+        url = getRequestGetUri(url, paramValues);
+        log.info("REQUEST getRequestGetUri URI:{}", url);
+        RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
+        HttpGet httpGet = new HttpGet(url);
+        httpGet.setHeader("Content-Type", "application/json;charset=UTF-8");
+        httpGet.setHeader("Connection", "keep-alive");
+        httpGet.setHeader("Accept", "application/json, text/plain, */*");
+        httpGet.setHeader("Accept-Encoding", "gzip, deflate, br");
+        httpGet.setHeader("Sec-Fetch-Site", "same-origin");
+        httpGet.setHeader("Sec-Fetch-Mode", "cors");
+        httpGet.setHeader("Sec-Fetch-Dest", "empty");
+        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
+        if (StringUtils.isNotBlank(cookie)) {
+            log.info("==============cookie=================={}", cookie);
+            httpGet.setHeader("Cookie", cookie);
+//            CookieStore cookieStore = new BasicCookieStore();
+//            //添加cookie
+//            BasicClientCookie basicClientCookie = new BasicClientCookie(cookies[0], cookies[1]);
+//            cookieStore.addCookie(basicClientCookie);
         }
+
+        httpGet.setConfig(defaultConfig);
+        return httpClient.execute(httpGet);
     }
 }

+ 1 - 1
src/main/java/com/zhiqiyun/open/mvc/controller/BzController.java

@@ -45,7 +45,7 @@ public class BzController {
     @GetMapping("/testLoadVideo")
     public Result testLoadVideo() throws IOException {
         try {
-            videoDockingService.loadDeviceList();
+            videoDockingService.refreshValidToken();
         } catch (Exception e) {
             log.error("BzController error:" + e.getMessage(), e);
         }

+ 56 - 0
src/main/java/com/zhiqiyun/open/mvc/controller/HwVideoController.java

@@ -0,0 +1,56 @@
+package com.zhiqiyun.open.mvc.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Maps;
+import com.zhiqiyun.open.annotation.Permission;
+import com.zhiqiyun.open.core.models.hwVideo.DeviceList;
+import com.zhiqiyun.open.core.video.VideoDockingService;
+import com.zhiqiyun.open.mvc.Result;
+import com.zhiqiyun.open.mvc.params.QueryPageParams;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 华为视频接口登录问题
+ *
+ * @author NINGMEI
+ */
+@RestController
+@RequestMapping("/hw/video/")
+public class HwVideoController {
+
+    @Resource
+    private VideoDockingService videoDockingService;
+
+    @PostMapping("findPage")
+    @Permission(value = "hw.video.find", tags = "查询子设备列表")
+    public Result page(@RequestBody QueryPageParams pageParams) throws Exception {
+        Map<String, String> params = Maps.newHashMap();
+        params.put("fromIndex", String.valueOf((pageParams.getCurrent() - 1) * 10 + 1));
+        params.put("toIndex", String.valueOf(pageParams.getCurrent() * 10));
+        JSONObject jsonObject = videoDockingService.loadDeviceList(params);
+        JSONObject cameraBriefExInfos = jsonObject.getJSONObject("cameraBriefExInfos");
+
+        int total = cameraBriefExInfos.getIntValue("total");
+
+        JSONObject cameraBriefInfoExList = jsonObject.getJSONObject("cameraBriefInfoExList");
+        List<DeviceList> cameraBriefInfoExes = cameraBriefInfoExList.getJSONArray("cameraBriefInfoExes")
+                .toJavaList(DeviceList.class);
+
+        Page<DeviceList> resultData = pageParams.getPage();
+        resultData.setTotal(total);
+        resultData.setRecords(cameraBriefInfoExes);
+        return Result.instance(Result.Code.SUCCESS).setData(resultData);
+    }
+
+    @Permission(value = "hw.video.url.find", tags = "获取视频URL信息")
+    @PostMapping("/rtspUrl")
+    public Result loadVideoRtspUrl(@RequestBody String cameraCode) throws Exception {
+        JSONObject jsonObject = videoDockingService.loadVideoRtspUrl(cameraCode);
+        return Result.instance(Result.Code.SUCCESS).setData(jsonObject.getString("rtspURL"));
+    }
+}

+ 44 - 0
src/main/java/com/zhiqiyun/open/mvc/controller/RtspFlvPlayController.java

@@ -0,0 +1,44 @@
+package com.zhiqiyun.open.mvc.controller;
+
+import com.zhiqiyun.open.core.video.VideoAnalysisService;
+import com.zhiqiyun.open.core.video.VideoDockingService;
+import com.zhiqiyun.open.utils.ServletContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * @author xin yang
+ * @date 2022/10/12
+ */
+@Slf4j
+@Controller
+@RequestMapping("/IntelligentData")
+public class RtspFlvPlayController {
+
+    @Resource
+    private VideoAnalysisService videoAnalysisService;
+
+    @Resource
+    private VideoDockingService videoDockingService;
+
+    @Resource
+    private ThreadPoolTaskExecutor taskExecutor;
+
+    @RequestMapping("/SubscribeNotifications")
+    public void subscribeNotifications(HttpServletRequest request) throws IOException {
+        log.info("======================推送1400数据和智能元数据=========================");
+        log.info("=======body:{}", ServletContext.getBody(request.getInputStream()));
+    }
+
+    @RequestMapping("/DispositionNotifications")
+    public void dispositionNotifications(HttpServletRequest request) throws IOException {
+        log.info("======================推送智能告警数据=========================");
+        log.info("=======body:{}", ServletContext.getBody(request.getInputStream()));
+    }
+}

+ 2 - 2
src/main/java/com/zhiqiyun/open/router/apis/CategoryApi.java

@@ -37,8 +37,8 @@ public class CategoryApi {
     @Autowired
     private SequenceService sequenceService;
 
-    @ServiceMethod(method = "list.category.occupation", title = "查询文旅岗位目录")
-    public OapResponse listCategoryOccupation(CategoryOccupationRequest request) {
+    @ServiceMethod(method = "list.category.occupation", title = "查询子设备列表")
+    public OapResponse loadDeviceList(CategoryOccupationRequest request) {
         QueryWrapper<CategoryOccupation> queryWrapper = new QueryWrapper<>();
 
         if (StringUtils.isNotBlank(request.getName())) {

+ 94 - 0
src/main/java/com/zhiqiyun/open/router/apis/HwVideoApi.java

@@ -0,0 +1,94 @@
+package com.zhiqiyun.open.router.apis;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.dliyun.oap.framework.annotation.ServiceMethod;
+import com.dliyun.oap.framework.annotation.ServiceMethodBean;
+import com.dliyun.oap.framework.response.OapResponse;
+import com.google.common.collect.Maps;
+import com.zhiqiyun.open.camera.rtsp.RtspPushSrs;
+import com.zhiqiyun.open.camera.rtsp.RtspTransfer;
+import com.zhiqiyun.open.core.models.hwVideo.DeviceList;
+import com.zhiqiyun.open.core.video.VideoDockingService;
+import com.zhiqiyun.open.router.request.hwVideo.LoadVideoPageRequest;
+import com.zhiqiyun.open.router.request.hwVideo.VideoRtsUrlRequest;
+import com.zhiqiyun.open.router.request.hwVideo.VideoRtsUrlResponse;
+import com.zhiqiyun.open.router.request.hwVideo.VideoStopPushRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author NINGMEI
+ */
+@Slf4j
+@ServiceMethodBean
+public class HwVideoApi {
+
+    @Autowired
+    private VideoDockingService videoDockingService;
+
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
+
+    @ServiceMethod(method = "hw.video.find", title = "查询子设备列表")
+    public OapResponse loadDeviceList(LoadVideoPageRequest request) throws Exception {
+        try {
+            Map<String, String> params = Maps.newHashMap();
+            params.put("fromIndex", String.valueOf((request.getPage() - 1) * request.getLimit() + 1));
+            params.put("toIndex", String.valueOf(request.getPage() * request.getLimit()));
+            JSONObject jsonObject = videoDockingService.loadDeviceList(params);
+            JSONObject cameraBriefExInfos = jsonObject.getJSONObject("cameraBriefExInfos");
+            int total = cameraBriefExInfos.getIntValue("total");
+            JSONObject cameraBriefInfoExList = cameraBriefExInfos.getJSONObject("cameraBriefInfoExList");
+            List<DeviceList> cameraBriefInfoExes = cameraBriefInfoExList.getJSONArray("cameraBriefInfoExes").toJavaList(DeviceList.class);
+            Page<DeviceList> resultData = new Page<>(request.getPage(), request.getLimit(), total);
+            resultData.setRecords(cameraBriefInfoExes);
+            return OapResponse.success().setBody(resultData);
+        } catch (Exception ex) {
+            log.error(ex.getMessage(), ex);
+        }
+        return OapResponse.success();
+    }
+
+    @ServiceMethod(method = "hw.video.url.find", title = "获取视频URL信息")
+    public OapResponse loadVideoRtspUrl(@Valid VideoRtsUrlRequest request) throws Exception {
+        VideoRtsUrlResponse response = new VideoRtsUrlResponse();
+        JSONObject jsonObject = videoDockingService.loadVideoRtspUrl(request.getCameraCode());
+        String rtspUri = jsonObject.getString("rtspURL");
+        log.info("获取到监控rtspURI:{}", rtspUri);
+        if (StringUtils.isBlank(rtspUri)) return OapResponse.fail("ERROR", "视频流地址获取失败");
+        String rtspId = request.getCameraCode().split("#")[0];
+        RtspTransfer.createRtspId(rtspUri, rtspId);
+        response.setRtspId(rtspId);
+        log.info("RtspTransfer.STREAM_MAP:{}", RtspTransfer.STREAM_MAP.get(rtspId));
+        if (StringUtils.isNotBlank(RtspTransfer.STREAM_MAP.get(rtspId))) {
+            response.setRtspUri(RtspTransfer.STREAM_MAP.get(rtspId));
+        } else {
+            rtspUri = String.format(RtspPushSrs.SRS_PUSH_ADDRESS, rtspId);
+            log.info("重新进行流推送:{}", rtspUri);
+            taskExecutor.execute(() -> {
+                try {
+                    RtspTransfer.startPushRtmp(rtspId);
+                } catch (Exception e) {
+                    log.info("============停止推流=======================");
+                    RtspTransfer.removeRtsp(rtspId);
+                }
+            });
+            RtspTransfer.STREAM_MAP.put(rtspId, rtspUri);
+            response.setRtspUri(rtspUri);
+        }
+        return OapResponse.success().setBody(response);
+    }
+
+    @ServiceMethod(method = "hw.video.live.stop", title = "停止视频推流工作")
+    public OapResponse videoStopPush(VideoStopPushRequest request) throws Exception {
+        RtspTransfer.removeRtsp(request.getRtspId());
+        return OapResponse.success();
+    }
+}

+ 24 - 0
src/main/java/com/zhiqiyun/open/router/request/hwVideo/LoadVideoPageRequest.java

@@ -0,0 +1,24 @@
+package com.zhiqiyun.open.router.request.hwVideo;
+
+import com.dliyun.oap.framework.annotation.ServiceParamField;
+import com.dliyun.oap.framework.request.AbstractOapRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author NINGMEI
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LoadVideoPageRequest extends AbstractOapRequest {
+
+	@NotNull
+	@ServiceParamField(describe = "页数")
+	private Integer page = 1;
+
+	@NotNull
+	@ServiceParamField(describe = "每页条数")
+	private Integer limit = 10;
+}

+ 21 - 0
src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoRtsUrlRequest.java

@@ -0,0 +1,21 @@
+package com.zhiqiyun.open.router.request.hwVideo;
+
+import com.dliyun.oap.framework.annotation.ServiceParamField;
+import com.dliyun.oap.framework.request.AbstractOapRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author NINGMEI
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class VideoRtsUrlRequest extends AbstractOapRequest {
+
+	@NotBlank
+	@ServiceParamField(describe = "实时浏览或录像的摄像机编码")
+	private String cameraCode;
+}

+ 22 - 0
src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoRtsUrlResponse.java

@@ -0,0 +1,22 @@
+package com.zhiqiyun.open.router.request.hwVideo;
+
+import com.dliyun.oap.framework.annotation.ServiceParamField;
+import com.dliyun.oap.framework.request.AbstractOapRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author NINGMEI
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class VideoRtsUrlResponse extends AbstractOapRequest {
+
+	@ServiceParamField(describe = "播放流地址")
+	private String rtspUri;
+
+	@ServiceParamField(describe = "播放流ID")
+	private String rtspId;
+}

+ 20 - 0
src/main/java/com/zhiqiyun/open/router/request/hwVideo/VideoStopPushRequest.java

@@ -0,0 +1,20 @@
+package com.zhiqiyun.open.router.request.hwVideo;
+
+import com.dliyun.oap.framework.annotation.ServiceParamField;
+import com.dliyun.oap.framework.request.AbstractOapRequest;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author NINGMEI
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class VideoStopPushRequest extends AbstractOapRequest {
+
+    @NotBlank
+    @ServiceParamField(describe = "实时浏览视频流ID")
+    private String rtspId;
+}

+ 22 - 0
src/main/java/com/zhiqiyun/open/utils/ServletContext.java

@@ -4,6 +4,9 @@ import org.apache.commons.lang3.StringUtils;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
@@ -183,6 +186,25 @@ public class ServletContext {
         return StringUtils.contains(contentType, "application/json");
     }
 
+    /**
+     * 获取请求body数据
+     *
+     * @param inStream
+     * @return
+     * @throws IOException
+     */
+    public static String getBody(InputStream inStream) throws IOException {
+        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len = 0;
+        while ((len = inStream.read(buffer)) != -1) {
+            outSteam.write(buffer, 0, len);
+        }
+        outSteam.close();
+        inStream.close();
+        return outSteam.toString("utf-8");
+    }
+
     private Map<String, Object> getContext() {
         return context;
     }