Commit 2d8054be18bf6ad6591703c6ab957777d137ecfe

Authored by 648540858
Committed by GitHub
2 parents 021b8207 5de358d3

Merge pull request #32 from lawrencehj/master

优化录像列表获取算法等
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
1 /* 1 /*
2 -* Conditions Of Use  
3 -*  
4 -* This software was developed by employees of the National Institute of  
5 -* Standards and Technology (NIST), an agency of the Federal Government.  
6 -* Pursuant to title 15 Untied States Code Section 105, works of NIST  
7 -* employees are not subject to copyright protection in the United States  
8 -* and are considered to be in the public domain. As a result, a formal  
9 -* license is not needed to use the software.  
10 -*  
11 -* This software is provided by NIST as a service and is expressly  
12 -* provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED  
13 -* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF  
14 -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT  
15 -* AND DATA ACCURACY. NIST does not warrant or make any representations  
16 -* regarding the use of the software or the results thereof, including but  
17 -* not limited to the correctness, accuracy, reliability or usefulness of  
18 -* the software.  
19 -*  
20 -* Permission to use this software is contingent upon your acceptance  
21 -* of the terms of this agreement  
22 -*  
23 -* .  
24 -*  
25 -*/ 2 + * Conditions Of Use
  3 + *
  4 + * This software was developed by employees of the National Institute of
  5 + * Standards and Technology (NIST), an agency of the Federal Government.
  6 + * Pursuant to title 15 Untied States Code Section 105, works of NIST
  7 + * employees are not subject to copyright protection in the United States
  8 + * and are considered to be in the public domain. As a result, a formal
  9 + * license is not needed to use the software.
  10 + *
  11 + * This software is provided by NIST as a service and is expressly
  12 + * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
  13 + * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
  14 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
  15 + * AND DATA ACCURACY. NIST does not warrant or make any representations
  16 + * regarding the use of the software or the results thereof, including but
  17 + * not limited to the correctness, accuracy, reliability or usefulness of
  18 + * the software.
  19 + *
  20 + * Permission to use this software is contingent upon your acceptance
  21 + * of the terms of this agreement
  22 + *
  23 + * .
  24 + *
  25 + */
26 package com.genersoft.iot.vmp.gb28181.auth; 26 package com.genersoft.iot.vmp.gb28181.auth;
27 27
28 import java.security.MessageDigest; 28 import java.security.MessageDigest;
@@ -42,18 +42,18 @@ import gov.nist.core.InternalErrorHandler; @@ -42,18 +42,18 @@ import gov.nist.core.InternalErrorHandler;
42 42
43 /** 43 /**
44 * Implements the HTTP digest authentication method server side functionality. 44 * Implements the HTTP digest authentication method server side functionality.
45 - * 45 + *
46 * @author M. Ranganathan 46 * @author M. Ranganathan
47 * @author Marc Bednarek 47 * @author Marc Bednarek
48 */ 48 */
49 49
50 public class DigestServerAuthenticationHelper { 50 public class DigestServerAuthenticationHelper {
51 - 51 +
52 private MessageDigest messageDigest; 52 private MessageDigest messageDigest;
53 - 53 +
54 public static final String DEFAULT_ALGORITHM = "MD5"; 54 public static final String DEFAULT_ALGORITHM = "MD5";
55 public static final String DEFAULT_SCHEME = "Digest"; 55 public static final String DEFAULT_SCHEME = "Digest";
56 - 56 +
57 57
58 58
59 59
@@ -63,11 +63,11 @@ public class DigestServerAuthenticationHelper { @@ -63,11 +63,11 @@ public class DigestServerAuthenticationHelper {
63 63
64 /** 64 /**
65 * Default constructor. 65 * Default constructor.
66 - * @throws NoSuchAlgorithmException 66 + * @throws NoSuchAlgorithmException
67 */ 67 */
68 - public DigestServerAuthenticationHelper()  
69 - throws NoSuchAlgorithmException {  
70 - messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM); 68 + public DigestServerAuthenticationHelper()
  69 + throws NoSuchAlgorithmException {
  70 + messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
71 } 71 }
72 72
73 public static String toHexString(byte b[]) { 73 public static String toHexString(byte b[]) {
@@ -79,7 +79,7 @@ public class DigestServerAuthenticationHelper { @@ -79,7 +79,7 @@ public class DigestServerAuthenticationHelper {
79 } 79 }
80 return new String(c); 80 return new String(c);
81 } 81 }
82 - 82 +
83 /** 83 /**
84 * Generate the challenge string. 84 * Generate the challenge string.
85 * 85 *
@@ -121,34 +121,34 @@ public class DigestServerAuthenticationHelper { @@ -121,34 +121,34 @@ public class DigestServerAuthenticationHelper {
121 * 121 *
122 * @param request - the request to authenticate. 122 * @param request - the request to authenticate.
123 * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password. 123 * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
124 - * 124 + *
125 * @return true if authentication succeded and false otherwise. 125 * @return true if authentication succeded and false otherwise.
126 */ 126 */
127 public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) { 127 public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
128 - AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); 128 + AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
129 if ( authHeader == null ) return false; 129 if ( authHeader == null ) return false;
130 String realm = authHeader.getRealm(); 130 String realm = authHeader.getRealm();
131 String username = authHeader.getUsername(); 131 String username = authHeader.getUsername();
132 - 132 +
133 if ( username == null || realm == null ) { 133 if ( username == null || realm == null ) {
134 return false; 134 return false;
135 } 135 }
136 - 136 +
137 String nonce = authHeader.getNonce(); 137 String nonce = authHeader.getNonce();
138 URI uri = authHeader.getURI(); 138 URI uri = authHeader.getURI();
139 if (uri == null) { 139 if (uri == null) {
140 return false; 140 return false;
141 } 141 }
142 -  
143 142
144 - 143 +
  144 +
145 String A2 = request.getMethod().toUpperCase() + ":" + uri.toString(); 145 String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
146 String HA1 = hashedPassword; 146 String HA1 = hashedPassword;
147 147
148 - 148 +
149 byte[] mdbytes = messageDigest.digest(A2.getBytes()); 149 byte[] mdbytes = messageDigest.digest(A2.getBytes());
150 String HA2 = toHexString(mdbytes); 150 String HA2 = toHexString(mdbytes);
151 - 151 +
152 String cnonce = authHeader.getCNonce(); 152 String cnonce = authHeader.getCNonce();
153 String KD = HA1 + ":" + nonce; 153 String KD = HA1 + ":" + nonce;
154 if (cnonce != null) { 154 if (cnonce != null) {
@@ -158,7 +158,7 @@ public class DigestServerAuthenticationHelper { @@ -158,7 +158,7 @@ public class DigestServerAuthenticationHelper {
158 mdbytes = messageDigest.digest(KD.getBytes()); 158 mdbytes = messageDigest.digest(KD.getBytes());
159 String mdString = toHexString(mdbytes); 159 String mdString = toHexString(mdbytes);
160 String response = authHeader.getResponse(); 160 String response = authHeader.getResponse();
161 - 161 +
162 162
163 return mdString.equals(response); 163 return mdString.equals(response);
164 } 164 }
@@ -168,11 +168,11 @@ public class DigestServerAuthenticationHelper { @@ -168,11 +168,11 @@ public class DigestServerAuthenticationHelper {
168 * 168 *
169 * @param request - the request to authenticate. 169 * @param request - the request to authenticate.
170 * @param pass -- the plain text password. 170 * @param pass -- the plain text password.
171 - * 171 + *
172 * @return true if authentication succeded and false otherwise. 172 * @return true if authentication succeded and false otherwise.
173 */ 173 */
174 public boolean doAuthenticatePlainTextPassword(Request request, String pass) { 174 public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
175 - AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); 175 + AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
176 if ( authHeader == null ) return false; 176 if ( authHeader == null ) return false;
177 String realm = authHeader.getRealm().trim(); 177 String realm = authHeader.getRealm().trim();
178 String username = authHeader.getUsername().trim(); 178 String username = authHeader.getUsername().trim();
@@ -184,7 +184,7 @@ public class DigestServerAuthenticationHelper { @@ -184,7 +184,7 @@ public class DigestServerAuthenticationHelper {
184 String nonce = authHeader.getNonce(); 184 String nonce = authHeader.getNonce();
185 URI uri = authHeader.getURI(); 185 URI uri = authHeader.getURI();
186 if (uri == null) { 186 if (uri == null) {
187 - return false; 187 + return false;
188 } 188 }
189 // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 189 // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
190 String qop = authHeader.getQop(); 190 String qop = authHeader.getQop();
@@ -233,6 +233,6 @@ public class DigestServerAuthenticationHelper { @@ -233,6 +233,6 @@ public class DigestServerAuthenticationHelper {
233 String response = authHeader.getResponse(); 233 String response = authHeader.getResponse();
234 System.out.println("response: " + response); 234 System.out.println("response: " + response);
235 return mdString.equals(response); 235 return mdString.equals(response);
236 - 236 +
237 } 237 }
238 } 238 }
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -409,7 +409,12 @@ public class SIPCommander implements ISIPCommander { @@ -409,7 +409,12 @@ public class SIPCommander implements ISIPCommander {
409 try { 409 try {
410 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); 410 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
411 String ssrc = streamSession.createPlayBackSsrc(); 411 String ssrc = streamSession.createPlayBackSsrc();
412 - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); 412 + String streamId = null;
  413 + if (rtpEnable) {
  414 + streamId = String.format("gb_playback_%s_%s", device.getDeviceId(), channelId);
  415 + }else {
  416 + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
  417 + }
413 // 添加订阅 418 // 添加订阅
414 JSONObject subscribeKey = new JSONObject(); 419 JSONObject subscribeKey = new JSONObject();
415 subscribeKey.put("app", "rtp"); 420 subscribeKey.put("app", "rtp");
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
@@ -342,6 +342,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { @@ -342,6 +342,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
342 try { 342 try {
343 // 回复200 OK 343 // 回复200 OK
344 responseAck(evt); 344 responseAck(evt);
  345 + String seqNo = String.valueOf(System.currentTimeMillis());
345 RecordInfo recordInfo = new RecordInfo(); 346 RecordInfo recordInfo = new RecordInfo();
346 Element rootElement = getRootElement(evt); 347 Element rootElement = getRootElement(evt);
347 Element deviceIdElement = rootElement.element("DeviceID"); 348 Element deviceIdElement = rootElement.element("DeviceID");
@@ -396,32 +397,22 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { @@ -396,32 +397,22 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
396 if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) { 397 if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
397 // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分 398 // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
398 String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn; 399 String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
399 - // TODO 暂时直接操作redis存储,后续封装专用缓存接口,改为本地内存缓存  
400 - if (redis.hasKey(cacheKey)) {  
401 - List<RecordItem> previousList = (List<RecordItem>) redis.get(cacheKey);  
402 - if (previousList != null && previousList.size() > 0) {  
403 - recordList.addAll(previousList);  
404 - }  
405 - // 本分支表示录像列表被拆包,且加上之前的数据还是不够,保存缓存返回,等待下个包再处理  
406 - if (recordList.size() < recordInfo.getSumNum()) {  
407 - logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");  
408 - redis.set(cacheKey, recordList, 90);  
409 - return;  
410 - } else {  
411 - // 本分支表示录像被拆包,但加上之前的数据够足够,返回响应  
412 - // 因设备心跳有监听redis过期机制,为提高性能,此处手动删除  
413 - logger.info("录像数据已全部获取");  
414 - redis.del(cacheKey);  
415 - }  
416 - } else {  
417 - // 本分支有两种可能:1、录像列表被拆包,且是第一个包,直接保存缓存返回,等待下个包再处理  
418 - // 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据  
419 - logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");  
420 - logger.info("等待后续的包...");  
421 400
422 - redis.set(cacheKey, recordList, 90); 401 + redis.set(cacheKey + "_" + seqNo, recordList, 90);
  402 + List<Object> cacheKeys = redis.scan(cacheKey + "_*");
  403 + List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
  404 + for (int i = 0; i < cacheKeys.size(); i++) {
  405 + totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
  406 + }
  407 + if (totalRecordList.size() < recordInfo.getSumNum()) {
  408 + logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
423 return; 409 return;
424 } 410 }
  411 + logger.info("录像数据已全部获取,共" + recordInfo.getSumNum() + "项");
  412 + recordInfo.setRecordList(totalRecordList);
  413 + for (int i = 0; i < cacheKeys.size(); i++) {
  414 + redis.del(cacheKeys.get(i).toString());
  415 + }
425 } 416 }
426 // 自然顺序排序, 元素进行升序排列 417 // 自然顺序排序, 元素进行升序排列
427 recordInfo.getRecordList().sort(Comparator.naturalOrder()); 418 recordInfo.getRecordList().sort(Comparator.naturalOrder());
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
@@ -144,10 +144,10 @@ public class RegisterRequestProcessor extends SIPRequestAbstractProcessor { @@ -144,10 +144,10 @@ public class RegisterRequestProcessor extends SIPRequestAbstractProcessor {
144 storager.updateDevice(device); 144 storager.updateDevice(device);
145 publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER); 145 publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER);
146 146
147 - // 只有第一次注册才更新通道  
148 - if (!exists) { 147 + // 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
  148 + //if (!exists) {
149 handler.onRegister(device); 149 handler.onRegister(device);
150 - } 150 + //}
151 } else if (registerFlag == 2) { 151 } else if (registerFlag == 2) {
152 logger.info("注销成功! deviceId:" + device.getDeviceId()); 152 logger.info("注销成功! deviceId:" + device.getDeviceId());
153 publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); 153 publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
@@ -43,7 +43,6 @@ public interface DeviceMapper { @@ -43,7 +43,6 @@ public interface DeviceMapper {
43 ")") 43 ")")
44 int add(Device device); 44 int add(Device device);
45 45
46 -  
47 @Update(value = {" <script>" + 46 @Update(value = {" <script>" +
48 "UPDATE device " + 47 "UPDATE device " +
49 "SET deviceId='${deviceId}'" + 48 "SET deviceId='${deviceId}'" +
src/main/resources/application-dev.yml
@@ -13,12 +13,12 @@ spring: @@ -13,12 +13,12 @@ spring:
13 timeout: 10000 13 timeout: 10000
14 # [不可用] jdbc数据库配置, 暂不支持 14 # [不可用] jdbc数据库配置, 暂不支持
15 datasource: 15 datasource:
16 - # name: eiot  
17 - # url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true  
18 - # username:  
19 - # password:  
20 - # type: com.alibaba.druid.pool.DruidDataSource  
21 - # driver-class-name: com.mysql.jdbc.Driver 16 + #name: eiot
  17 + #url: jdbc:mysql://127.0.0.1:3306/eiot?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
  18 + #username:
  19 + #password:
  20 + #type: com.alibaba.druid.pool.DruidDataSource
  21 + #driver-class-name: com.mysql.jdbc.Driver
22 name: eiot 22 name: eiot
23 url: jdbc:sqlite::resource:wvp.sqlite 23 url: jdbc:sqlite::resource:wvp.sqlite
24 username: 24 username:
src/main/resources/application.yml
1 spring: 1 spring:
2 profiles: 2 profiles:
3 - active: local  
4 \ No newline at end of file 3 \ No newline at end of file
  4 + active: dev
5 \ No newline at end of file 5 \ No newline at end of file
web_src/src/components/gb28181/devicePlayer.vue
1 <template> 1 <template>
2 <div id="devicePlayer" v-loading="isLoging"> 2 <div id="devicePlayer" v-loading="isLoging">
3 - 3 +
4 <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> 4 <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()">
5 <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> 5 <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> -->
6 <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player> 6 <player ref="videoPlayer" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></player>
@@ -125,7 +125,7 @@ @@ -125,7 +125,7 @@
125 <p>采样率: {{item.sample_rate}}</p> 125 <p>采样率: {{item.sample_rate}}</p>
126 </div> 126 </div>
127 </div> 127 </div>
128 - 128 +
129 </div> 129 </div>
130 130
131 </el-tab-pane> 131 </el-tab-pane>
@@ -237,7 +237,7 @@ export default { @@ -237,7 +237,7 @@ export default {
237 console.log(val) 237 console.log(val)
238 }, 238 },
239 play: function (streamInfo, hasAudio) { 239 play: function (streamInfo, hasAudio) {
240 - 240 +
241 this.hasaudio = hasAudio; 241 this.hasaudio = hasAudio;
242 this.isLoging = false; 242 this.isLoging = false;
243 this.videoUrl = streamInfo.ws_flv; 243 this.videoUrl = streamInfo.ws_flv;
@@ -319,7 +319,7 @@ export default { @@ -319,7 +319,7 @@ export default {
319 } 319 }
320 this.convertKey = '' 320 this.convertKey = ''
321 }, 321 },
322 - 322 +
323 copySharedInfo: function (data) { 323 copySharedInfo: function (data) {
324 console.log('复制内容:' + data); 324 console.log('复制内容:' + data);
325 this.coverPlaying = false; 325 this.coverPlaying = false;