From e48fa711a3664bece9b3e58840a75fe7c05bc47c Mon Sep 17 00:00:00 2001
From: panlinlin <648540858@qq.com>
Date: 星期六, 08 五月 2021 17:14:05 +0800
Subject: [PATCH] 添加截图(快照)功能

---
 web_src/src/components/Login.vue                                              |    2 
 web_src/config/index.js                                                       |    7 +
 web_src/src/components/channelList.vue                                        |   69 +++++++++++++----
 src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java         |   62 ++++++++++++++-
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java            |   59 ++++++++++++++
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java |    2 
 web_src/src/components/test.vue                                               |   30 +++++++
 7 files changed, 208 insertions(+), 23 deletions(-)

diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index b41ef6d..c8aa245 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -12,7 +12,7 @@
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
-import java.io.IOException;
+import java.io.*;
 import java.net.ConnectException;
 import java.util.HashMap;
 import java.util.Map;
@@ -25,6 +25,8 @@
 
     @Autowired
     private MediaConfig mediaConfig;
+
+
 
     public interface RequestCallback{
         void run(JSONObject response);
@@ -94,6 +96,53 @@
 
         return responseJSON;
     }
+
+
+    public void sendPostForImg(String api, Map<String, Object> param, String targetPath, String fileName) {
+        OkHttpClient client = new OkHttpClient();
+        String url = String.format("http://%s:%s/index/api/%s",  mediaConfig.getIp(), mediaConfig.getHttpPort(), api);
+        JSONObject responseJSON = null;
+        logger.debug(url);
+
+        FormBody.Builder builder = new FormBody.Builder();
+        builder.add("secret",mediaConfig.getSecret());
+        if (param != null && param.keySet().size() > 0) {
+            for (String key : param.keySet()){
+                if (param.get(key) != null) {
+                    builder.add(key, param.get(key).toString());
+                }
+            }
+        }
+
+        FormBody body = builder.build();
+
+        Request request = new Request.Builder()
+                .post(body)
+                .url(url)
+                .build();
+        try {
+            Response response = client.newCall(request).execute();
+            if (response.isSuccessful()) {
+                if (targetPath != null) {
+                    File snapFolder = new File(targetPath);
+                    if (!snapFolder.exists()) {
+                        snapFolder.mkdirs();
+                    }
+                    File snapFile = new File(targetPath + "/" + fileName);
+                    FileOutputStream outStream = new FileOutputStream(snapFile);
+                    outStream.write(response.body().bytes());
+                    outStream.close();
+                }
+            }
+        } catch (ConnectException e) {
+            logger.error(String.format("杩炴帴ZLM澶辫触: %s, %s", e.getCause().getMessage(), e.getMessage()));
+            logger.info("璇锋鏌edia閰嶇疆骞剁‘璁LM宸插惎鍔�...");
+        }catch (IOException e) {
+            logger.error(String.format("[ %s ]璇锋眰澶辫触: %s", url, e.getMessage()));
+        }
+
+    }
+
 
     public JSONObject getMediaList(String app, String stream, String schema, RequestCallback callback){
         Map<String, Object> param = new HashMap<>();
@@ -201,4 +250,12 @@
         param.put("local_port", localPortSStr);
         sendPost("kick_sessions",param, null);
     }
+
+    public void getSnap(String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("url", flvUrl);
+        param.put("timeout_sec", timeout_sec);
+        param.put("expire_sec", expire_sec);
+        sendPostForImg("getSnap",param, targetPath, fileName);
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
index 42c10a7..49412ff 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -15,6 +15,7 @@
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
 import com.genersoft.iot.vmp.service.IMediaService;
 import com.genersoft.iot.vmp.service.IPlayService;
@@ -23,14 +24,18 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
+import org.springframework.util.ResourceUtils;
 import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.sip.ClientTransaction;
 import javax.sip.Dialog;
 import javax.sip.header.CallIdHeader;
 import javax.sip.message.Response;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.UUID;
 
 @Service
@@ -82,8 +87,32 @@
             cmder.closeRTPServer(playResult.getDevice(), channelId);
             RequestMessage msg = new RequestMessage();
             msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid());
-            msg.setData("Timeout");
+            WVPResult wvpResult = new WVPResult();
+            wvpResult.setCode(-1);
+            wvpResult.setMsg("Timeout");
+            msg.setData(wvpResult);
             resultHolder.invokeResult(msg);
+        });
+        result.onCompletion(()->{
+            // 鐐规挱缁撴潫鏃惰皟鐢ㄦ埅鍥炬帴鍙�
+            try {
+                String path = ResourceUtils.getURL("classpath:").getPath()+"static/static/snap/";
+                String fileName =  deviceId + "_" + channelId + ".jpg";
+                ResponseEntity responseEntity =  (ResponseEntity)result.getResult();
+                if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
+                    WVPResult wvpResult = (WVPResult)responseEntity.getBody();
+                    if (wvpResult.getCode() == 0) {
+                        StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
+                        String flvUrl = streamInfoForSuccess.getFlv();
+                        // 璇锋眰鎴浘
+                        zlmresTfulUtils.getSnap(flvUrl, 5, 1, path, fileName);
+                    }
+                }
+
+                System.out.println(path);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            }
         });
         if (streamInfo == null) {
             // 鍙戦�佺偣鎾秷鎭�
@@ -98,7 +127,10 @@
                 msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
                 Response response = event.getResponse();
                 cmder.closeRTPServer(playResult.getDevice(), channelId);
-                msg.setData(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+                WVPResult wvpResult = new WVPResult();
+                wvpResult.setCode(-1);
+                wvpResult.setMsg(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+                msg.setData(wvpResult);
                 resultHolder.invokeResult(msg);
                 if (errorEvent != null) {
                     errorEvent.response(event);
@@ -109,7 +141,10 @@
             if (streamId == null) {
                 RequestMessage msg = new RequestMessage();
                 msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
-                msg.setData(String.format("鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null"));
+                WVPResult wvpResult = new WVPResult();
+                wvpResult.setCode(-1);
+                wvpResult.setMsg(String.format("鐐规挱澶辫触锛� redis缂撳瓨streamId绛変簬null"));
+                msg.setData(wvpResult);
                 resultHolder.invokeResult(msg);
                 return playResult;
             }
@@ -117,7 +152,13 @@
             if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
                 RequestMessage msg = new RequestMessage();
                 msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
-                msg.setData(JSON.toJSONString(streamInfo));
+
+                WVPResult wvpResult = new WVPResult();
+                wvpResult.setCode(0);
+                wvpResult.setMsg("success");
+                wvpResult.setData(streamInfo);
+                msg.setData(wvpResult);
+
                 resultHolder.invokeResult(msg);
                 if (hookEvent != null) {
                     hookEvent.response(JSONObject.parseObject(JSON.toJSONString(streamInfo)));
@@ -133,7 +174,11 @@
                     RequestMessage msg = new RequestMessage();
                     msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
                     Response response = event.getResponse();
-                    msg.setData(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+
+                    WVPResult wvpResult = new WVPResult();
+                    wvpResult.setCode(-1);
+                    wvpResult.setMsg(String.format("鐐规挱澶辫触锛� 閿欒鐮侊細 %s, %s", response.getStatusCode(), response.getReasonPhrase()));
+                    msg.setData(wvpResult);
                     resultHolder.invokeResult(msg);
                 });
             }
@@ -163,6 +208,13 @@
             streamInfo.setTransactionInfo(transactionInfo);
             redisCatchStorage.startPlay(streamInfo);
             msg.setData(JSON.toJSONString(streamInfo));
+
+            WVPResult wvpResult = new WVPResult();
+            wvpResult.setCode(0);
+            wvpResult.setMsg("sucess");
+            wvpResult.setData(streamInfo);
+            msg.setData(wvpResult);
+
             resultHolder.invokeResult(msg);
         } else {
             logger.warn("璁惧棰勮API璋冪敤澶辫触锛�");
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
index dce0732..5f44e76 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -19,6 +19,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.util.ResourceUtils;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -31,6 +32,7 @@
 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
 import org.springframework.web.context.request.async.DeferredResult;
 
+import java.io.FileNotFoundException;
 import java.util.UUID;
 
 import javax.sip.message.Response;
diff --git a/web_src/config/index.js b/web_src/config/index.js
index 5ab5eac..cec91b8 100644
--- a/web_src/config/index.js
+++ b/web_src/config/index.js
@@ -18,6 +18,13 @@
           '^/debug': '/'
         }
       },
+      '/static/snap': {
+        target: 'http://localhost:18080',
+        changeOrigin: true,
+        // pathRewrite: {
+        //   '^/static/snap': '/static/snap'
+        // }
+      },
 
     },
 
diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue
index a507f59..7aff1ae 100644
--- a/web_src/src/components/Login.vue
+++ b/web_src/src/components/Login.vue
@@ -80,7 +80,7 @@
 
       this.$axios({
       	method: 'get',
-	url:"/api/user/login",
+	      url:"/api/user/login",
         params: loginParam
       }).then(function (res) {
         console.log(JSON.stringify(res));
diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue
index 324eef4..548bd2c 100644
--- a/web_src/src/components/channelList.vue
+++ b/web_src/src/components/channelList.vue
@@ -30,9 +30,27 @@
             <el-table ref="channelListTable" :data="deviceChannelList" :height="winHeight" border style="width: 100%">
                 <el-table-column prop="channelId" label="閫氶亾缂栧彿" width="210">
                 </el-table-column>
-                <el-table-column prop="channelId" label="璁惧缂栧彿" width="210">
+                <el-table-column prop="deviceId" label="璁惧缂栧彿" width="210">
                 </el-table-column>
                 <el-table-column prop="name" label="閫氶亾鍚嶇О">
+                </el-table-column>
+                <el-table-column label="蹇収" width="80" align="center">
+                  <template slot-scope="scope">
+                    <img style="max-height: 3rem;max-width: 4rem;"
+                         :id="scope.row.deviceId + '_' + scope.row.channelId"
+                         :src="getSnap(scope.row)"
+                         @error="getSnapErrorEvent($event.target.id)"
+                         alt="">
+<!--                    <el-image-->
+<!--                      :id="'snapImg_' + scope.row.deviceId + '_' + scope.row.channelId"-->
+<!--                      :src="getSnap(scope.row)"-->
+<!--                      @error="getSnapErrorEvent($event, scope.row)"-->
+<!--                      :fit="'contain'">-->
+<!--                      <div slot="error" class="image-slot">-->
+<!--                        <i class="el-icon-picture-outline"></i>-->
+<!--                      </div>-->
+<!--                    </el-image>-->
+                  </template>
                 </el-table-column>
                 <el-table-column prop="subCount" label="瀛愯妭鐐规暟">
                 </el-table-column>
@@ -100,7 +118,8 @@
             total: 0,
             beforeUrl: "/deviceList",
             isLoging: false,
-            autoList: true
+            autoList: true,
+            loadSnap:{}
         };
     },
 
@@ -122,7 +141,6 @@
             } else {
                 this.showSubchannels();
             }
-
         },
         initParam: function () {
             this.deviceId = this.$route.params.deviceId;
@@ -174,8 +192,6 @@
             }).catch(function (error) {
                 console.log(error);
             });
-
-
         },
 
         //閫氱煡璁惧涓婁紶濯掍綋娴�
@@ -190,18 +206,22 @@
                 method: 'get',
                 url: '/api/play/start/' + deviceId + '/' + channelId
             }).then(function (res) {
-                console.log(res.data)
-                let streamId = res.data.streamId;
                 that.isLoging = false;
-                if (!!streamId) {
-                    // that.$refs.devicePlayer.play(res.data, deviceId, channelId, itemData.hasAudio);
-                    that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
-                        streamInfo: res.data,
-                        hasAudio: itemData.hasAudio
-                    });
-                    that.initData();
-                } else {
-                    that.$message.error(res.data);
+                if (res.data.code == 0) {
+
+                  setTimeout(()=>{
+                    console.log("涓嬭浇鎴浘")
+                    let snapId = deviceId + "_" + channelId;
+                    that.loadSnap[snapId] = 0;
+                    that.getSnapErrorEvent(snapId)
+                  },5000)
+                  that.$refs.devicePlayer.openDialog("media", deviceId, channelId, {
+                    streamInfo: res.data.data,
+                    hasAudio: itemData.hasAudio
+                  });
+                  that.initData();
+                }else {
+                  that.$message.error(res.data.msg);
                 }
             }).catch(function (e) {});
         },
@@ -228,7 +248,24 @@
               }
             });
         },
+        getSnap: function (row){
+          return '/static/snap/' + row.deviceId + '_' + row.channelId + '.jpg'
+        },
+        getSnapErrorEvent: function (id){
 
+          if (typeof (this.loadSnap[id]) != "undefined") {
+            console.log("涓嬭浇鎴浘" + this.loadSnap[id])
+            if (this.loadSnap[id] > 5) {
+              delete this.loadSnap[id];
+              return;
+            }
+            setTimeout(()=>{
+              this.loadSnap[id] ++
+              document.getElementById(id).setAttribute("src", '/static/snap/' + id + '.jpg?' + new Date().getTime())
+            },1000)
+
+          }
+        },
         showDevice: function () {
             this.$router.push(this.beforeUrl).then(() => {
                 this.initParam();
diff --git a/web_src/src/components/test.vue b/web_src/src/components/test.vue
index 603138c..6d53c54 100644
--- a/web_src/src/components/test.vue
+++ b/web_src/src/components/test.vue
@@ -24,6 +24,9 @@
           <div v-for="index of timeNode" class="timeQuery-label-cell" :style="'left:' + (100.0/timeNode*index).toFixed(4) + '%'">
             <div class="timeQuery-label-cell-label">{{24/timeNode * index}}</div>
           </div>
+          <ul>
+            <li v-for="item of allDataList" >{{!!item.name?item.name:item.dname}}</li>
+          </ul>
         </div>
       </el-col>
     </el-row>
@@ -36,6 +39,7 @@
   name: "test",
   data() {
     return {
+      allDataList:[],
       timeNode: 24,
       recordData:[
         {
@@ -58,6 +62,32 @@
     };
   },
   mounted() {
+    var list1 = [{
+                  key: Math.random()*10,
+                  name: "浜�1"
+                },{
+                  key: Math.random()*10,
+                  name: "浜�2"
+                },{
+                  key: Math.random()*10,
+                  name: "浜�3"
+                }]
+    var list2 = [{
+              key: Math.random()*10,
+              dname: "閮ㄩ棬1"
+              },{
+              key: Math.random()*10,
+              dname: "閮ㄩ棬2"
+              },{
+              key: Math.random()*10,
+              dname: "閮ㄩ棬3"
+              }]
+
+    var allData = list1.concat(list2)
+    allData.sort((a, b)=>{
+      return a.key-b.key;
+    })
+    this.allDataList = allData;
     for (let i = 1; i <= 24; i++) {
       console.log("<div class=\"timeQuery-label-cell\" style=\"left: " + (100.0/24*i).toFixed(4) + "%\"></div>")
     }

--
Gitblit v1.8.0