Browse Source

Merge pull request #32 from lawrencehj/master

优化录像列表获取算法等
648540858 4 years ago
parent
commit
2d8054be18

+ 46 - 46
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java

@@ -1,28 +1,28 @@
 /*
-* Conditions Of Use
-*
-* This software was developed by employees of the National Institute of
-* Standards and Technology (NIST), an agency of the Federal Government.
-* Pursuant to title 15 Untied States Code Section 105, works of NIST
-* employees are not subject to copyright protection in the United States
-* and are considered to be in the public domain.  As a result, a formal
-* license is not needed to use the software.
-*
-* This software is provided by NIST as a service and is expressly
-* provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
-* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
-* AND DATA ACCURACY.  NIST does not warrant or make any representations
-* regarding the use of the software or the results thereof, including but
-* not limited to the correctness, accuracy, reliability or usefulness of
-* the software.
-*
-* Permission to use this software is contingent upon your acceptance
-* of the terms of this agreement
-*
-* .
-*
-*/
+ * Conditions Of Use
+ *
+ * This software was developed by employees of the National Institute of
+ * Standards and Technology (NIST), an agency of the Federal Government.
+ * Pursuant to title 15 Untied States Code Section 105, works of NIST
+ * employees are not subject to copyright protection in the United States
+ * and are considered to be in the public domain.  As a result, a formal
+ * license is not needed to use the software.
+ *
+ * This software is provided by NIST as a service and is expressly
+ * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
+ * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
+ * AND DATA ACCURACY.  NIST does not warrant or make any representations
+ * regarding the use of the software or the results thereof, including but
+ * not limited to the correctness, accuracy, reliability or usefulness of
+ * the software.
+ *
+ * Permission to use this software is contingent upon your acceptance
+ * of the terms of this agreement
+ *
+ * .
+ *
+ */
 package com.genersoft.iot.vmp.gb28181.auth;
 
 import java.security.MessageDigest;
@@ -42,18 +42,18 @@ import gov.nist.core.InternalErrorHandler;
 
 /**
  * Implements the HTTP digest authentication method server side functionality.
- * 
+ *
  * @author M. Ranganathan
  * @author Marc Bednarek
  */
 
 public class DigestServerAuthenticationHelper  {
-    
+
     private MessageDigest messageDigest;
-    
+
     public static final String DEFAULT_ALGORITHM = "MD5";
     public static final String DEFAULT_SCHEME = "Digest";
-    
+
 
 
 
@@ -63,11 +63,11 @@ public class DigestServerAuthenticationHelper  {
 
     /**
      * Default constructor.
-     * @throws NoSuchAlgorithmException 
+     * @throws NoSuchAlgorithmException
      */
-    public DigestServerAuthenticationHelper() 
-        throws NoSuchAlgorithmException {
-            messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+    public DigestServerAuthenticationHelper()
+            throws NoSuchAlgorithmException {
+        messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
     }
 
     public static String toHexString(byte b[]) {
@@ -79,7 +79,7 @@ public class DigestServerAuthenticationHelper  {
         }
         return new String(c);
     }
-    
+
     /**
      * Generate the challenge string.
      *
@@ -121,34 +121,34 @@ public class DigestServerAuthenticationHelper  {
      *
      * @param request - the request to authenticate.
      * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
-     * 
+     *
      * @return true if authentication succeded and false otherwise.
      */
     public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
-    	AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
         if ( authHeader == null ) return false;
         String realm = authHeader.getRealm();
         String username = authHeader.getUsername();
-      
+
         if ( username == null || realm == null ) {
             return false;
         }
-       
+
         String nonce = authHeader.getNonce();
         URI uri = authHeader.getURI();
         if (uri == null) {
             return false;
         }
-        
 
-      
+
+
         String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
         String HA1 = hashedPassword;
 
-       
+
         byte[] mdbytes = messageDigest.digest(A2.getBytes());
         String HA2 = toHexString(mdbytes);
-      
+
         String cnonce = authHeader.getCNonce();
         String KD = HA1 + ":" + nonce;
         if (cnonce != null) {
@@ -158,7 +158,7 @@ public class DigestServerAuthenticationHelper  {
         mdbytes = messageDigest.digest(KD.getBytes());
         String mdString = toHexString(mdbytes);
         String response = authHeader.getResponse();
-       
+
 
         return mdString.equals(response);
     }
@@ -168,11 +168,11 @@ public class DigestServerAuthenticationHelper  {
      *
      * @param request - the request to authenticate.
      * @param pass -- the plain text password.
-     * 
+     *
      * @return true if authentication succeded and false otherwise.
      */
     public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
-    	AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
         if ( authHeader == null ) return false;
         String realm = authHeader.getRealm().trim();
         String username = authHeader.getUsername().trim();
@@ -184,7 +184,7 @@ public class DigestServerAuthenticationHelper  {
         String nonce = authHeader.getNonce();
         URI uri = authHeader.getURI();
         if (uri == null) {
-           return false;
+            return false;
         }
         // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
         String qop = authHeader.getQop();
@@ -233,6 +233,6 @@ public class DigestServerAuthenticationHelper  {
         String response = authHeader.getResponse();
         System.out.println("response: " + response);
         return mdString.equals(response);
-        
+
     }
 }

+ 6 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -409,7 +409,12 @@ public class SIPCommander implements ISIPCommander {
 		try {
 			MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
 			String ssrc = streamSession.createPlayBackSsrc();
-			String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
+			String streamId = null;
+			if (rtpEnable) {
+				streamId = String.format("gb_playback_%s_%s", device.getDeviceId(), channelId);
+			}else {
+				streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
+			}
 			// 添加订阅
 			JSONObject subscribeKey = new JSONObject();
 			subscribeKey.put("app", "rtp");

+ 14 - 23
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java

@@ -342,6 +342,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 		try {
 			// 回复200 OK
 			responseAck(evt);
+			String seqNo = String.valueOf(System.currentTimeMillis());
 			RecordInfo recordInfo = new RecordInfo();
 			Element rootElement = getRootElement(evt);
 			Element deviceIdElement = rootElement.element("DeviceID");
@@ -396,32 +397,22 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
 				if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
 					// 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
 					String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
-					// TODO 暂时直接操作redis存储,后续封装专用缓存接口,改为本地内存缓存
-					if (redis.hasKey(cacheKey)) {
-						List<RecordItem> previousList = (List<RecordItem>) redis.get(cacheKey);
-						if (previousList != null && previousList.size() > 0) {
-							recordList.addAll(previousList);
-						}
-						// 本分支表示录像列表被拆包,且加上之前的数据还是不够,保存缓存返回,等待下个包再处理
-						if (recordList.size() < recordInfo.getSumNum()) {
-							logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
-							redis.set(cacheKey, recordList, 90);
-							return;
-						} else {
-							// 本分支表示录像被拆包,但加上之前的数据够足够,返回响应
-							// 因设备心跳有监听redis过期机制,为提高性能,此处手动删除
-							logger.info("录像数据已全部获取");
-							redis.del(cacheKey);
-						}
-					} else {
-						// 本分支有两种可能:1、录像列表被拆包,且是第一个包,直接保存缓存返回,等待下个包再处理
-						// 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
-						logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
-						logger.info("等待后续的包...");
 
-						redis.set(cacheKey, recordList, 90);
+					redis.set(cacheKey + "_" + seqNo, recordList, 90);
+					List<Object> cacheKeys = redis.scan(cacheKey + "_*");
+					List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
+					for (int i = 0; i < cacheKeys.size(); i++) {
+						totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+					}
+					if (totalRecordList.size() < recordInfo.getSumNum()) {
+						logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
 						return;
 					}
+					logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项");
+					recordInfo.setRecordList(totalRecordList);
+					for (int i = 0; i < cacheKeys.size(); i++) {
+						redis.del(cacheKeys.get(i).toString());
+					}
 				}
 				// 自然顺序排序, 元素进行升序排列
 				recordInfo.getRecordList().sort(Comparator.naturalOrder());

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java

@@ -144,10 +144,10 @@ public class RegisterRequestProcessor extends SIPRequestAbstractProcessor {
 				storager.updateDevice(device);
 				publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER);
 
-				// 只有第一次注册才更新通道
-				if (!exists) {
+				// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
+				//if (!exists) {
 					handler.onRegister(device);
-				}
+				//}
 			} else if (registerFlag == 2) {
 				logger.info("注销成功! deviceId:" + device.getDeviceId());
 				publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java

@@ -43,7 +43,6 @@ public interface DeviceMapper {
             ")")
     int add(Device device);
 
-
     @Update(value = {" <script>" +
                 "UPDATE device " +
                 "SET deviceId='${deviceId}'" +

+ 6 - 6
src/main/resources/application-dev.yml

@@ -13,12 +13,12 @@ spring:
         timeout: 10000
     # [不可用] jdbc数据库配置, 暂不支持
     datasource:
-        #        name: eiot
-        #        url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
-        #        username:
-        #        password:
-        #        type: com.alibaba.druid.pool.DruidDataSource
-        #        driver-class-name: com.mysql.jdbc.Driver
+        #name: eiot
+        #url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
+        #username:
+        #password:
+        #type: com.alibaba.druid.pool.DruidDataSource
+        #driver-class-name: com.mysql.jdbc.Driver
         name: eiot
         url: jdbc:sqlite::resource:wvp.sqlite
         username:

+ 1 - 1
src/main/resources/application.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: local
+    active: dev

+ 4 - 4
web_src/src/components/gb28181/devicePlayer.vue

@@ -1,6 +1,6 @@
 <template>
 <div id="devicePlayer" v-loading="isLoging">
-    
+
     <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
         <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> -->
         <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player>
@@ -125,7 +125,7 @@
                                 <p>采样率: {{item.sample_rate}}</p>
                             </div>
                         </div>
-                        
+
                     </div>
 
                 </el-tab-pane>
@@ -237,7 +237,7 @@ export default {
             console.log(val)
         },
         play: function (streamInfo, hasAudio) {
-            
+
             this.hasaudio = hasAudio;
             this.isLoging = false;
             this.videoUrl = streamInfo.ws_flv;
@@ -319,7 +319,7 @@ export default {
             }
             this.convertKey = ''
         },
-        
+
         copySharedInfo: function (data) {
             console.log('复制内容:' + data);
             this.coverPlaying = false;