Commit 34135cce5d59f6ad7653737dd035bb1d441e185f
Merge remote-tracking branch 'origin/master' into wvp-28181-2.0
# Conflicts: # README.md # src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java # src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java # src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java # src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java # src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java # src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java # src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java # src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java # src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java # src/main/java/com/genersoft/iot/vmp/web/ApiStreamController.java # src/main/resources/application-dev.yml # web_src/src/components/gb28181/devicePlayer.vue
Showing
43 changed files
with
1440 additions
and
2198 deletions
Too many changes to show.
To preserve performance only 43 of 49 files are displayed.
pom.xml
| ... | ... | @@ -13,13 +13,38 @@ |
| 13 | 13 | <artifactId>wvp</artifactId> |
| 14 | 14 | <name>web video platform</name> |
| 15 | 15 | |
| 16 | + <repositories> | |
| 17 | + <repository> | |
| 18 | + <id>nexus-aliyun</id> | |
| 19 | + <name>Nexus aliyun</name> | |
| 20 | + <url>https://maven.aliyun.com/repository/public</url> | |
| 21 | + <layout>default</layout> | |
| 22 | + <snapshots> | |
| 23 | + <enabled>false</enabled> | |
| 24 | + </snapshots> | |
| 25 | + <releases> | |
| 26 | + <enabled>true</enabled> | |
| 27 | + </releases> | |
| 28 | + </repository> | |
| 29 | + </repositories> | |
| 30 | + <pluginRepositories> | |
| 31 | + <pluginRepository> | |
| 32 | + <id>nexus-aliyun</id> | |
| 33 | + <name>Nexus aliyun</name> | |
| 34 | + <url>https://maven.aliyun.com/repository/public</url> | |
| 35 | + <snapshots> | |
| 36 | + <enabled>false</enabled> | |
| 37 | + </snapshots> | |
| 38 | + <releases> | |
| 39 | + <enabled>true</enabled> | |
| 40 | + </releases> | |
| 41 | + </pluginRepository> | |
| 42 | + </pluginRepositories> | |
| 43 | + | |
| 16 | 44 | <properties> |
| 17 | 45 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 18 | 46 | |
| 19 | 47 | <!-- 依赖版本 --> |
| 20 | - <mapper.version>4.1.5</mapper.version> | |
| 21 | - <mybatis.version>3.5.5</mybatis.version> | |
| 22 | - <mybatis.spring.version>2.0.5</mybatis.spring.version> | |
| 23 | 48 | <pagehelper.version>5.2.0</pagehelper.version> |
| 24 | 49 | <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory> |
| 25 | 50 | <asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory> |
| ... | ... | @@ -31,30 +56,16 @@ |
| 31 | 56 | <dependencies> |
| 32 | 57 | <dependency> |
| 33 | 58 | <groupId>org.springframework.boot</groupId> |
| 34 | - <artifactId>spring-boot-starter-jdbc</artifactId> | |
| 35 | - </dependency> | |
| 36 | - <dependency> | |
| 37 | - <groupId>org.springframework.boot</groupId> | |
| 38 | - <artifactId>spring-boot-starter-tomcat</artifactId> | |
| 59 | + <artifactId>spring-boot-starter-data-redis</artifactId> | |
| 39 | 60 | </dependency> |
| 40 | 61 | <dependency> |
| 41 | 62 | <groupId>org.springframework.boot</groupId> |
| 42 | 63 | <artifactId>spring-boot-starter-web</artifactId> |
| 43 | 64 | </dependency> |
| 44 | 65 | <dependency> |
| 45 | - <groupId>org.springframework</groupId> | |
| 46 | - <artifactId>spring-context</artifactId> | |
| 47 | - </dependency> | |
| 48 | - | |
| 49 | - <!-- redis --> | |
| 50 | - <dependency> | |
| 51 | - <groupId>org.springframework.data</groupId> | |
| 52 | - <artifactId>spring-data-redis</artifactId> | |
| 53 | - </dependency> | |
| 54 | - <dependency> | |
| 55 | - <groupId>redis.clients</groupId> | |
| 56 | - <artifactId>jedis</artifactId> | |
| 57 | - <version>3.3.0</version> | |
| 66 | + <groupId>org.mybatis.spring.boot</groupId> | |
| 67 | + <artifactId>mybatis-spring-boot-starter</artifactId> | |
| 68 | + <version>2.1.4</version> | |
| 58 | 69 | </dependency> |
| 59 | 70 | |
| 60 | 71 | <!-- druid数据库连接池 --> |
| ... | ... | @@ -71,36 +82,25 @@ |
| 71 | 82 | <version>8.0.22</version> |
| 72 | 83 | </dependency> |
| 73 | 84 | |
| 74 | - <!--Mybatis --> | |
| 75 | - <dependency> | |
| 76 | - <groupId>org.mybatis</groupId> | |
| 77 | - <artifactId>mybatis</artifactId> | |
| 78 | - <version>${mybatis.version}</version> | |
| 79 | - </dependency> | |
| 85 | + <!-- 添加sqlite-jdbc数据库驱动 --> | |
| 80 | 86 | <dependency> |
| 81 | - <groupId>org.mybatis</groupId> | |
| 82 | - <artifactId>mybatis-spring</artifactId> | |
| 83 | - <version>${mybatis.spring.version}</version> | |
| 87 | + <groupId>org.xerial</groupId> | |
| 88 | + <artifactId>sqlite-jdbc</artifactId> | |
| 89 | + <version>3.32.3.2</version> | |
| 84 | 90 | </dependency> |
| 85 | 91 | |
| 86 | - <!--分页插件 --> | |
| 92 | + <!--Mybatis分页插件 --> | |
| 87 | 93 | <dependency> |
| 88 | 94 | <groupId>com.github.pagehelper</groupId> |
| 89 | - <artifactId>pagehelper</artifactId> | |
| 90 | - <version>${pagehelper.version}</version> | |
| 95 | + <artifactId>pagehelper-spring-boot-starter</artifactId> | |
| 96 | + <version>1.2.10</version> | |
| 91 | 97 | </dependency> |
| 92 | 98 | |
| 93 | - <!--通用Mapper --> | |
| 94 | - <dependency> | |
| 95 | - <groupId>tk.mybatis</groupId> | |
| 96 | - <artifactId>mapper</artifactId> | |
| 97 | - <version>${mapper.version}</version> | |
| 98 | - </dependency> | |
| 99 | - <dependency> | |
| 100 | - <groupId>org.apache.commons</groupId> | |
| 101 | - <artifactId>commons-lang3</artifactId> | |
| 102 | - <version>3.11</version> | |
| 103 | - </dependency> | |
| 99 | +<!-- <dependency>--> | |
| 100 | +<!-- <groupId>org.apache.commons</groupId>--> | |
| 101 | +<!-- <artifactId>commons-lang3</artifactId>--> | |
| 102 | +<!-- <version>3.11</version>--> | |
| 103 | +<!-- </dependency>--> | |
| 104 | 104 | |
| 105 | 105 | <!--Swagger2 --> |
| 106 | 106 | <!--在线文档 --> | ... | ... |
src/main/java/com/genersoft/iot/vmp/common/PageResult.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.common; | |
| 2 | - | |
| 3 | - | |
| 4 | -import java.util.List; | |
| 5 | - | |
| 6 | -public class PageResult<T> { | |
| 7 | - | |
| 8 | - private int page; | |
| 9 | - private int count; | |
| 10 | - private int total; | |
| 11 | - | |
| 12 | - private List<T> data; | |
| 13 | - | |
| 14 | - public List<T> getData() { | |
| 15 | - return data; | |
| 16 | - } | |
| 17 | - | |
| 18 | - public int getPage() { | |
| 19 | - return page; | |
| 20 | - } | |
| 21 | - | |
| 22 | - public void setPage(int page) { | |
| 23 | - this.page = page; | |
| 24 | - } | |
| 25 | - | |
| 26 | - public int getCount() { | |
| 27 | - return count; | |
| 28 | - } | |
| 29 | - | |
| 30 | - public void setCount(int count) { | |
| 31 | - this.count = count; | |
| 32 | - } | |
| 33 | - | |
| 34 | - public int getTotal() { | |
| 35 | - return total; | |
| 36 | - } | |
| 37 | - | |
| 38 | - public void setTotal(int total) { | |
| 39 | - this.total = total; | |
| 40 | - } | |
| 41 | - | |
| 42 | - public void setData(List<T> data) { | |
| 43 | - this.data = data; | |
| 44 | - } | |
| 45 | -} |
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
| ... | ... | @@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSONArray; |
| 4 | 4 | |
| 5 | 5 | public class StreamInfo { |
| 6 | 6 | |
| 7 | - private String ssrc; | |
| 8 | 7 | private String streamId; |
| 9 | 8 | private String deviceID; |
| 10 | 9 | private String cahnnelId; |
| ... | ... | @@ -20,14 +19,6 @@ public class StreamInfo { |
| 20 | 19 | private String rtsp; |
| 21 | 20 | private JSONArray tracks; |
| 22 | 21 | |
| 23 | - public String getSsrc() { | |
| 24 | - return ssrc; | |
| 25 | - } | |
| 26 | - | |
| 27 | - public void setSsrc(String ssrc) { | |
| 28 | - this.ssrc = ssrc; | |
| 29 | - } | |
| 30 | - | |
| 31 | 22 | public String getDeviceID() { |
| 32 | 23 | return deviceID; |
| 33 | 24 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.conf; | |
| 2 | + | |
| 3 | +import org.slf4j.Logger; | |
| 4 | +import org.slf4j.LoggerFactory; | |
| 5 | +import org.springframework.beans.factory.annotation.Value; | |
| 6 | +import org.springframework.boot.CommandLineRunner; | |
| 7 | +import org.springframework.core.annotation.Order; | |
| 8 | +import org.springframework.stereotype.Component; | |
| 9 | + | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 对配置文件进行校验 | |
| 13 | + */ | |
| 14 | +@Component | |
| 15 | +@Order(value=2) | |
| 16 | +public class ApplicationCheckRunner implements CommandLineRunner { | |
| 17 | + | |
| 18 | + private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); | |
| 19 | + | |
| 20 | + @Value("${sip.ip}") | |
| 21 | + private String sipIp; | |
| 22 | + | |
| 23 | + @Value("${media.ip}") | |
| 24 | + private String mediaIp; | |
| 25 | + | |
| 26 | + @Value("${media.wanIp}") | |
| 27 | + private String mediaWanIp; | |
| 28 | + | |
| 29 | + @Value("${media.hookIp}") | |
| 30 | + private String mediaHookIp; | |
| 31 | + | |
| 32 | + @Value("${media.port}") | |
| 33 | + private int mediaPort; | |
| 34 | + | |
| 35 | + @Value("${media.secret}") | |
| 36 | + private String mediaSecret; | |
| 37 | + | |
| 38 | + @Value("${media.streamNoneReaderDelayMS}") | |
| 39 | + private String streamNoneReaderDelayMS; | |
| 40 | + | |
| 41 | + @Value("${sip.ip}") | |
| 42 | + private String sipIP; | |
| 43 | + | |
| 44 | + @Value("${server.port}") | |
| 45 | + private String serverPort; | |
| 46 | + | |
| 47 | + @Value("${media.autoConfig}") | |
| 48 | + private boolean autoConfig; | |
| 49 | + | |
| 50 | + | |
| 51 | + @Override | |
| 52 | + public void run(String... args) throws Exception { | |
| 53 | + if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { | |
| 54 | + logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); | |
| 55 | + System.exit(1); | |
| 56 | + } | |
| 57 | + | |
| 58 | + if (mediaIp.equals("localhost") || mediaIp.equals("127.0.0.1")) { | |
| 59 | + logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); | |
| 60 | + } | |
| 61 | + | |
| 62 | + } | |
| 63 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
| ... | ... | @@ -8,8 +8,10 @@ import java.util.concurrent.ThreadPoolExecutor; |
| 8 | 8 | import java.util.concurrent.TimeUnit; |
| 9 | 9 | |
| 10 | 10 | import javax.sip.*; |
| 11 | +import javax.sip.header.CallIdHeader; | |
| 11 | 12 | import javax.sip.message.Response; |
| 12 | 13 | |
| 14 | +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | |
| 13 | 15 | import org.slf4j.Logger; |
| 14 | 16 | import org.slf4j.LoggerFactory; |
| 15 | 17 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -34,6 +36,9 @@ public class SipLayer implements SipListener { |
| 34 | 36 | @Autowired |
| 35 | 37 | private SIPProcessorFactory processorFactory; |
| 36 | 38 | |
| 39 | + @Autowired | |
| 40 | + private SipSubscribe sipSubscribe; | |
| 41 | + | |
| 37 | 42 | private SipStack sipStack; |
| 38 | 43 | |
| 39 | 44 | private SipFactory sipFactory; |
| ... | ... | @@ -133,17 +138,34 @@ public class SipLayer implements SipListener { |
| 133 | 138 | // TODO Auto-generated catch block |
| 134 | 139 | e.printStackTrace(); |
| 135 | 140 | } |
| 141 | + if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { | |
| 142 | + CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); | |
| 143 | + if (callIdHeader != null) { | |
| 144 | + SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); | |
| 145 | + if (subscribe != null) { | |
| 146 | + subscribe.response(evt); | |
| 147 | + } | |
| 148 | + } | |
| 149 | + } | |
| 136 | 150 | // } else if (status == Response.TRYING) { |
| 137 | 151 | // trying不会回复 |
| 138 | 152 | } else if ((status >= 100) && (status < 200)) { |
| 139 | 153 | // 增加其它无需回复的响应,如101、180等 |
| 140 | 154 | } else { |
| 141 | 155 | logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); |
| 156 | + if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { | |
| 157 | + CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME); | |
| 158 | + if (callIdHeader != null) { | |
| 159 | + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); | |
| 160 | + if (subscribe != null) { | |
| 161 | + subscribe.response(evt); | |
| 162 | + } | |
| 163 | + } | |
| 164 | + } | |
| 142 | 165 | } |
| 143 | - // trying不会回复 | |
| 144 | - // if (status == Response.TRYING) { | |
| 145 | 166 | |
| 146 | - // } | |
| 167 | + | |
| 168 | + | |
| 147 | 169 | } |
| 148 | 170 | |
| 149 | 171 | /** | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
| ... | ... | @@ -27,6 +27,7 @@ package com.genersoft.iot.vmp.gb28181.auth; |
| 27 | 27 | |
| 28 | 28 | import java.security.MessageDigest; |
| 29 | 29 | import java.security.NoSuchAlgorithmException; |
| 30 | +import java.text.DecimalFormat; | |
| 30 | 31 | import java.util.Date; |
| 31 | 32 | import java.util.Random; |
| 32 | 33 | |
| ... | ... | @@ -103,9 +104,12 @@ public class DigestServerAuthenticationHelper { |
| 103 | 104 | .createWWWAuthenticateHeader(DEFAULT_SCHEME); |
| 104 | 105 | proxyAuthenticate.setParameter("realm", realm); |
| 105 | 106 | proxyAuthenticate.setParameter("nonce", generateNonce()); |
| 107 | + | |
| 106 | 108 | proxyAuthenticate.setParameter("opaque", ""); |
| 107 | 109 | proxyAuthenticate.setParameter("stale", "FALSE"); |
| 108 | 110 | proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM); |
| 111 | + | |
| 112 | +// proxyAuthenticate.setParameter("qop", "auth"); | |
| 109 | 113 | response.setHeader(proxyAuthenticate); |
| 110 | 114 | } catch (Exception ex) { |
| 111 | 115 | InternalErrorHandler.handleException(ex); |
| ... | ... | @@ -170,42 +174,116 @@ public class DigestServerAuthenticationHelper { |
| 170 | 174 | public boolean doAuthenticatePlainTextPassword(Request request, String pass) { |
| 171 | 175 | AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); |
| 172 | 176 | if ( authHeader == null ) return false; |
| 173 | - String realm = authHeader.getRealm(); | |
| 174 | - String username = authHeader.getUsername(); | |
| 175 | - | |
| 176 | - | |
| 177 | + String realm = authHeader.getRealm().trim(); | |
| 178 | + String username = authHeader.getUsername().trim(); | |
| 179 | + | |
| 177 | 180 | if ( username == null || realm == null ) { |
| 178 | 181 | return false; |
| 179 | 182 | } |
| 180 | - | |
| 181 | 183 | |
| 182 | 184 | String nonce = authHeader.getNonce(); |
| 183 | 185 | URI uri = authHeader.getURI(); |
| 184 | 186 | if (uri == null) { |
| 185 | 187 | return false; |
| 186 | 188 | } |
| 187 | - | |
| 189 | + // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 | |
| 190 | + String qop = authHeader.getQop(); | |
| 191 | + | |
| 192 | + // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 | |
| 193 | + // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 | |
| 194 | + String cNonce = authHeader.getCNonce(); | |
| 195 | + | |
| 196 | + // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量 | |
| 197 | + int nc = authHeader.getNonceCount(); | |
| 198 | + String ncStr = new DecimalFormat("00000000").format(nc); | |
| 199 | +// String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16)); | |
| 188 | 200 | |
| 189 | 201 | String A1 = username + ":" + realm + ":" + pass; |
| 190 | 202 | String A2 = request.getMethod().toUpperCase() + ":" + uri.toString(); |
| 191 | 203 | byte mdbytes[] = messageDigest.digest(A1.getBytes()); |
| 192 | 204 | String HA1 = toHexString(mdbytes); |
| 205 | + System.out.println("A1: " + A1); | |
| 206 | + System.out.println("A2: " + A2); | |
| 193 | 207 | |
| 194 | - | |
| 195 | 208 | mdbytes = messageDigest.digest(A2.getBytes()); |
| 196 | 209 | String HA2 = toHexString(mdbytes); |
| 197 | - | |
| 210 | + System.out.println("HA1: " + HA1); | |
| 211 | + System.out.println("HA2: " + HA2); | |
| 198 | 212 | String cnonce = authHeader.getCNonce(); |
| 213 | + System.out.println("nonce: " + nonce); | |
| 214 | + System.out.println("nc: " + ncStr); | |
| 215 | + System.out.println("cnonce: " + cnonce); | |
| 216 | + System.out.println("qop: " + qop); | |
| 199 | 217 | String KD = HA1 + ":" + nonce; |
| 200 | - if (cnonce != null) { | |
| 201 | - KD += ":" + cnonce; | |
| 218 | + | |
| 219 | + if (qop != null && qop.equals("auth") ) { | |
| 220 | + if (nc != -1) { | |
| 221 | + KD += ":" + ncStr; | |
| 222 | + } | |
| 223 | + if (cnonce != null) { | |
| 224 | + KD += ":" + cnonce; | |
| 225 | + } | |
| 226 | + KD += ":" + qop; | |
| 202 | 227 | } |
| 203 | 228 | KD += ":" + HA2; |
| 229 | + System.out.println("KD: " + KD); | |
| 204 | 230 | mdbytes = messageDigest.digest(KD.getBytes()); |
| 205 | 231 | String mdString = toHexString(mdbytes); |
| 232 | + System.out.println("mdString: " + mdString); | |
| 206 | 233 | String response = authHeader.getResponse(); |
| 234 | + System.out.println("response: " + response); | |
| 207 | 235 | return mdString.equals(response); |
| 208 | 236 | |
| 209 | 237 | } |
| 210 | 238 | |
| 239 | + | |
| 240 | + public static void main(String[] args) throws NoSuchAlgorithmException { | |
| 241 | + MessageDigest messageDigest2 = MessageDigest.getInstance(DEFAULT_ALGORITHM); | |
| 242 | + String realm = "DS-2CD2520F"; | |
| 243 | + String username = "admin"; | |
| 244 | + String passwd = "12345"; | |
| 245 | + | |
| 246 | + String nonce = "4d6a553452444d30525441364e6d4d304e6a68684e47553d"; | |
| 247 | + | |
| 248 | + String uri = "/ISAPI/Streaming/channels/101/picture"; | |
| 249 | + // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 | |
| 250 | + String qop = "auth"; | |
| 251 | + | |
| 252 | + // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 | |
| 253 | + // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 | |
| 254 | + String cNonce = "C1A5298F939E87E8F962A5EDFC206918"; | |
| 255 | + | |
| 256 | + // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量 | |
| 257 | + int nc = 1; | |
| 258 | + | |
| 259 | + String A1 = username + ":" + realm + ":" + passwd; | |
| 260 | + System.out.println("A1: " + A1); | |
| 261 | + String A2 = "GET" + ":" + uri.toString(); | |
| 262 | + System.out.println("A2: " + A2); | |
| 263 | + byte mdbytes[] = messageDigest2.digest(A1.getBytes()); | |
| 264 | + String HA1 = toHexString(mdbytes); | |
| 265 | + System.out.println("HA1: " + HA1); | |
| 266 | + | |
| 267 | + mdbytes = messageDigest2.digest(A2.getBytes()); | |
| 268 | + String HA2 = toHexString(mdbytes); | |
| 269 | + System.out.println("HA2: " + HA2); | |
| 270 | + String cnonce = "93d4d37df32e1a85"; | |
| 271 | + String KD = HA1 + ":" + nonce; | |
| 272 | + | |
| 273 | + if (nc != -1) { | |
| 274 | + KD += ":" + "00000001"; | |
| 275 | + } | |
| 276 | + if (cnonce != null) { | |
| 277 | + KD += ":" + cnonce; | |
| 278 | + } | |
| 279 | + if (qop != null) { | |
| 280 | + KD += ":" + qop; | |
| 281 | + } | |
| 282 | + KD += ":" + HA2; | |
| 283 | + System.out.println("KD: " + KD); | |
| 284 | + mdbytes = messageDigest2.digest(KD.getBytes()); | |
| 285 | + String mdString = toHexString(mdbytes); | |
| 286 | + String response = "3993a815e5cdaf4470e9b4f9bd41cf4a"; | |
| 287 | + System.out.println(mdString); | |
| 288 | + } | |
| 211 | 289 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.bean; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | -import java.util.List; | |
| 5 | -import java.util.Map; | |
| 6 | - | |
| 7 | 4 | public class Device { |
| 8 | 5 | |
| 9 | 6 | /** |
| ... | ... | @@ -46,24 +43,36 @@ public class Device { |
| 46 | 43 | private String streamMode; |
| 47 | 44 | |
| 48 | 45 | /** |
| 46 | + * wan地址_ip | |
| 47 | + */ | |
| 48 | + private String ip; | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * wan地址_port | |
| 52 | + */ | |
| 53 | + private int port; | |
| 54 | + | |
| 55 | + /** | |
| 49 | 56 | * wan地址 |
| 50 | 57 | */ |
| 51 | - private Host host; | |
| 58 | + private String hostAddress; | |
| 52 | 59 | |
| 53 | 60 | /** |
| 54 | 61 | * 在线 |
| 55 | 62 | */ |
| 56 | 63 | private int online; |
| 57 | 64 | |
| 65 | + | |
| 58 | 66 | /** |
| 59 | - * 通道列表 | |
| 67 | + * 注册时间 | |
| 60 | 68 | */ |
| 61 | -// private Map<String,DeviceChannel> channelMap; | |
| 69 | + private Long registerTimeMillis; | |
| 62 | 70 | |
| 71 | + /** | |
| 72 | + * 通道个数 | |
| 73 | + */ | |
| 63 | 74 | private int channelCount; |
| 64 | 75 | |
| 65 | - private List<String> channelList; | |
| 66 | - | |
| 67 | 76 | public String getDeviceId() { |
| 68 | 77 | return deviceId; |
| 69 | 78 | } |
| ... | ... | @@ -120,12 +129,28 @@ public class Device { |
| 120 | 129 | this.streamMode = streamMode; |
| 121 | 130 | } |
| 122 | 131 | |
| 123 | - public Host getHost() { | |
| 124 | - return host; | |
| 132 | + public String getIp() { | |
| 133 | + return ip; | |
| 134 | + } | |
| 135 | + | |
| 136 | + public void setIp(String ip) { | |
| 137 | + this.ip = ip; | |
| 138 | + } | |
| 139 | + | |
| 140 | + public int getPort() { | |
| 141 | + return port; | |
| 142 | + } | |
| 143 | + | |
| 144 | + public void setPort(int port) { | |
| 145 | + this.port = port; | |
| 146 | + } | |
| 147 | + | |
| 148 | + public String getHostAddress() { | |
| 149 | + return hostAddress; | |
| 125 | 150 | } |
| 126 | 151 | |
| 127 | - public void setHost(Host host) { | |
| 128 | - this.host = host; | |
| 152 | + public void setHostAddress(String hostAddress) { | |
| 153 | + this.hostAddress = hostAddress; | |
| 129 | 154 | } |
| 130 | 155 | |
| 131 | 156 | public int getOnline() { |
| ... | ... | @@ -144,11 +169,11 @@ public class Device { |
| 144 | 169 | this.channelCount = channelCount; |
| 145 | 170 | } |
| 146 | 171 | |
| 147 | - public List<String> getChannelList() { | |
| 148 | - return channelList; | |
| 172 | + public Long getRegisterTimeMillis() { | |
| 173 | + return registerTimeMillis; | |
| 149 | 174 | } |
| 150 | 175 | |
| 151 | - public void setChannelList(List<String> channelList) { | |
| 152 | - this.channelList = channelList; | |
| 176 | + public void setRegisterTimeMillis(Long registerTimeMillis) { | |
| 177 | + this.registerTimeMillis = registerTimeMillis; | |
| 153 | 178 | } |
| 154 | 179 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
| ... | ... | @@ -2,10 +2,17 @@ package com.genersoft.iot.vmp.gb28181.bean; |
| 2 | 2 | |
| 3 | 3 | public class DeviceChannel { |
| 4 | 4 | |
| 5 | + | |
| 6 | + | |
| 5 | 7 | /** |
| 6 | 8 | * 通道id |
| 7 | 9 | */ |
| 8 | 10 | private String channelId; |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 设备id | |
| 14 | + */ | |
| 15 | + private String deviceId; | |
| 9 | 16 | |
| 10 | 17 | /** |
| 11 | 18 | * 通道名 |
| ... | ... | @@ -141,18 +148,20 @@ public class DeviceChannel { |
| 141 | 148 | /** |
| 142 | 149 | * 流唯一编号,存在表示正在直播 |
| 143 | 150 | */ |
| 144 | - private String ssrc; | |
| 151 | + private String streamId; | |
| 145 | 152 | |
| 146 | 153 | /** |
| 147 | 154 | * 是否含有音频 |
| 148 | 155 | */ |
| 149 | - private boolean hasAudio; | |
| 156 | + private boolean hasAudio; | |
| 150 | 157 | |
| 151 | - /** | |
| 152 | - * 是否正在播放 | |
| 153 | - */ | |
| 154 | - private boolean play; | |
| 158 | + public String getDeviceId() { | |
| 159 | + return deviceId; | |
| 160 | + } | |
| 155 | 161 | |
| 162 | + public void setDeviceId(String deviceId) { | |
| 163 | + this.deviceId = deviceId; | |
| 164 | + } | |
| 156 | 165 | |
| 157 | 166 | public void setPTZType(int PTZType) { |
| 158 | 167 | this.PTZType = PTZType; |
| ... | ... | @@ -379,14 +388,6 @@ public class DeviceChannel { |
| 379 | 388 | this.subCount = subCount; |
| 380 | 389 | } |
| 381 | 390 | |
| 382 | - public String getSsrc() { | |
| 383 | - return ssrc; | |
| 384 | - } | |
| 385 | - | |
| 386 | - public void setSsrc(String ssrc) { | |
| 387 | - this.ssrc = ssrc; | |
| 388 | - } | |
| 389 | - | |
| 390 | 391 | public boolean isHasAudio() { |
| 391 | 392 | return hasAudio; |
| 392 | 393 | } |
| ... | ... | @@ -395,11 +396,11 @@ public class DeviceChannel { |
| 395 | 396 | this.hasAudio = hasAudio; |
| 396 | 397 | } |
| 397 | 398 | |
| 398 | - public boolean isPlay() { | |
| 399 | - return play; | |
| 399 | + public String getStreamId() { | |
| 400 | + return streamId; | |
| 400 | 401 | } |
| 401 | 402 | |
| 402 | - public void setPlay(boolean play) { | |
| 403 | - this.play = play; | |
| 403 | + public void setStreamId(String streamId) { | |
| 404 | + this.streamId = streamId; | |
| 404 | 405 | } |
| 405 | 406 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.event; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | |
| 5 | +import org.slf4j.Logger; | |
| 6 | +import org.slf4j.LoggerFactory; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | + | |
| 9 | +import javax.sip.ResponseEvent; | |
| 10 | +import javax.sip.message.Request; | |
| 11 | +import java.util.EventObject; | |
| 12 | +import java.util.Map; | |
| 13 | +import java.util.concurrent.ConcurrentHashMap; | |
| 14 | + | |
| 15 | +@Component | |
| 16 | +public class SipSubscribe { | |
| 17 | + | |
| 18 | + private final static Logger logger = LoggerFactory.getLogger(SipSubscribe.class); | |
| 19 | + | |
| 20 | + private Map<String, SipSubscribe.Event> errorSubscribes = new ConcurrentHashMap<>(); | |
| 21 | + | |
| 22 | + private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>(); | |
| 23 | + | |
| 24 | + public interface Event { | |
| 25 | + void response(ResponseEvent event); | |
| 26 | + } | |
| 27 | + | |
| 28 | + public void addErrorSubscribe(String key, SipSubscribe.Event event) { | |
| 29 | + errorSubscribes.put(key, event); | |
| 30 | + } | |
| 31 | + | |
| 32 | + public void addOkSubscribe(String key, SipSubscribe.Event event) { | |
| 33 | + okSubscribes.put(key, event); | |
| 34 | + } | |
| 35 | + | |
| 36 | + public SipSubscribe.Event getErrorSubscribe(String key) { | |
| 37 | + return errorSubscribes.get(key); | |
| 38 | + } | |
| 39 | + | |
| 40 | + public SipSubscribe.Event getOkSubscribe(String key) { | |
| 41 | + return okSubscribes.get(key); | |
| 42 | + } | |
| 43 | + | |
| 44 | + public int getErrorSubscribesSize(){ | |
| 45 | + return errorSubscribes.size(); | |
| 46 | + } | |
| 47 | + public int getOkSubscribesSize(){ | |
| 48 | + return okSubscribes.size(); | |
| 49 | + } | |
| 50 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
| ... | ... | @@ -7,8 +7,10 @@ import javax.sip.header.CSeqHeader; |
| 7 | 7 | import javax.sip.message.Request; |
| 8 | 8 | import javax.sip.message.Response; |
| 9 | 9 | |
| 10 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 10 | 11 | import com.alibaba.fastjson.JSON; |
| 11 | 12 | import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; |
| 13 | +import com.genersoft.iot.vmp.gb28181.transmit.response.impl.*; | |
| 12 | 14 | import org.slf4j.Logger; |
| 13 | 15 | import org.slf4j.LoggerFactory; |
| 14 | 16 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -54,7 +56,10 @@ public class SIPProcessorFactory { |
| 54 | 56 | |
| 55 | 57 | @Autowired |
| 56 | 58 | private IVideoManagerStorager storager; |
| 57 | - | |
| 59 | + | |
| 60 | + @Autowired | |
| 61 | + private IRedisCatchStorage redisCatchStorage; | |
| 62 | + | |
| 58 | 63 | @Autowired |
| 59 | 64 | private EventPublisher publisher; |
| 60 | 65 | |
| ... | ... | @@ -82,10 +87,11 @@ public class SIPProcessorFactory { |
| 82 | 87 | @Autowired |
| 83 | 88 | @Lazy |
| 84 | 89 | private RegisterResponseProcessor registerResponseProcessor; |
| 85 | - | |
| 90 | + | |
| 86 | 91 | @Autowired |
| 87 | 92 | private OtherResponseProcessor otherResponseProcessor; |
| 88 | - | |
| 93 | + | |
| 94 | + | |
| 89 | 95 | // 注:这里使用注解会导致循环依赖注入,暂用springBean |
| 90 | 96 | private SipProvider tcpSipProvider; |
| 91 | 97 | |
| ... | ... | @@ -140,6 +146,7 @@ public class SIPProcessorFactory { |
| 140 | 146 | processor.setOffLineDetector(offLineDetector); |
| 141 | 147 | processor.setCmder(cmder); |
| 142 | 148 | processor.setStorager(storager); |
| 149 | + processor.setRedisCatchStorage(redisCatchStorage); | |
| 143 | 150 | return processor; |
| 144 | 151 | } else { |
| 145 | 152 | return new OtherRequestProcessor(); |
| ... | ... | @@ -147,6 +154,7 @@ public class SIPProcessorFactory { |
| 147 | 154 | } |
| 148 | 155 | |
| 149 | 156 | public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) { |
| 157 | + | |
| 150 | 158 | Response response = evt.getResponse(); |
| 151 | 159 | CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
| 152 | 160 | String method = cseqHeader.getMethod(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
| ... | ... | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback; |
| 2 | 2 | |
| 3 | 3 | import java.util.HashMap; |
| 4 | 4 | import java.util.Map; |
| 5 | +import java.util.concurrent.ConcurrentHashMap; | |
| 5 | 6 | |
| 6 | 7 | import org.springframework.http.HttpStatus; |
| 7 | 8 | import org.springframework.http.ResponseEntity; |
| ... | ... | @@ -24,8 +25,10 @@ public class DeferredResultHolder { |
| 24 | 25 | |
| 25 | 26 | public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY"; |
| 26 | 27 | |
| 27 | - private Map<String, DeferredResult> map = new HashMap<String, DeferredResult>(); | |
| 28 | - | |
| 28 | + public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; | |
| 29 | + | |
| 30 | + private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>(); | |
| 31 | + | |
| 29 | 32 | public void put(String key, DeferredResult result) { |
| 30 | 33 | map.put(key, result); |
| 31 | 34 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| ... | ... | @@ -2,8 +2,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 4 | 4 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 5 | +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | |
| 5 | 6 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 6 | -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | 9 | * @Description:设备能力接口,用于定义设备的控制、查询能力 |
| ... | ... | @@ -84,7 +84,7 @@ public interface ISIPCommander { |
| 84 | 84 | * @param device 视频设备 |
| 85 | 85 | * @param channelId 预览通道 |
| 86 | 86 | */ |
| 87 | - void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event); | |
| 87 | + void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); | |
| 88 | 88 | |
| 89 | 89 | /** |
| 90 | 90 | * 请求回放视频流 |
| ... | ... | @@ -94,15 +94,16 @@ public interface ISIPCommander { |
| 94 | 94 | * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 95 | 95 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 96 | 96 | */ |
| 97 | - void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event); | |
| 97 | + void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); | |
| 98 | 98 | |
| 99 | 99 | /** |
| 100 | 100 | * 视频流停止 |
| 101 | 101 | * |
| 102 | 102 | * @param ssrc ssrc |
| 103 | 103 | */ |
| 104 | + void streamByeCmd(String ssrc, SipSubscribe.Event okEvent); | |
| 104 | 105 | void streamByeCmd(String ssrc); |
| 105 | - | |
| 106 | + | |
| 106 | 107 | /** |
| 107 | 108 | * 语音广播 |
| 108 | 109 | * |
| ... | ... | @@ -176,7 +177,7 @@ public interface ISIPCommander { |
| 176 | 177 | * |
| 177 | 178 | * @param device 视频设备 |
| 178 | 179 | */ |
| 179 | - boolean catalogQuery(Device device); | |
| 180 | + boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); | |
| 180 | 181 | |
| 181 | 182 | /** |
| 182 | 183 | * 查询录像信息 |
| ... | ... | @@ -214,4 +215,11 @@ public interface ISIPCommander { |
| 214 | 215 | * @param device 视频设备 |
| 215 | 216 | */ |
| 216 | 217 | boolean mobilePostitionQuery(Device device); |
| 218 | + | |
| 219 | + /** | |
| 220 | + * 释放rtpserver | |
| 221 | + * @param device | |
| 222 | + * @param channelId | |
| 223 | + */ | |
| 224 | + void closeRTPServer(Device device, String channelId); | |
| 217 | 225 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -47,9 +47,8 @@ public class SIPRequestHeaderProvider { |
| 47 | 47 | |
| 48 | 48 | public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| 49 | 49 | Request request = null; |
| 50 | - Host host = device.getHost(); | |
| 51 | 50 | // sipuri |
| 52 | - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), host.getAddress()); | |
| 51 | + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); | |
| 53 | 52 | // via |
| 54 | 53 | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
| 55 | 54 | ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), |
| ... | ... | @@ -75,22 +74,21 @@ public class SIPRequestHeaderProvider { |
| 75 | 74 | |
| 76 | 75 | request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, |
| 77 | 76 | toHeader, viaHeaders, maxForwards); |
| 78 | - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); | |
| 77 | + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); | |
| 79 | 78 | request.setContent(content, contentTypeHeader); |
| 80 | 79 | return request; |
| 81 | 80 | } |
| 82 | 81 | |
| 83 | 82 | public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| 84 | 83 | Request request = null; |
| 85 | - Host host = device.getHost(); | |
| 86 | 84 | //请求行 |
| 87 | - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, host.getAddress()); | |
| 85 | + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); | |
| 88 | 86 | //via |
| 89 | 87 | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
| 90 | - // ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag); | |
| 91 | - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getHost().getIp(), device.getHost().getPort(), device.getTransport(), viaTag); | |
| 88 | + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); | |
| 92 | 89 | viaHeader.setRPort(); |
| 93 | 90 | viaHeaders.add(viaHeader); |
| 91 | + | |
| 94 | 92 | //from |
| 95 | 93 | SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); |
| 96 | 94 | Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); |
| ... | ... | @@ -122,20 +120,18 @@ public class SIPRequestHeaderProvider { |
| 122 | 120 | // Subject |
| 123 | 121 | SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getSipId(), 0)); |
| 124 | 122 | request.addHeader(subjectHeader); |
| 125 | - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "SDP"); | |
| 123 | + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); | |
| 126 | 124 | request.setContent(content, contentTypeHeader); |
| 127 | 125 | return request; |
| 128 | 126 | } |
| 129 | 127 | |
| 130 | 128 | public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| 131 | 129 | Request request = null; |
| 132 | - Host host = device.getHost(); | |
| 133 | 130 | //请求行 |
| 134 | - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), host.getAddress()); | |
| 135 | - //via | |
| 131 | + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); | |
| 132 | + // via | |
| 136 | 133 | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
| 137 | - // ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag); | |
| 138 | - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getHost().getIp(), device.getHost().getPort(), device.getTransport(), viaTag); | |
| 134 | + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), device.getTransport(), viaTag); | |
| 139 | 135 | viaHeader.setRPort(); |
| 140 | 136 | viaHeaders.add(viaHeader); |
| 141 | 137 | //from |
| ... | ... | @@ -167,7 +163,7 @@ public class SIPRequestHeaderProvider { |
| 167 | 163 | // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), device.getHost().getIp()+":"+device.getHost().getPort())); |
| 168 | 164 | request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); |
| 169 | 165 | |
| 170 | - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "SDP"); | |
| 166 | + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); | |
| 171 | 167 | request.setContent(content, contentTypeHeader); |
| 172 | 168 | return request; |
| 173 | 169 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; |
| 2 | 2 | |
| 3 | 3 | import java.text.ParseException; |
| 4 | +import java.util.UUID; | |
| 4 | 5 | import java.util.regex.Matcher; |
| 5 | 6 | import java.util.regex.Pattern; |
| 6 | 7 | |
| 7 | -import javax.sip.ClientTransaction; | |
| 8 | -import javax.sip.Dialog; | |
| 9 | -import javax.sip.InvalidArgumentException; | |
| 10 | -import javax.sip.SipException; | |
| 11 | -import javax.sip.SipFactory; | |
| 12 | -import javax.sip.SipProvider; | |
| 13 | -import javax.sip.TransactionDoesNotExistException; | |
| 8 | +import javax.sip.*; | |
| 14 | 9 | import javax.sip.address.SipURI; |
| 10 | +import javax.sip.header.CallIdHeader; | |
| 11 | +import javax.sip.header.Header; | |
| 15 | 12 | import javax.sip.header.ViaHeader; |
| 16 | 13 | import javax.sip.message.Request; |
| 17 | 14 | |
| ... | ... | @@ -19,9 +16,13 @@ import com.alibaba.fastjson.JSONObject; |
| 19 | 16 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 20 | 17 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 21 | 18 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 19 | +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | |
| 22 | 20 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 23 | -import com.genersoft.iot.vmp.media.zlm.ZLMUtils; | |
| 21 | +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | |
| 22 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 24 | 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 24 | +import org.slf4j.Logger; | |
| 25 | +import org.slf4j.LoggerFactory; | |
| 25 | 26 | import org.springframework.beans.factory.annotation.Autowired; |
| 26 | 27 | import org.springframework.beans.factory.annotation.Qualifier; |
| 27 | 28 | import org.springframework.beans.factory.annotation.Value; |
| ... | ... | @@ -41,6 +42,8 @@ import com.genersoft.iot.vmp.gb28181.utils.DateUtil; |
| 41 | 42 | */ |
| 42 | 43 | @Component |
| 43 | 44 | public class SIPCommander implements ISIPCommander { |
| 45 | + | |
| 46 | + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); | |
| 44 | 47 | |
| 45 | 48 | @Autowired |
| 46 | 49 | private SipConfig sipConfig; |
| ... | ... | @@ -53,6 +56,9 @@ public class SIPCommander implements ISIPCommander { |
| 53 | 56 | |
| 54 | 57 | @Autowired |
| 55 | 58 | private IVideoManagerStorager storager; |
| 59 | + | |
| 60 | + @Autowired | |
| 61 | + private IRedisCatchStorage redisCatchStorage; | |
| 56 | 62 | |
| 57 | 63 | @Autowired |
| 58 | 64 | @Qualifier(value="tcpSipProvider") |
| ... | ... | @@ -63,14 +69,20 @@ public class SIPCommander implements ISIPCommander { |
| 63 | 69 | private SipProvider udpSipProvider; |
| 64 | 70 | |
| 65 | 71 | @Autowired |
| 66 | - private ZLMUtils zlmUtils; | |
| 72 | + private ZLMRTPServerFactory zlmrtpServerFactory; | |
| 67 | 73 | |
| 68 | 74 | @Value("${media.rtp.enable}") |
| 69 | 75 | private boolean rtpEnable; |
| 70 | 76 | |
| 77 | + @Value("${media.seniorSdp}") | |
| 78 | + private boolean seniorSdp; | |
| 79 | + | |
| 71 | 80 | @Autowired |
| 72 | 81 | private ZLMHttpHookSubscribe subscribe; |
| 73 | 82 | |
| 83 | + @Autowired | |
| 84 | + private SipSubscribe sipSubscribe; | |
| 85 | + | |
| 74 | 86 | |
| 75 | 87 | |
| 76 | 88 | /** |
| ... | ... | @@ -176,19 +188,29 @@ public class SIPCommander implements ISIPCommander { |
| 176 | 188 | * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) |
| 177 | 189 | * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) |
| 178 | 190 | */ |
| 179 | - public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { | |
| 191 | + | |
| 192 | + /** | |
| 193 | + * 云台指令码计算 | |
| 194 | + * | |
| 195 | + * @param cmdCode 指令码 | |
| 196 | + * @param horizonSpeed 水平移动速度 | |
| 197 | + * @param verticalSpeed 垂直移动速度 | |
| 198 | + * @param zoomSpeed 缩放速度 | |
| 199 | + * @return | |
| 200 | + */ | |
| 201 | + public static String frontEndCmdString(int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed) { | |
| 180 | 202 | StringBuilder builder = new StringBuilder("A50F01"); |
| 181 | 203 | String strTmp; |
| 182 | 204 | strTmp = String.format("%02X", cmdCode); |
| 183 | 205 | builder.append(strTmp, 0, 2); |
| 184 | - strTmp = String.format("%02X", parameter1); | |
| 206 | + strTmp = String.format("%02X", horizonSpeed); | |
| 185 | 207 | builder.append(strTmp, 0, 2); |
| 186 | - strTmp = String.format("%02X", parameter2); | |
| 208 | + strTmp = String.format("%02X", verticalSpeed); | |
| 187 | 209 | builder.append(strTmp, 0, 2); |
| 188 | - strTmp = String.format("%X", combineCode2); | |
| 210 | + strTmp = String.format("%X", zoomSpeed); | |
| 189 | 211 | builder.append(strTmp, 0, 1).append("0"); |
| 190 | 212 | //计算校验码 |
| 191 | - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; | |
| 213 | + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + horizonSpeed + verticalSpeed + (zoomSpeed & 0XF0)) % 0X100; | |
| 192 | 214 | strTmp = String.format("%02X", checkCode); |
| 193 | 215 | builder.append(strTmp, 0, 2); |
| 194 | 216 | return builder.toString(); |
| ... | ... | @@ -237,14 +259,14 @@ public class SIPCommander implements ISIPCommander { |
| 237 | 259 | * @param device 控制设备 |
| 238 | 260 | * @param channelId 预览通道 |
| 239 | 261 | * @param cmdCode 指令码 |
| 240 | - * @param parameter1 数据1 | |
| 241 | - * @param parameter2 数据2 | |
| 242 | - * @param combineCode2 组合码2 | |
| 262 | + * @param horizonSpeed 水平移动速度 | |
| 263 | + * @param verticalSpeed 垂直移动速度 | |
| 264 | + * @param zoomSpeed 缩放速度 | |
| 243 | 265 | */ |
| 244 | 266 | @Override |
| 245 | - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) { | |
| 267 | + public boolean frontEndCmd(Device device, String channelId, int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed) { | |
| 246 | 268 | try { |
| 247 | - String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); | |
| 269 | + String cmdStr= frontEndCmdString(cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); | |
| 248 | 270 | System.out.println("控制字符串:" + cmdStr); |
| 249 | 271 | StringBuffer ptzXml = new StringBuffer(200); |
| 250 | 272 | ptzXml.append("<?xml version=\"1.0\" ?>\r\n"); |
| ... | ... | @@ -258,7 +280,6 @@ public class SIPCommander implements ISIPCommander { |
| 258 | 280 | ptzXml.append("</Control>\r\n"); |
| 259 | 281 | |
| 260 | 282 | Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "ViaPtzBranch", "FromPtzTag", "ToPtzTag"); |
| 261 | - | |
| 262 | 283 | transmitRequest(device, request); |
| 263 | 284 | return true; |
| 264 | 285 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -266,28 +287,39 @@ public class SIPCommander implements ISIPCommander { |
| 266 | 287 | } |
| 267 | 288 | return false; |
| 268 | 289 | } |
| 290 | + | |
| 269 | 291 | /** |
| 270 | - * 请求预览视频流 | |
| 271 | - * | |
| 292 | + * 请求预览视频流 | |
| 272 | 293 | * @param device 视频设备 |
| 273 | 294 | * @param channelId 预览通道 |
| 295 | + * @param event hook订阅 | |
| 296 | + * @param errorEvent sip错误订阅 | |
| 274 | 297 | */ |
| 275 | 298 | @Override |
| 276 | - public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event) { | |
| 299 | + public void playStreamCmd(Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { | |
| 277 | 300 | try { |
| 278 | 301 | |
| 279 | 302 | String ssrc = streamSession.createPlaySsrc(); |
| 303 | + String streamId = null; | |
| 304 | + if (rtpEnable) { | |
| 305 | + streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); | |
| 306 | + }else { | |
| 307 | + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | |
| 308 | + } | |
| 280 | 309 | String streamMode = device.getStreamMode().toUpperCase(); |
| 281 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 310 | + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | |
| 311 | + if (mediaInfo == null) { | |
| 312 | + logger.warn("点播时发现ZLM尚未连接..."); | |
| 313 | + return; | |
| 314 | + } | |
| 282 | 315 | String mediaPort = null; |
| 283 | 316 | // 使用动态udp端口 |
| 284 | 317 | if (rtpEnable) { |
| 285 | - mediaPort = zlmUtils.getNewRTPPort(ssrc) + ""; | |
| 318 | + mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; | |
| 286 | 319 | }else { |
| 287 | 320 | mediaPort = mediaInfo.getRtpProxyPort(); |
| 288 | 321 | } |
| 289 | 322 | |
| 290 | - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | |
| 291 | 323 | // 添加订阅 |
| 292 | 324 | JSONObject subscribeKey = new JSONObject(); |
| 293 | 325 | subscribeKey.put("app", "rtp"); |
| ... | ... | @@ -297,7 +329,8 @@ public class SIPCommander implements ISIPCommander { |
| 297 | 329 | // |
| 298 | 330 | StringBuffer content = new StringBuffer(200); |
| 299 | 331 | content.append("v=0\r\n"); |
| 300 | - content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | |
| 332 | +// content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | |
| 333 | + content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); | |
| 301 | 334 | content.append("s=Play\r\n"); |
| 302 | 335 | content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n"); |
| 303 | 336 | content.append("t=0 0\r\n"); |
| ... | ... | @@ -327,17 +360,14 @@ public class SIPCommander implements ISIPCommander { |
| 327 | 360 | } |
| 328 | 361 | content.append("y="+ssrc+"\r\n");//ssrc |
| 329 | 362 | |
| 363 | +// String fromTag = UUID.randomUUID().toString(); | |
| 364 | +// Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, fromTag, null, ssrc); | |
| 365 | + | |
| 330 | 366 | Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null, ssrc); |
| 331 | 367 | |
| 332 | - ClientTransaction transaction = transmitRequest(device, request); | |
| 333 | - streamSession.put(ssrc, transaction); | |
| 334 | - DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); | |
| 335 | - if (deviceChannel != null) { | |
| 336 | - deviceChannel.setSsrc(ssrc); | |
| 337 | - storager.updateChannel(device.getDeviceId(), deviceChannel); | |
| 338 | - } | |
| 368 | + ClientTransaction transaction = transmitRequest(device, request, errorEvent); | |
| 369 | + streamSession.put(streamId, transaction); | |
| 339 | 370 | |
| 340 | - // TODO 订阅SIP response,处理对方的错误返回 | |
| 341 | 371 | |
| 342 | 372 | |
| 343 | 373 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -354,9 +384,10 @@ public class SIPCommander implements ISIPCommander { |
| 354 | 384 | * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss |
| 355 | 385 | */ |
| 356 | 386 | @Override |
| 357 | - public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event) { | |
| 387 | + public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event | |
| 388 | + , SipSubscribe.Event errorEvent) { | |
| 358 | 389 | try { |
| 359 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 390 | + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | |
| 360 | 391 | String ssrc = streamSession.createPlayBackSsrc(); |
| 361 | 392 | String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); |
| 362 | 393 | // 添加订阅 |
| ... | ... | @@ -378,57 +409,91 @@ public class SIPCommander implements ISIPCommander { |
| 378 | 409 | String mediaPort = null; |
| 379 | 410 | // 使用动态udp端口 |
| 380 | 411 | if (rtpEnable) { |
| 381 | - mediaPort = zlmUtils.getNewRTPPort(ssrc) + ""; | |
| 412 | + mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; | |
| 382 | 413 | }else { |
| 383 | 414 | mediaPort = mediaInfo.getRtpProxyPort(); |
| 384 | 415 | } |
| 385 | 416 | String streamMode = device.getStreamMode().toUpperCase(); |
| 386 | - if("TCP-PASSIVE".equals(streamMode)) { | |
| 387 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 388 | - }else if ("TCP-ACTIVE".equals(streamMode)) { | |
| 389 | - content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 390 | - }else if("UDP".equals(streamMode)) { | |
| 391 | - content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 392 | - } | |
| 393 | - content.append("a=recvonly\r\n"); | |
| 394 | - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); | |
| 395 | - content.append("a=rtpmap:126 H264/90000\r\n"); | |
| 396 | - content.append("a=rtpmap:125 H264S/90000\r\n"); | |
| 397 | - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); | |
| 398 | - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); | |
| 399 | - content.append("a=fmtp:99 profile-level-id=3\r\n"); | |
| 400 | - content.append("a=rtpmap:98 H264/90000\r\n"); | |
| 401 | - content.append("a=rtpmap:97 MPEG4/90000\r\n"); | |
| 402 | - content.append("a=rtpmap:96 PS/90000\r\n"); | |
| 403 | - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | |
| 404 | - content.append("a=setup:passive\r\n"); | |
| 405 | - content.append("a=connection:new\r\n"); | |
| 406 | - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 | |
| 407 | - content.append("a=setup:active\r\n"); | |
| 408 | - content.append("a=connection:new\r\n"); | |
| 417 | + | |
| 418 | + if (seniorSdp) { | |
| 419 | + if("TCP-PASSIVE".equals(streamMode)) { | |
| 420 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 421 | + }else if ("TCP-ACTIVE".equals(streamMode)) { | |
| 422 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 423 | + }else if("UDP".equals(streamMode)) { | |
| 424 | + content.append("m=video "+ mediaPort +" RTP/AVP 126 125 99 34 98 97 96\r\n"); | |
| 425 | + } | |
| 426 | + content.append("a=recvonly\r\n"); | |
| 427 | + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); | |
| 428 | + content.append("a=rtpmap:126 H264/90000\r\n"); | |
| 429 | + content.append("a=rtpmap:125 H264S/90000\r\n"); | |
| 430 | + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); | |
| 431 | + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); | |
| 432 | + content.append("a=fmtp:99 profile-level-id=3\r\n"); | |
| 433 | + content.append("a=rtpmap:98 H264/90000\r\n"); | |
| 434 | + content.append("a=rtpmap:97 MPEG4/90000\r\n"); | |
| 435 | + content.append("a=rtpmap:96 PS/90000\r\n"); | |
| 436 | + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | |
| 437 | + content.append("a=setup:passive\r\n"); | |
| 438 | + content.append("a=connection:new\r\n"); | |
| 439 | + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 | |
| 440 | + content.append("a=setup:active\r\n"); | |
| 441 | + content.append("a=connection:new\r\n"); | |
| 442 | + } | |
| 443 | + }else { | |
| 444 | + if("TCP-PASSIVE".equals(streamMode)) { | |
| 445 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | |
| 446 | + }else if ("TCP-ACTIVE".equals(streamMode)) { | |
| 447 | + content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n"); | |
| 448 | + }else if("UDP".equals(streamMode)) { | |
| 449 | + content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n"); | |
| 450 | + } | |
| 451 | + content.append("a=recvonly\r\n"); | |
| 452 | + content.append("a=rtpmap:96 PS/90000\r\n"); | |
| 453 | + content.append("a=rtpmap:98 H264/90000\r\n"); | |
| 454 | + content.append("a=rtpmap:97 MPEG4/90000\r\n"); | |
| 455 | + if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 | |
| 456 | + content.append("a=setup:passive\r\n"); | |
| 457 | + content.append("a=connection:new\r\n"); | |
| 458 | + }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 | |
| 459 | + content.append("a=setup:active\r\n"); | |
| 460 | + content.append("a=connection:new\r\n"); | |
| 461 | + } | |
| 409 | 462 | } |
| 463 | + | |
| 410 | 464 | content.append("y="+ssrc+"\r\n");//ssrc |
| 411 | 465 | |
| 412 | 466 | Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "playback", null); |
| 413 | - | |
| 414 | - ClientTransaction transaction = transmitRequest(device, request); | |
| 415 | - streamSession.put(ssrc, transaction); | |
| 467 | + | |
| 468 | + ClientTransaction transaction = transmitRequest(device, request, errorEvent); | |
| 469 | + streamSession.put(streamId, transaction); | |
| 416 | 470 | |
| 417 | 471 | } catch ( SipException | ParseException | InvalidArgumentException e) { |
| 418 | 472 | e.printStackTrace(); |
| 419 | 473 | } |
| 420 | 474 | } |
| 421 | - | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 422 | 478 | /** |
| 423 | 479 | * 视频流停止 |
| 424 | 480 | * |
| 425 | 481 | */ |
| 426 | 482 | @Override |
| 427 | 483 | public void streamByeCmd(String ssrc) { |
| 484 | + streamByeCmd(ssrc, null); | |
| 485 | + } | |
| 486 | + @Override | |
| 487 | + public void streamByeCmd(String streamId, SipSubscribe.Event okEvent) { | |
| 428 | 488 | |
| 429 | 489 | try { |
| 430 | - ClientTransaction transaction = streamSession.get(ssrc); | |
| 490 | + ClientTransaction transaction = streamSession.get(streamId); | |
| 491 | + // 服务重启后 | |
| 431 | 492 | if (transaction == null) { |
| 493 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 494 | + if (streamInfo != null) { | |
| 495 | + | |
| 496 | + } | |
| 432 | 497 | return; |
| 433 | 498 | } |
| 434 | 499 | |
| ... | ... | @@ -436,6 +501,9 @@ public class SIPCommander implements ISIPCommander { |
| 436 | 501 | if (dialog == null) { |
| 437 | 502 | return; |
| 438 | 503 | } |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 439 | 507 | Request byeRequest = dialog.createRequest(Request.BYE); |
| 440 | 508 | SipURI byeURI = (SipURI) byeRequest.getRequestURI(); |
| 441 | 509 | String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString(); |
| ... | ... | @@ -452,8 +520,16 @@ public class SIPCommander implements ISIPCommander { |
| 452 | 520 | } else if("UDP".equals(protocol)) { |
| 453 | 521 | clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); |
| 454 | 522 | } |
| 523 | + | |
| 524 | + CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); | |
| 525 | + if (okEvent != null) { | |
| 526 | + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); | |
| 527 | + } | |
| 528 | + | |
| 455 | 529 | dialog.sendRequest(clientTransaction); |
| 456 | - streamSession.remove(ssrc); | |
| 530 | + | |
| 531 | + streamSession.remove(streamId); | |
| 532 | + zlmrtpServerFactory.closeRTPServer(streamId); | |
| 457 | 533 | } catch (TransactionDoesNotExistException e) { |
| 458 | 534 | e.printStackTrace(); |
| 459 | 535 | } catch (SipException e) { |
| ... | ... | @@ -571,6 +647,7 @@ public class SIPCommander implements ISIPCommander { |
| 571 | 647 | catalogXml.append("</Query>\r\n"); |
| 572 | 648 | |
| 573 | 649 | Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaDeviceInfoBranch", "FromDeviceInfoTag", "ToDeviceInfoTag"); |
| 650 | + | |
| 574 | 651 | transmitRequest(device, request); |
| 575 | 652 | |
| 576 | 653 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| ... | ... | @@ -586,7 +663,7 @@ public class SIPCommander implements ISIPCommander { |
| 586 | 663 | * @param device 视频设备 |
| 587 | 664 | */ |
| 588 | 665 | @Override |
| 589 | - public boolean catalogQuery(Device device) { | |
| 666 | + public boolean catalogQuery(Device device, SipSubscribe.Event errorEvent) { | |
| 590 | 667 | // 清空通道 |
| 591 | 668 | storager.cleanChannelsForDevice(device.getDeviceId()); |
| 592 | 669 | try { |
| ... | ... | @@ -598,8 +675,9 @@ public class SIPCommander implements ISIPCommander { |
| 598 | 675 | catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n"); |
| 599 | 676 | catalogXml.append("</Query>\r\n"); |
| 600 | 677 | |
| 601 | - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCatalogTag", "ToCatalogTag"); | |
| 602 | - transmitRequest(device, request); | |
| 678 | + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaCatalogBranch", "FromCatalogTag", null); | |
| 679 | + | |
| 680 | + transmitRequest(device, request, errorEvent); | |
| 603 | 681 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| 604 | 682 | e.printStackTrace(); |
| 605 | 683 | return false; |
| ... | ... | @@ -631,7 +709,8 @@ public class SIPCommander implements ISIPCommander { |
| 631 | 709 | recordInfoXml.append("<Type>all</Type>\r\n"); |
| 632 | 710 | recordInfoXml.append("</Query>\r\n"); |
| 633 | 711 | |
| 634 | - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", "ToRecordInfoTag"); | |
| 712 | + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", null); | |
| 713 | + | |
| 635 | 714 | transmitRequest(device, request); |
| 636 | 715 | } catch (SipException | ParseException | InvalidArgumentException e) { |
| 637 | 716 | e.printStackTrace(); |
| ... | ... | @@ -683,17 +762,45 @@ public class SIPCommander implements ISIPCommander { |
| 683 | 762 | // TODO Auto-generated method stub |
| 684 | 763 | return false; |
| 685 | 764 | } |
| 686 | - | |
| 765 | + | |
| 687 | 766 | private ClientTransaction transmitRequest(Device device, Request request) throws SipException { |
| 767 | + return transmitRequest(device, request, null, null); | |
| 768 | + } | |
| 769 | + | |
| 770 | + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException { | |
| 771 | + return transmitRequest(device, request, errorEvent, null); | |
| 772 | + } | |
| 773 | + | |
| 774 | + private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException { | |
| 688 | 775 | ClientTransaction clientTransaction = null; |
| 689 | 776 | if("TCP".equals(device.getTransport())) { |
| 690 | 777 | clientTransaction = tcpSipProvider.getNewClientTransaction(request); |
| 691 | 778 | } else if("UDP".equals(device.getTransport())) { |
| 692 | 779 | clientTransaction = udpSipProvider.getNewClientTransaction(request); |
| 693 | 780 | } |
| 781 | + | |
| 782 | + CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME); | |
| 783 | + // 添加错误订阅 | |
| 784 | + if (errorEvent != null) { | |
| 785 | + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent); | |
| 786 | + } | |
| 787 | + // 添加订阅 | |
| 788 | + if (okEvent != null) { | |
| 789 | + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); | |
| 790 | + } | |
| 791 | + | |
| 694 | 792 | clientTransaction.sendRequest(); |
| 695 | 793 | return clientTransaction; |
| 696 | 794 | } |
| 697 | 795 | |
| 698 | 796 | |
| 797 | + | |
| 798 | + | |
| 799 | + @Override | |
| 800 | + public void closeRTPServer(Device device, String channelId) { | |
| 801 | + if (rtpEnable) { | |
| 802 | + String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); | |
| 803 | + zlmrtpServerFactory.closeRTPServer(streamId); | |
| 804 | + } | |
| 805 | + } | |
| 699 | 806 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
| ... | ... | @@ -10,6 +10,7 @@ import javax.sip.SipException; |
| 10 | 10 | import javax.sip.message.Request; |
| 11 | 11 | import javax.sip.message.Response; |
| 12 | 12 | |
| 13 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 13 | 14 | import org.dom4j.Document; |
| 14 | 15 | import org.dom4j.DocumentException; |
| 15 | 16 | import org.dom4j.Element; |
| ... | ... | @@ -48,6 +49,8 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 48 | 49 | |
| 49 | 50 | private IVideoManagerStorager storager; |
| 50 | 51 | |
| 52 | + private IRedisCatchStorage redisCatchStorage; | |
| 53 | + | |
| 51 | 54 | private EventPublisher publisher; |
| 52 | 55 | |
| 53 | 56 | private RedisUtil redis; |
| ... | ... | @@ -294,7 +297,7 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 294 | 297 | device.setStreamMode("UDP"); |
| 295 | 298 | } |
| 296 | 299 | storager.updateDevice(device); |
| 297 | - cmder.catalogQuery(device); | |
| 300 | + cmder.catalogQuery(device, null); | |
| 298 | 301 | // 回复200 OK |
| 299 | 302 | responseAck(evt); |
| 300 | 303 | if (offLineDetector.isOnline(deviceId)) { |
| ... | ... | @@ -315,12 +318,16 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 315 | 318 | try { |
| 316 | 319 | Element rootElement = getRootElement(evt); |
| 317 | 320 | String deviceId = XmlUtil.getText(rootElement, "DeviceID"); |
| 318 | - // 回复200 OK | |
| 319 | - responseAck(evt); | |
| 320 | - if (offLineDetector.isOnline(deviceId)) { | |
| 321 | - publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); | |
| 322 | - } else { | |
| 321 | + // 检查设备是否存在, 不存在则不回复 | |
| 322 | + if (storager.exists(deviceId)) { | |
| 323 | + // 回复200 OK | |
| 324 | + responseAck(evt); | |
| 325 | + if (offLineDetector.isOnline(deviceId)) { | |
| 326 | + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); | |
| 327 | + } else { | |
| 328 | + } | |
| 323 | 329 | } |
| 330 | + | |
| 324 | 331 | } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { |
| 325 | 332 | e.printStackTrace(); |
| 326 | 333 | } |
| ... | ... | @@ -447,10 +454,10 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 447 | 454 | String NotifyType =XmlUtil.getText(rootElement, "NotifyType"); |
| 448 | 455 | if (NotifyType.equals("121")){ |
| 449 | 456 | logger.info("媒体播放完毕,通知关流"); |
| 450 | - StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, "*"); | |
| 457 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, "*"); | |
| 451 | 458 | if (streamInfo != null) { |
| 452 | - storager.stopPlayback(streamInfo); | |
| 453 | - cmder.streamByeCmd(streamInfo.getSsrc()); | |
| 459 | + redisCatchStorage.stopPlayback(streamInfo); | |
| 460 | + cmder.streamByeCmd(streamInfo.getStreamId()); | |
| 454 | 461 | } |
| 455 | 462 | } |
| 456 | 463 | } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { |
| ... | ... | @@ -503,4 +510,11 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor { |
| 503 | 510 | this.offLineDetector = offLineDetector; |
| 504 | 511 | } |
| 505 | 512 | |
| 513 | + public IRedisCatchStorage getRedisCatchStorage() { | |
| 514 | + return redisCatchStorage; | |
| 515 | + } | |
| 516 | + | |
| 517 | + public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { | |
| 518 | + this.redisCatchStorage = redisCatchStorage; | |
| 519 | + } | |
| 506 | 520 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
| ... | ... | @@ -107,17 +107,15 @@ public class RegisterRequestProcessor extends SIPRequestAbstractProcessor { |
| 107 | 107 | rPort = viaHeader.getPort(); |
| 108 | 108 | } |
| 109 | 109 | // |
| 110 | - Host host = new Host(); | |
| 111 | - host.setIp(received); | |
| 112 | - host.setPort(rPort); | |
| 113 | - host.setAddress(received.concat(":").concat(String.valueOf(rPort))); | |
| 114 | 110 | AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
| 115 | 111 | SipUri uri = (SipUri) address.getURI(); |
| 116 | 112 | String deviceId = uri.getUser(); |
| 117 | 113 | device = new Device(); |
| 118 | 114 | device.setStreamMode("UDP"); |
| 119 | 115 | device.setDeviceId(deviceId); |
| 120 | - device.setHost(host); | |
| 116 | + device.setIp(received); | |
| 117 | + device.setPort(rPort); | |
| 118 | + device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); | |
| 121 | 119 | // 注销成功 |
| 122 | 120 | if (expiresHeader != null && expiresHeader.getExpires() == 0) { |
| 123 | 121 | registerFlag = 2; |
| ... | ... | @@ -141,9 +139,15 @@ public class RegisterRequestProcessor extends SIPRequestAbstractProcessor { |
| 141 | 139 | // 下发catelog查询目录 |
| 142 | 140 | if (registerFlag == 1 && device != null) { |
| 143 | 141 | logger.info("注册成功! deviceId:" + device.getDeviceId()); |
| 142 | + boolean exists = storager.exists(device.getDeviceId()); | |
| 143 | + device.setRegisterTimeMillis(System.currentTimeMillis()); | |
| 144 | 144 | storager.updateDevice(device); |
| 145 | 145 | publisher.onlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_ONLINE_REGISTER); |
| 146 | - handler.onRegister(device); | |
| 146 | + | |
| 147 | + // 只有第一次注册才更新通道 | |
| 148 | + if (!exists) { | |
| 149 | + handler.onRegister(device); | |
| 150 | + } | |
| 147 | 151 | } else if (registerFlag == 2) { |
| 148 | 152 | logger.info("注销成功! deviceId:" + device.getDeviceId()); |
| 149 | 153 | publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
| ... | ... | @@ -12,6 +12,7 @@ import javax.sip.header.ViaHeader; |
| 12 | 12 | import javax.sip.message.Request; |
| 13 | 13 | import javax.sip.message.Response; |
| 14 | 14 | |
| 15 | +import gov.nist.javax.sip.header.CSeq; | |
| 15 | 16 | import org.slf4j.Logger; |
| 16 | 17 | import org.slf4j.LoggerFactory; |
| 17 | 18 | import org.springframework.stereotype.Component; |
| ... | ... | @@ -23,14 +24,14 @@ import com.genersoft.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor; |
| 23 | 24 | |
| 24 | 25 | |
| 25 | 26 | /** |
| 26 | - * @Description:处理INVITE响应 | |
| 27 | + * @Description:处理INVITE响应 | |
| 27 | 28 | * @author: swwheihei |
| 28 | - * @date: 2020年5月3日 下午4:43:52 | |
| 29 | + * @date: 2020年5月3日 下午4:43:52 | |
| 29 | 30 | */ |
| 30 | 31 | @Component |
| 31 | 32 | public class InviteResponseProcessor implements ISIPResponseProcessor { |
| 32 | 33 | |
| 33 | - private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class); | |
| 34 | + private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); | |
| 34 | 35 | |
| 35 | 36 | /** |
| 36 | 37 | * 处理invite响应 |
| ... | ... | @@ -49,48 +50,16 @@ public class InviteResponseProcessor implements ISIPResponseProcessor { |
| 49 | 50 | // 成功响应 |
| 50 | 51 | // 下发ack |
| 51 | 52 | if (statusCode == Response.OK) { |
| 52 | - // ClientTransaction clientTransaction = evt.getClientTransaction(); | |
| 53 | - // if(clientTransaction == null){ | |
| 54 | - // logger.error("回复ACK时,clientTransaction为null >>> {}",response); | |
| 55 | - // return; | |
| 56 | - // } | |
| 57 | - // Dialog clientDialog = clientTransaction.getDialog(); | |
| 58 | - | |
| 59 | - // CSeqHeader clientCSeqHeader = (CSeqHeader) | |
| 60 | - // response.getHeader(CSeqHeader.NAME); | |
| 61 | - // long cseqId = clientCSeqHeader.getSeqNumber(); | |
| 62 | - // /* | |
| 63 | - // createAck函数,创建的ackRequest,会采用Invite响应的200OK,中的contact字段中的地址,作为目标地址。 | |
| 64 | - // 有的终端传上来的可能还是内网地址,会造成ack发送不出去。接受不到音视频流 | |
| 65 | - // 所以在此处统一替换地址。和响应消息的Via头中的地址保持一致。 | |
| 66 | - // */ | |
| 67 | - // Request ackRequest = clientDialog.createAck(cseqId); | |
| 68 | - // SipURI requestURI = (SipURI) ackRequest.getRequestURI(); | |
| 69 | - // ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); | |
| 70 | - // try { | |
| 71 | - // requestURI.setHost(viaHeader.getHost()); | |
| 72 | - // } catch (Exception e) { | |
| 73 | - // e.printStackTrace(); | |
| 74 | - // } | |
| 75 | - // requestURI.setPort(viaHeader.getPort()); | |
| 76 | - // clientDialog.sendAck(ackRequest); | |
| 77 | - | |
| 78 | 53 | Dialog dialog = evt.getDialog(); |
| 79 | 54 | CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); |
| 80 | 55 | Request reqAck = dialog.createAck(cseq.getSeqNumber()); |
| 81 | 56 | |
| 82 | 57 | SipURI requestURI = (SipURI) reqAck.getRequestURI(); |
| 83 | 58 | ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME); |
| 84 | - // String viaHost =viaHeader.getHost(); | |
| 85 | - //getHost()函数取回的IP地址是“[xxx.xxx.xxx.xxx:yyyy]”的格式,需用正则表达式截取为“xxx.xxx.xxx.xxx"格式 | |
| 86 | - // Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+"); | |
| 87 | - // Matcher matcher = p.matcher(viaHeader.getHost()); | |
| 88 | - // if (matcher.find()) { | |
| 89 | - // requestURI.setHost(matcher.group()); | |
| 90 | - // } | |
| 91 | 59 | requestURI.setHost(viaHeader.getHost()); |
| 92 | 60 | requestURI.setPort(viaHeader.getPort()); |
| 93 | 61 | reqAck.setRequestURI(requestURI); |
| 62 | + | |
| 94 | 63 | dialog.sendAck(reqAck); |
| 95 | 64 | } |
| 96 | 65 | } catch (InvalidArgumentException | SipException e) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
| ... | ... | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.media.zlm; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | 4 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 5 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 5 | 6 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 6 | 7 | import org.slf4j.Logger; |
| 7 | 8 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -29,6 +30,9 @@ public class ZLMHTTPProxyController { |
| 29 | 30 | @Autowired |
| 30 | 31 | private IVideoManagerStorager storager; |
| 31 | 32 | |
| 33 | + @Autowired | |
| 34 | + private IRedisCatchStorage redisCatchStorage; | |
| 35 | + | |
| 32 | 36 | @Value("${media.port}") |
| 33 | 37 | private int mediaHttpPort; |
| 34 | 38 | |
| ... | ... | @@ -36,10 +40,10 @@ public class ZLMHTTPProxyController { |
| 36 | 40 | @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") |
| 37 | 41 | public Object proxy(HttpServletRequest request, HttpServletResponse response){ |
| 38 | 42 | |
| 39 | - if (storager.getMediaInfo() == null) { | |
| 43 | + if (redisCatchStorage.getMediaInfo() == null) { | |
| 40 | 44 | return "未接入流媒体"; |
| 41 | 45 | } |
| 42 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 46 | + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | |
| 43 | 47 | String requestURI = String.format("http://%s:%s%s?%s&%s", |
| 44 | 48 | mediaInfo.getLocalIP(), |
| 45 | 49 | mediaHttpPort, | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -4,13 +4,17 @@ import java.math.BigInteger; |
| 4 | 4 | import java.text.DecimalFormat; |
| 5 | 5 | import java.util.ArrayList; |
| 6 | 6 | import java.util.List; |
| 7 | +import java.util.UUID; | |
| 7 | 8 | |
| 8 | 9 | import com.alibaba.fastjson.JSON; |
| 9 | 10 | import com.alibaba.fastjson.JSONArray; |
| 10 | 11 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 11 | 12 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 13 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 14 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 12 | 15 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 13 | 16 | import com.genersoft.iot.vmp.utils.IpUtil; |
| 17 | +import com.genersoft.iot.vmp.vmanager.service.IPlayService; | |
| 14 | 18 | import org.slf4j.Logger; |
| 15 | 19 | import org.slf4j.LoggerFactory; |
| 16 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -44,14 +48,23 @@ public class ZLMHttpHookListener { |
| 44 | 48 | private SIPCommander cmder; |
| 45 | 49 | |
| 46 | 50 | @Autowired |
| 51 | + private IPlayService playService; | |
| 52 | + | |
| 53 | + @Autowired | |
| 47 | 54 | private IVideoManagerStorager storager; |
| 48 | 55 | |
| 49 | 56 | @Autowired |
| 57 | + private IRedisCatchStorage redisCatchStorage; | |
| 58 | + | |
| 59 | + @Autowired | |
| 50 | 60 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 51 | 61 | |
| 52 | 62 | @Autowired |
| 53 | 63 | private ZLMHttpHookSubscribe subscribe; |
| 54 | 64 | |
| 65 | + @Value("${media.autoApplyPlay}") | |
| 66 | + private boolean autoApplyPlay; | |
| 67 | + | |
| 55 | 68 | @Value("${media.ip}") |
| 56 | 69 | private String mediaIp; |
| 57 | 70 | |
| ... | ... | @@ -135,34 +148,6 @@ public class ZLMHttpHookListener { |
| 135 | 148 | ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); |
| 136 | 149 | if (subscribe != null) subscribe.response(json); |
| 137 | 150 | |
| 138 | -// if ("rtp".equals(app)) { | |
| 139 | -// String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | |
| 140 | -// StreamInfo streamInfoForPlay = storager.queryPlayBySSRC(ssrc); | |
| 141 | -// if ("rtp".equals(app) && streamInfoForPlay != null ) { | |
| 142 | -// MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 143 | -// streamInfoForPlay.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 144 | -// streamInfoForPlay.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 145 | -// streamInfoForPlay.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 146 | -// streamInfoForPlay.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 147 | -// streamInfoForPlay.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | |
| 148 | -// streamInfoForPlay.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 149 | -// streamInfoForPlay.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | |
| 150 | -// storager.startPlay(streamInfoForPlay); | |
| 151 | -// } | |
| 152 | -// | |
| 153 | -// StreamInfo streamInfoForPlayBack = storager.queryPlaybackBySSRC(ssrc); | |
| 154 | -// if ("rtp".equals(app) && streamInfoForPlayBack != null ) { | |
| 155 | -// MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 156 | -// streamInfoForPlayBack.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 157 | -// streamInfoForPlayBack.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 158 | -// streamInfoForPlayBack.setFmp4(String.format("http://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 159 | -// streamInfoForPlayBack.setWs_fmp4(String.format("ws://%s:%s/rtp/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 160 | -// streamInfoForPlayBack.setRtmp(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtmpPort(), streamId)); | |
| 161 | -// streamInfoForPlayBack.setHls(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId)); | |
| 162 | -// streamInfoForPlayBack.setRtsp(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId)); | |
| 163 | -// storager.startPlayback(streamInfoForPlayBack); | |
| 164 | -// } | |
| 165 | -// } | |
| 166 | 151 | |
| 167 | 152 | // TODO Auto-generated method stub |
| 168 | 153 | |
| ... | ... | @@ -268,15 +253,13 @@ public class ZLMHttpHookListener { |
| 268 | 253 | String app = json.getString("app"); |
| 269 | 254 | String streamId = json.getString("stream"); |
| 270 | 255 | boolean regist = json.getBoolean("regist"); |
| 271 | -// String ssrc = String.format("%10d", Integer.parseInt(streamId, 16)); // ZLM 要求大写且首位补零 | |
| 272 | - String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | |
| 273 | - StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); | |
| 256 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 274 | 257 | if ("rtp".equals(app) && !regist ) { |
| 275 | 258 | if (streamInfo!=null){ |
| 276 | - storager.stopPlay(streamInfo); | |
| 259 | + redisCatchStorage.stopPlay(streamInfo); | |
| 277 | 260 | }else{ |
| 278 | - streamInfo = storager.queryPlaybackBySSRC(ssrc); | |
| 279 | - storager.stopPlayback(streamInfo); | |
| 261 | + streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 262 | + redisCatchStorage.stopPlayback(streamInfo); | |
| 280 | 263 | } |
| 281 | 264 | } |
| 282 | 265 | |
| ... | ... | @@ -299,17 +282,15 @@ public class ZLMHttpHookListener { |
| 299 | 282 | logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); |
| 300 | 283 | } |
| 301 | 284 | |
| 302 | - BigInteger bigint=new BigInteger(json.getString("stream"), 16); | |
| 303 | - int numb=bigint.intValue(); | |
| 304 | - String ssrc = String.format("%010d", numb); | |
| 305 | - | |
| 306 | - cmder.streamByeCmd(ssrc); | |
| 307 | - StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); | |
| 285 | + String streamId = json.getString("stream"); | |
| 286 | + | |
| 287 | + cmder.streamByeCmd(streamId); | |
| 288 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 308 | 289 | if (streamInfo!=null){ |
| 309 | - storager.stopPlay(streamInfo); | |
| 290 | + redisCatchStorage.stopPlay(streamInfo); | |
| 310 | 291 | }else{ |
| 311 | - streamInfo = storager.queryPlaybackBySSRC(ssrc); | |
| 312 | - storager.stopPlayback(streamInfo); | |
| 292 | + streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 293 | + redisCatchStorage.stopPlayback(streamInfo); | |
| 313 | 294 | } |
| 314 | 295 | |
| 315 | 296 | JSONObject ret = new JSONObject(); |
| ... | ... | @@ -330,7 +311,31 @@ public class ZLMHttpHookListener { |
| 330 | 311 | logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); |
| 331 | 312 | } |
| 332 | 313 | // TODO Auto-generated method stub |
| 333 | - | |
| 314 | + | |
| 315 | + if (autoApplyPlay) { | |
| 316 | + String app = json.getString("app"); | |
| 317 | + String streamId = json.getString("stream"); | |
| 318 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 319 | + if ("rtp".equals(app) && streamId.indexOf("gb_play") > -1 && streamInfo == null) { | |
| 320 | + String[] s = streamId.split("_"); | |
| 321 | + if (s.length == 4) { | |
| 322 | + String deviceId = s[2]; | |
| 323 | + String channelId = s[3]; | |
| 324 | + Device device = storager.queryVideoDevice(deviceId); | |
| 325 | + if (device != null) { | |
| 326 | + UUID uuid = UUID.randomUUID(); | |
| 327 | + cmder.playStreamCmd(device, channelId, (JSONObject response) -> { | |
| 328 | + logger.info("收到订阅消息: " + response.toJSONString()); | |
| 329 | + playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); | |
| 330 | + }, null); | |
| 331 | + } | |
| 332 | + | |
| 333 | + } | |
| 334 | + | |
| 335 | + } | |
| 336 | + | |
| 337 | + } | |
| 338 | + | |
| 334 | 339 | JSONObject ret = new JSONObject(); |
| 335 | 340 | ret.put("code", 0); |
| 336 | 341 | ret.put("msg", "success"); |
| ... | ... | @@ -354,7 +359,7 @@ public class ZLMHttpHookListener { |
| 354 | 359 | // MediaServerConfig mediaServerConfig = mediaServerConfigs.get(0); |
| 355 | 360 | MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); |
| 356 | 361 | mediaServerConfig.setLocalIP(mediaIp); |
| 357 | - storager.updateMediaInfo(mediaServerConfig); | |
| 362 | + redisCatchStorage.updateMediaInfo(mediaServerConfig); | |
| 358 | 363 | // TODO Auto-generated method stub |
| 359 | 364 | |
| 360 | 365 | JSONObject ret = new JSONObject(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
| ... | ... | @@ -116,4 +116,8 @@ public class ZLMRESTfulUtils { |
| 116 | 116 | public JSONObject openRtpServer(Map<String, Object> param){ |
| 117 | 117 | return sendPost("openRtpServer",param); |
| 118 | 118 | } |
| 119 | + | |
| 120 | + public JSONObject closeRtpServer(Map<String, Object> param) { | |
| 121 | + return sendPost("closeRtpServer",param); | |
| 122 | + } | |
| 119 | 123 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMUtils.java renamed to src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| 1 | 1 | package com.genersoft.iot.vmp.media.zlm; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | +import org.slf4j.Logger; | |
| 5 | +import org.slf4j.LoggerFactory; | |
| 4 | 6 | import org.springframework.beans.factory.annotation.Autowired; |
| 5 | 7 | import org.springframework.beans.factory.annotation.Value; |
| 6 | 8 | import org.springframework.stereotype.Component; |
| ... | ... | @@ -9,7 +11,9 @@ import java.util.HashMap; |
| 9 | 11 | import java.util.Map; |
| 10 | 12 | |
| 11 | 13 | @Component |
| 12 | -public class ZLMUtils { | |
| 14 | +public class ZLMRTPServerFactory { | |
| 15 | + | |
| 16 | + private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); | |
| 13 | 17 | |
| 14 | 18 | @Value("${media.rtp.udpPortRange}") |
| 15 | 19 | private String udpPortRange; |
| ... | ... | @@ -21,19 +25,54 @@ public class ZLMUtils { |
| 21 | 25 | |
| 22 | 26 | private int currentPort = 0; |
| 23 | 27 | |
| 24 | - public int getNewRTPPort(String ssrc) { | |
| 25 | - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | |
| 28 | + public int createRTPServer(String streamId) { | |
| 26 | 29 | Map<String, Object> param = new HashMap<>(); |
| 30 | + int result = -1; | |
| 27 | 31 | int newPort = getPortFromUdpPortRange(); |
| 28 | 32 | param.put("port", newPort); |
| 29 | 33 | param.put("enable_tcp", 1); |
| 30 | 34 | param.put("stream_id", streamId); |
| 31 | 35 | JSONObject jsonObject = zlmresTfulUtils.openRtpServer(param); |
| 32 | - if (jsonObject != null && jsonObject.getInteger("code") == 0) { | |
| 33 | - return newPort; | |
| 34 | - } else { | |
| 35 | - return getNewRTPPort(ssrc); | |
| 36 | + System.out.println(jsonObject); | |
| 37 | + | |
| 38 | + if (jsonObject != null) { | |
| 39 | + switch (jsonObject.getInteger("code")){ | |
| 40 | + case 0: | |
| 41 | + result= newPort; | |
| 42 | + break; | |
| 43 | + case -300: // id已经存在 | |
| 44 | + result = newPort; | |
| 45 | + break; | |
| 46 | + case -400: // 端口占用 | |
| 47 | + result= createRTPServer(streamId); | |
| 48 | + break; | |
| 49 | + default: | |
| 50 | + logger.error("创建RTP Server 失败: " + jsonObject.getString("msg")); | |
| 51 | + break; | |
| 52 | + } | |
| 53 | + }else { | |
| 54 | + // 检查ZLM状态 | |
| 55 | + logger.error("创建RTP Server 失败: 请检查ZLM服务"); | |
| 56 | + } | |
| 57 | + return result; | |
| 58 | + } | |
| 59 | + | |
| 60 | + public boolean closeRTPServer(String streamId) { | |
| 61 | + boolean result = false; | |
| 62 | + Map<String, Object> param = new HashMap<>(); | |
| 63 | + param.put("stream_id", streamId); | |
| 64 | + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(param); | |
| 65 | + if (jsonObject != null ) { | |
| 66 | + if (jsonObject.getInteger("code") == 0) { | |
| 67 | + result = jsonObject.getInteger("hit") == 1; | |
| 68 | + }else { | |
| 69 | + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); | |
| 70 | + } | |
| 71 | + }else { | |
| 72 | + // 检查ZLM状态 | |
| 73 | + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); | |
| 36 | 74 | } |
| 75 | + return result; | |
| 37 | 76 | } |
| 38 | 77 | |
| 39 | 78 | private int getPortFromUdpPortRange() { | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
| ... | ... | @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; |
| 4 | 4 | import com.alibaba.fastjson.JSONArray; |
| 5 | 5 | import com.alibaba.fastjson.JSONObject; |
| 6 | 6 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 7 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 7 | 8 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 8 | 9 | import okhttp3.*; |
| 9 | 10 | import org.slf4j.Logger; |
| ... | ... | @@ -30,6 +31,9 @@ public class ZLMRunner implements CommandLineRunner { |
| 30 | 31 | @Autowired |
| 31 | 32 | private IVideoManagerStorager storager; |
| 32 | 33 | |
| 34 | + @Autowired | |
| 35 | + private IRedisCatchStorage redisCatchStorage; | |
| 36 | + | |
| 33 | 37 | @Value("${media.ip}") |
| 34 | 38 | private String mediaIp; |
| 35 | 39 | |
| ... | ... | @@ -69,7 +73,7 @@ public class ZLMRunner implements CommandLineRunner { |
| 69 | 73 | logger.info("zlm接入成功..."); |
| 70 | 74 | if (autoConfig) saveZLMConfig(); |
| 71 | 75 | mediaServerConfig = getMediaServerConfig(); |
| 72 | - storager.updateMediaInfo(mediaServerConfig); | |
| 76 | + redisCatchStorage.updateMediaInfo(mediaServerConfig); | |
| 73 | 77 | } |
| 74 | 78 | } |
| 75 | 79 | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.storager; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.common.StreamInfo; | |
| 4 | +import com.genersoft.iot.vmp.conf.MediaServerConfig; | |
| 5 | + | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 8 | +public interface IRedisCatchStorage { | |
| 9 | + | |
| 10 | + /** | |
| 11 | + * 开始播放时将流存入 | |
| 12 | + * | |
| 13 | + * @param stream 流信息 | |
| 14 | + * @return | |
| 15 | + */ | |
| 16 | + boolean startPlay(StreamInfo stream); | |
| 17 | + | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 停止播放时删除 | |
| 21 | + * | |
| 22 | + * @return | |
| 23 | + */ | |
| 24 | + boolean stopPlay(StreamInfo streamInfo); | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 查询播放列表 | |
| 28 | + * @return | |
| 29 | + */ | |
| 30 | + StreamInfo queryPlay(StreamInfo streamInfo); | |
| 31 | + | |
| 32 | + StreamInfo queryPlayByStreamId(String steamId); | |
| 33 | + | |
| 34 | + StreamInfo queryPlaybackByStreamId(String steamId); | |
| 35 | + | |
| 36 | + StreamInfo queryPlayByDevice(String deviceId, String code); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 更新流媒体信息 | |
| 40 | + * @param mediaServerConfig | |
| 41 | + * @return | |
| 42 | + */ | |
| 43 | + boolean updateMediaInfo(MediaServerConfig mediaServerConfig); | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 获取流媒体信息 | |
| 47 | + * @return | |
| 48 | + */ | |
| 49 | + MediaServerConfig getMediaInfo(); | |
| 50 | + | |
| 51 | + Map<String, StreamInfo> queryPlayByDeviceId(String deviceId); | |
| 52 | + | |
| 53 | + boolean startPlayback(StreamInfo stream); | |
| 54 | + | |
| 55 | + boolean stopPlayback(StreamInfo streamInfo); | |
| 56 | + | |
| 57 | + StreamInfo queryPlaybackByDevice(String deviceId, String code); | |
| 58 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
| 1 | 1 | package com.genersoft.iot.vmp.storager; |
| 2 | 2 | |
| 3 | 3 | import java.util.List; |
| 4 | -import java.util.Map; | |
| 5 | 4 | |
| 6 | -import com.alibaba.fastjson.JSONObject; | |
| 7 | -import com.genersoft.iot.vmp.common.PageResult; | |
| 8 | -import com.genersoft.iot.vmp.common.StreamInfo; | |
| 9 | -import com.genersoft.iot.vmp.conf.MediaServerConfig; | |
| 10 | 5 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 11 | 6 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 12 | -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | |
| 7 | +import com.github.pagehelper.PageInfo; | |
| 13 | 8 | |
| 14 | 9 | /** |
| 15 | 10 | * @Description:视频设备数据存储接口 |
| ... | ... | @@ -18,19 +13,6 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 18 | 13 | */ |
| 19 | 14 | public interface IVideoManagerStorager { |
| 20 | 15 | |
| 21 | - /** | |
| 22 | - * 更新流媒体信息 | |
| 23 | - * @param mediaServerConfig | |
| 24 | - * @return | |
| 25 | - */ | |
| 26 | - public boolean updateMediaInfo(MediaServerConfig mediaServerConfig); | |
| 27 | - | |
| 28 | - /** | |
| 29 | - * 获取流媒体信息 | |
| 30 | - * @return | |
| 31 | - */ | |
| 32 | - public MediaServerConfig getMediaInfo(); | |
| 33 | - | |
| 34 | 16 | /** |
| 35 | 17 | * 根据设备ID判断设备是否存在 |
| 36 | 18 | * |
| ... | ... | @@ -79,7 +61,7 @@ public interface IVideoManagerStorager { |
| 79 | 61 | * @param count 每页数量 |
| 80 | 62 | * @return |
| 81 | 63 | */ |
| 82 | - public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count); | |
| 64 | + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count); | |
| 83 | 65 | |
| 84 | 66 | /** |
| 85 | 67 | * 获取某个设备的通道列表 |
| ... | ... | @@ -88,6 +70,7 @@ public interface IVideoManagerStorager { |
| 88 | 70 | * @return |
| 89 | 71 | */ |
| 90 | 72 | public List<DeviceChannel> queryChannelsByDeviceId(String deviceId); |
| 73 | + | |
| 91 | 74 | /** |
| 92 | 75 | * 获取某个设备的通道 |
| 93 | 76 | * @param deviceId 设备ID |
| ... | ... | @@ -95,21 +78,20 @@ public interface IVideoManagerStorager { |
| 95 | 78 | */ |
| 96 | 79 | public DeviceChannel queryChannel(String deviceId, String channelId); |
| 97 | 80 | |
| 98 | - /** | |
| 81 | + /** | |
| 99 | 82 | * 获取多个设备 |
| 100 | - * | |
| 101 | - * @param deviceIds 设备ID数组 | |
| 83 | + * @param page 当前页数 | |
| 84 | + * @param count 每页数量 | |
| 102 | 85 | * @return List<Device> 设备对象数组 |
| 103 | 86 | */ |
| 104 | - public PageResult<Device> queryVideoDeviceList(String[] deviceIds, int page, int count); | |
| 87 | + public PageInfo<Device> queryVideoDeviceList(int page, int count); | |
| 105 | 88 | |
| 106 | 89 | /** |
| 107 | 90 | * 获取多个设备 |
| 108 | 91 | * |
| 109 | - * @param deviceIds 设备ID数组 | |
| 110 | 92 | * @return List<Device> 设备对象数组 |
| 111 | 93 | */ |
| 112 | - public List<Device> queryVideoDeviceList(String[] deviceIds); | |
| 94 | + public List<Device> queryVideoDeviceList(); | |
| 113 | 95 | |
| 114 | 96 | /** |
| 115 | 97 | * 删除设备 |
| ... | ... | @@ -135,27 +117,6 @@ public interface IVideoManagerStorager { |
| 135 | 117 | */ |
| 136 | 118 | public boolean outline(String deviceId); |
| 137 | 119 | |
| 138 | - /** | |
| 139 | - * 开始播放时将流存入 | |
| 140 | - * | |
| 141 | - * @param stream 流信息 | |
| 142 | - * @return | |
| 143 | - */ | |
| 144 | - public boolean startPlay(StreamInfo stream); | |
| 145 | - | |
| 146 | - /** | |
| 147 | - * 停止播放时删除 | |
| 148 | - * | |
| 149 | - * @return | |
| 150 | - */ | |
| 151 | - public boolean stopPlay(StreamInfo streamInfo); | |
| 152 | - | |
| 153 | - /** | |
| 154 | - * 查找视频流 | |
| 155 | - * | |
| 156 | - * @return | |
| 157 | - */ | |
| 158 | - public StreamInfo queryPlay(StreamInfo streamInfo); | |
| 159 | 120 | |
| 160 | 121 | /** |
| 161 | 122 | * 查询子设备 |
| ... | ... | @@ -166,12 +127,8 @@ public interface IVideoManagerStorager { |
| 166 | 127 | * @param count |
| 167 | 128 | * @return |
| 168 | 129 | */ |
| 169 | - PageResult querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); | |
| 130 | + PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count); | |
| 170 | 131 | |
| 171 | - /** | |
| 172 | - * 更新缓存 | |
| 173 | - */ | |
| 174 | - public void updateCatch(); | |
| 175 | 132 | |
| 176 | 133 | /** |
| 177 | 134 | * 清空通道 |
| ... | ... | @@ -179,45 +136,4 @@ public interface IVideoManagerStorager { |
| 179 | 136 | */ |
| 180 | 137 | void cleanChannelsForDevice(String deviceId); |
| 181 | 138 | |
| 182 | - StreamInfo queryPlayBySSRC(String ssrc); | |
| 183 | - | |
| 184 | - StreamInfo queryPlayByDevice(String deviceId, String code); | |
| 185 | - | |
| 186 | - Map<String, StreamInfo> queryPlayByDeviceId(String deviceId); | |
| 187 | - | |
| 188 | - boolean startPlayback(StreamInfo streamInfo); | |
| 189 | - | |
| 190 | - boolean stopPlayback(StreamInfo streamInfo); | |
| 191 | - | |
| 192 | - StreamInfo queryPlaybackByDevice(String deviceId, String channelId); | |
| 193 | - | |
| 194 | - StreamInfo queryPlaybackBySSRC(String ssrc); | |
| 195 | - | |
| 196 | - /** | |
| 197 | - * 更新或添加上级平台 | |
| 198 | - * @param parentPlatform | |
| 199 | - */ | |
| 200 | - boolean updateParentPlatform(ParentPlatform parentPlatform); | |
| 201 | - | |
| 202 | - /** | |
| 203 | - * 删除上级平台 | |
| 204 | - * @param parentPlatform | |
| 205 | - */ | |
| 206 | - boolean deleteParentPlatform(ParentPlatform parentPlatform); | |
| 207 | - | |
| 208 | - | |
| 209 | - /** | |
| 210 | - * 分页获取上级平台 | |
| 211 | - * @param page | |
| 212 | - * @param count | |
| 213 | - * @return | |
| 214 | - */ | |
| 215 | - public PageResult<ParentPlatform> queryParentPlatformList(int page, int count); | |
| 216 | - | |
| 217 | - /** | |
| 218 | - * 获取上级平台 | |
| 219 | - * @param platformGbId | |
| 220 | - * @return | |
| 221 | - */ | |
| 222 | - public ParentPlatform queryParentPlatById(String platformGbId); | |
| 223 | 139 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/VideoManagerStoragerFactory.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.storager; | |
| 2 | - | |
| 3 | -import org.springframework.beans.factory.annotation.Autowired; | |
| 4 | -import org.springframework.context.annotation.Bean; | |
| 5 | -import org.springframework.stereotype.Component; | |
| 6 | - | |
| 7 | -import com.genersoft.iot.vmp.conf.VManagerConfig; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * @Description:视频设备数据存储工厂,根据存储策略,返回对应的存储器 | |
| 11 | - * @author: swwheihei | |
| 12 | - * @date: 2020年5月6日 下午2:15:16 | |
| 13 | - */ | |
| 14 | -@Component | |
| 15 | -public class VideoManagerStoragerFactory { | |
| 16 | - | |
| 17 | - @Autowired | |
| 18 | - private VManagerConfig vmConfig; | |
| 19 | - | |
| 20 | - @Autowired | |
| 21 | - private IVideoManagerStorager jdbcStorager; | |
| 22 | - | |
| 23 | - @Autowired | |
| 24 | - private IVideoManagerStorager redisStorager; | |
| 25 | - | |
| 26 | - @Bean("storager") | |
| 27 | - public IVideoManagerStorager getStorager() { | |
| 28 | - if ("redis".equals(vmConfig.getDatabase().toLowerCase())) { | |
| 29 | - return redisStorager; | |
| 30 | - } else if ("jdbc".equals(vmConfig.getDatabase().toLowerCase())) { | |
| 31 | - return jdbcStorager; | |
| 32 | - } | |
| 33 | - return redisStorager; | |
| 34 | - } | |
| 35 | - | |
| 36 | -} |
src/main/java/com/genersoft/iot/vmp/storager/VodeoMannagerTask.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.storager; | |
| 2 | - | |
| 3 | -import org.springframework.beans.factory.annotation.Autowired; | |
| 4 | -import org.springframework.boot.CommandLineRunner; | |
| 5 | -import org.springframework.stereotype.Component; | |
| 6 | - | |
| 7 | -@Component | |
| 8 | -public class VodeoMannagerTask implements CommandLineRunner { | |
| 9 | - | |
| 10 | - @Autowired | |
| 11 | - private IVideoManagerStorager storager; | |
| 12 | - | |
| 13 | - @Override | |
| 14 | - public void run(String... strings) throws Exception { | |
| 15 | - storager.updateCatch(); | |
| 16 | - } | |
| 17 | -} |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.storager.dao; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 4 | +import org.apache.ibatis.annotations.*; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 用于存储设备通道信息 | |
| 10 | + */ | |
| 11 | +@Mapper | |
| 12 | +public interface DeviceChannelMapper { | |
| 13 | + | |
| 14 | + @Insert("INSERT INTO device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + | |
| 15 | + "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + | |
| 16 | + "ipAddress, port, password, PTZType, status) " + | |
| 17 | + "VALUES ('${channelId}', '${deviceId}', '${name}', '${manufacture}', '${model}', '${owner}', '${civilCode}', '${block}'," + | |
| 18 | + "'${address}', ${parental}, '${parentId}', ${safetyWay}, ${registerWay}, '${certNum}', ${certifiable}, ${errCode}, '${secrecy}', " + | |
| 19 | + "'${ipAddress}', ${port}, '${password}', ${PTZType}, ${status})") | |
| 20 | + int add(DeviceChannel channel); | |
| 21 | + | |
| 22 | + @Update("UPDATE device_channel " + | |
| 23 | + "SET name=#{name}, manufacture=#{manufacture}, model=#{model}, owner=#{owner}, civilCode=#{civilCode}, " + | |
| 24 | + "block=#{block}, address=#{address}, parental=#{parental}, parentId=#{parentId}, safetyWay=#{safetyWay}, " + | |
| 25 | + "registerWay=#{registerWay}, certNum=#{certNum}, certifiable=#{certifiable}, errCode=#{errCode}, secrecy=#{secrecy}, " + | |
| 26 | + "ipAddress=#{ipAddress}, port=#{port}, password=#{password}, PTZType=#{PTZType}, status=#{status}, streamId=#{streamId}, " + | |
| 27 | + "hasAudio=#{hasAudio}" + | |
| 28 | + "WHERE deviceId=#{deviceId} AND channelId=#{channelId}") | |
| 29 | + int update(DeviceChannel channel); | |
| 30 | + | |
| 31 | + @Select(value = {" <script>" + | |
| 32 | + "SELECT * FROM ( "+ | |
| 33 | + " SELECT * , (SELECT count(0) FROM device_channel WHERE parentId=dc.channelId) as subCount FROM device_channel dc " + | |
| 34 | + " WHERE dc.deviceId=#{deviceId} " + | |
| 35 | + " <if test=\"query != null\"> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " + | |
| 36 | + " <if test=\"parentChannelId != null\"> AND dc.parentId=#{parentChannelId} </if> " + | |
| 37 | + " <if test=\"online == true\" > AND dc.status=1</if>" + | |
| 38 | + " <if test=\"online == false\" > AND dc.status=0</if>) dcr" + | |
| 39 | + " WHERE 1=1 " + | |
| 40 | + " <if test=\"hasSubChannel == true\" > AND subCount >0</if>" + | |
| 41 | + " <if test=\"hasSubChannel == false\" > AND subCount=0</if>" + | |
| 42 | + " </script>"}) | |
| 43 | + List<DeviceChannel> queryChannelsByDeviceId(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); | |
| 44 | + | |
| 45 | + @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") | |
| 46 | + DeviceChannel queryChannel(String deviceId, String channelId); | |
| 47 | + | |
| 48 | + @Delete("DELETE FROM device_channel WHERE deviceId=#{deviceId}") | |
| 49 | + int cleanChannelsByDeviceId(String deviceId); | |
| 50 | + | |
| 51 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.storager.dao; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 4 | +import org.apache.ibatis.annotations.*; | |
| 5 | +import org.springframework.stereotype.Repository; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 用于存储设备信息 | |
| 11 | + */ | |
| 12 | +@Mapper | |
| 13 | +@Repository | |
| 14 | +public interface DeviceMapper { | |
| 15 | + | |
| 16 | + @Select("SELECT * FROM device WHERE deviceId = #{deviceId}") | |
| 17 | + Device getDeviceByDeviceId(String deviceId); | |
| 18 | + | |
| 19 | + @Insert("INSERT INTO device (" + | |
| 20 | + "deviceId, " + | |
| 21 | + "name, " + | |
| 22 | + "manufacturer, " + | |
| 23 | + "model, " + | |
| 24 | + "firmware, " + | |
| 25 | + "transport," + | |
| 26 | + "streamMode," + | |
| 27 | + "ip," + | |
| 28 | + "port," + | |
| 29 | + "hostAddress," + | |
| 30 | + "online" + | |
| 31 | + ") VALUES (" + | |
| 32 | + "#{deviceId}," + | |
| 33 | + "#{name}," + | |
| 34 | + "#{manufacturer}," + | |
| 35 | + "#{model}," + | |
| 36 | + "#{firmware}," + | |
| 37 | + "#{transport}," + | |
| 38 | + "#{streamMode}," + | |
| 39 | + "#{ip}," + | |
| 40 | + "#{port}," + | |
| 41 | + "#{hostAddress}," + | |
| 42 | + "#{online}" + | |
| 43 | + ")") | |
| 44 | + int add(Device device); | |
| 45 | + | |
| 46 | + | |
| 47 | + @Update("UPDATE device " + | |
| 48 | + "SET name=#{name}, " + | |
| 49 | + "manufacturer=#{manufacturer}," + | |
| 50 | + "model=#{model}," + | |
| 51 | + "firmware=#{firmware}, " + | |
| 52 | + "transport=#{transport}," + | |
| 53 | + "streamMode=#{streamMode}, " + | |
| 54 | + "ip=#{ip}, " + | |
| 55 | + "port=#{port}, " + | |
| 56 | + "hostAddress=#{hostAddress}, " + | |
| 57 | + "online=#{online} " + | |
| 58 | + "WHERE deviceId=#{deviceId}") | |
| 59 | + int update(Device device); | |
| 60 | + | |
| 61 | + @Select("SELECT *, (SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount FROM device de") | |
| 62 | + List<Device> getDevices(); | |
| 63 | + | |
| 64 | + @Delete("DELETE FROM device WHERE deviceId=#{deviceId}") | |
| 65 | + int del(String deviceId); | |
| 66 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.storager.impl; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.common.StreamInfo; | |
| 4 | +import com.genersoft.iot.vmp.common.VideoManagerConstants; | |
| 5 | +import com.genersoft.iot.vmp.conf.MediaServerConfig; | |
| 6 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 7 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 8 | +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; | |
| 9 | +import com.genersoft.iot.vmp.utils.redis.RedisUtil; | |
| 10 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 11 | +import org.springframework.stereotype.Component; | |
| 12 | + | |
| 13 | +import java.util.HashMap; | |
| 14 | +import java.util.HashSet; | |
| 15 | +import java.util.List; | |
| 16 | +import java.util.Map; | |
| 17 | + | |
| 18 | +@Component | |
| 19 | +public class RedisCatchStorageImpl implements IRedisCatchStorage { | |
| 20 | + | |
| 21 | + @Autowired | |
| 22 | + private RedisUtil redis; | |
| 23 | + | |
| 24 | + @Autowired | |
| 25 | + private DeviceChannelMapper deviceChannelMapper; | |
| 26 | + | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 开始播放时将流存入redis | |
| 30 | + * | |
| 31 | + * @return | |
| 32 | + */ | |
| 33 | + @Override | |
| 34 | + public boolean startPlay(StreamInfo stream) { | |
| 35 | + return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getCahnnelId()), | |
| 36 | + stream); | |
| 37 | + } | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * 停止播放时从redis删除 | |
| 41 | + * | |
| 42 | + * @return | |
| 43 | + */ | |
| 44 | + @Override | |
| 45 | + public boolean stopPlay(StreamInfo streamInfo) { | |
| 46 | + if (streamInfo == null) return false; | |
| 47 | + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId()); | |
| 48 | + if (deviceChannel != null) { | |
| 49 | + deviceChannel.setStreamId(null); | |
| 50 | + deviceChannel.setDeviceId(streamInfo.getDeviceID()); | |
| 51 | + deviceChannelMapper.update(deviceChannel); | |
| 52 | + } | |
| 53 | + return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 54 | + streamInfo.getStreamId(), | |
| 55 | + streamInfo.getDeviceID(), | |
| 56 | + streamInfo.getCahnnelId())); | |
| 57 | + } | |
| 58 | + | |
| 59 | + /** | |
| 60 | + * 查询播放列表 | |
| 61 | + * @return | |
| 62 | + */ | |
| 63 | + @Override | |
| 64 | + public StreamInfo queryPlay(StreamInfo streamInfo) { | |
| 65 | + return (StreamInfo)redis.get(String.format("%S_%s_%s_%s", | |
| 66 | + VideoManagerConstants.PLAYER_PREFIX, | |
| 67 | + streamInfo.getStreamId(), | |
| 68 | + streamInfo.getDeviceID(), | |
| 69 | + streamInfo.getCahnnelId())); | |
| 70 | + } | |
| 71 | + @Override | |
| 72 | + public StreamInfo queryPlayByStreamId(String steamId) { | |
| 73 | + List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, steamId)); | |
| 74 | + if (playLeys == null || playLeys.size() == 0) return null; | |
| 75 | + return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Override | |
| 79 | + public StreamInfo queryPlaybackByStreamId(String steamId) { | |
| 80 | + List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, steamId)); | |
| 81 | + if (playLeys == null || playLeys.size() == 0) return null; | |
| 82 | + return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @Override | |
| 86 | + public StreamInfo queryPlayByDevice(String deviceId, String code) { | |
| 87 | +// List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 88 | + List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 89 | + deviceId, | |
| 90 | + code)); | |
| 91 | + if (playLeys == null || playLeys.size() == 0) return null; | |
| 92 | + return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 93 | + } | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * 更新流媒体信息 | |
| 97 | + * @param mediaServerConfig | |
| 98 | + * @return | |
| 99 | + */ | |
| 100 | + @Override | |
| 101 | + public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { | |
| 102 | + return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 获取流媒体信息 | |
| 107 | + * @return | |
| 108 | + */ | |
| 109 | + @Override | |
| 110 | + public MediaServerConfig getMediaInfo() { | |
| 111 | + return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); | |
| 112 | + } | |
| 113 | + | |
| 114 | + @Override | |
| 115 | + public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) { | |
| 116 | + Map<String, StreamInfo> streamInfos = new HashMap<>(); | |
| 117 | +// List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); | |
| 118 | + List<Object> players = redis.scan(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); | |
| 119 | + if (players.size() == 0) return streamInfos; | |
| 120 | + for (int i = 0; i < players.size(); i++) { | |
| 121 | + String key = (String) players.get(i); | |
| 122 | + StreamInfo streamInfo = (StreamInfo)redis.get(key); | |
| 123 | + streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getCahnnelId(), streamInfo); | |
| 124 | + } | |
| 125 | + return streamInfos; | |
| 126 | + } | |
| 127 | + | |
| 128 | + | |
| 129 | + @Override | |
| 130 | + public boolean startPlayback(StreamInfo stream) { | |
| 131 | + return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getCahnnelId()), | |
| 132 | + stream); | |
| 133 | + } | |
| 134 | + | |
| 135 | + | |
| 136 | + @Override | |
| 137 | + public boolean stopPlayback(StreamInfo streamInfo) { | |
| 138 | + if (streamInfo == null) return false; | |
| 139 | + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId()); | |
| 140 | + if (deviceChannel != null) { | |
| 141 | + deviceChannel.setStreamId(null); | |
| 142 | + deviceChannel.setDeviceId(streamInfo.getDeviceID()); | |
| 143 | + deviceChannelMapper.update(deviceChannel); | |
| 144 | + } | |
| 145 | + return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 146 | + streamInfo.getStreamId(), | |
| 147 | + streamInfo.getDeviceID(), | |
| 148 | + streamInfo.getCahnnelId())); | |
| 149 | + } | |
| 150 | + | |
| 151 | + @Override | |
| 152 | + public StreamInfo queryPlaybackByDevice(String deviceId, String code) { | |
| 153 | + String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 154 | + deviceId, | |
| 155 | + code); | |
| 156 | + List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 157 | + deviceId, | |
| 158 | + code)); | |
| 159 | + if (playLeys == null || playLeys.size() == 0) { | |
| 160 | + playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 161 | + deviceId)); | |
| 162 | + } | |
| 163 | + if (playLeys == null || playLeys.size() == 0) return null; | |
| 164 | + return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 165 | + } | |
| 166 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.storager.impl; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | + | |
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 6 | +import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; | |
| 7 | +import com.genersoft.iot.vmp.storager.dao.DeviceMapper; | |
| 8 | +import com.github.pagehelper.PageHelper; | |
| 9 | +import com.github.pagehelper.PageInfo; | |
| 10 | +import io.swagger.models.auth.In; | |
| 11 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 12 | +import org.springframework.stereotype.Component; | |
| 13 | + | |
| 14 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 15 | +import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | |
| 16 | +import org.springframework.util.StringUtils; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * @Description:视频设备数据存储-jdbc实现 | |
| 20 | + * @author: swwheihei | |
| 21 | + * @date: 2020年5月6日 下午2:31:42 | |
| 22 | + */ | |
| 23 | +@Component | |
| 24 | +public class VideoManagerStoragerImpl implements IVideoManagerStorager { | |
| 25 | + | |
| 26 | + @Autowired | |
| 27 | + private DeviceMapper deviceMapper; | |
| 28 | + | |
| 29 | + @Autowired | |
| 30 | + private DeviceChannelMapper deviceChannelMapper; | |
| 31 | + | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 根据设备ID判断设备是否存在 | |
| 35 | + * | |
| 36 | + * @param deviceId 设备ID | |
| 37 | + * @return true:存在 false:不存在 | |
| 38 | + */ | |
| 39 | + @Override | |
| 40 | + public boolean exists(String deviceId) { | |
| 41 | + return deviceMapper.getDeviceByDeviceId(deviceId) != null; | |
| 42 | + } | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 视频设备创建 | |
| 46 | + * | |
| 47 | + * @param device 设备对象 | |
| 48 | + * @return true:创建成功 false:创建失败 | |
| 49 | + */ | |
| 50 | + @Override | |
| 51 | + public synchronized boolean create(Device device) { | |
| 52 | + return deviceMapper.add(device) > 0; | |
| 53 | + } | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 视频设备更新 | |
| 59 | + * | |
| 60 | + * @param device 设备对象 | |
| 61 | + * @return true:更新成功 false:更新失败 | |
| 62 | + */ | |
| 63 | + @Override | |
| 64 | + public synchronized boolean updateDevice(Device device) { | |
| 65 | + Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); | |
| 66 | + if (deviceByDeviceId == null) { | |
| 67 | + return deviceMapper.add(device) > 0; | |
| 68 | + }else { | |
| 69 | + return deviceMapper.update(device) > 0; | |
| 70 | + } | |
| 71 | + | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 75 | + public synchronized void updateChannel(String deviceId, DeviceChannel channel) { | |
| 76 | + String channelId = channel.getChannelId(); | |
| 77 | + channel.setDeviceId(deviceId); | |
| 78 | + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); | |
| 79 | + if (deviceChannel == null) { | |
| 80 | + deviceChannelMapper.add(channel); | |
| 81 | + }else { | |
| 82 | + deviceChannelMapper.update(channel); | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + /** | |
| 87 | + * 获取设备 | |
| 88 | + * | |
| 89 | + * @param deviceId 设备ID | |
| 90 | + * @return Device 设备对象 | |
| 91 | + */ | |
| 92 | + @Override | |
| 93 | + public Device queryVideoDevice(String deviceId) { | |
| 94 | + return deviceMapper.getDeviceByDeviceId(deviceId); | |
| 95 | + } | |
| 96 | + | |
| 97 | + @Override | |
| 98 | + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { | |
| 99 | + // 获取到所有正在播放的流 | |
| 100 | + PageHelper.startPage(page, count); | |
| 101 | + List<DeviceChannel> all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, null, query, hasSubChannel, online); | |
| 102 | + return new PageInfo<>(all); | |
| 103 | + } | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + @Override | |
| 108 | + public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { | |
| 109 | + return deviceChannelMapper.queryChannelsByDeviceId(deviceId, null,null, null, null); | |
| 110 | + } | |
| 111 | + | |
| 112 | + @Override | |
| 113 | + public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) { | |
| 114 | + PageHelper.startPage(page, count); | |
| 115 | + List<DeviceChannel> all = deviceChannelMapper.queryChannelsByDeviceId(deviceId, parentChannelId, null, null, null); | |
| 116 | + return new PageInfo<>(all); | |
| 117 | + } | |
| 118 | + | |
| 119 | + @Override | |
| 120 | + public DeviceChannel queryChannel(String deviceId, String channelId) { | |
| 121 | + return deviceChannelMapper.queryChannel(deviceId, channelId); | |
| 122 | + } | |
| 123 | + | |
| 124 | + | |
| 125 | + /** | |
| 126 | + * 获取多个设备 | |
| 127 | + * | |
| 128 | + * @param page 当前页数 | |
| 129 | + * @param count 每页数量 | |
| 130 | + * @return PageInfo<Device> 分页设备对象数组 | |
| 131 | + */ | |
| 132 | + @Override | |
| 133 | + public PageInfo<Device> queryVideoDeviceList(int page, int count) { | |
| 134 | + PageHelper.startPage(page, count); | |
| 135 | + List<Device> all = deviceMapper.getDevices(); | |
| 136 | + return new PageInfo<>(all); | |
| 137 | + } | |
| 138 | + | |
| 139 | + /** | |
| 140 | + * 获取多个设备 | |
| 141 | + * | |
| 142 | + * @return List<Device> 设备对象数组 | |
| 143 | + */ | |
| 144 | + @Override | |
| 145 | + public List<Device> queryVideoDeviceList() { | |
| 146 | + | |
| 147 | + List<Device> deviceList = deviceMapper.getDevices(); | |
| 148 | + return deviceList; | |
| 149 | + } | |
| 150 | + | |
| 151 | + /** | |
| 152 | + * 删除设备 | |
| 153 | + * | |
| 154 | + * @param deviceId 设备ID | |
| 155 | + * @return true:删除成功 false:删除失败 | |
| 156 | + */ | |
| 157 | + @Override | |
| 158 | + public boolean delete(String deviceId) { | |
| 159 | + int result = deviceMapper.del(deviceId); | |
| 160 | + | |
| 161 | + return result > 0; | |
| 162 | + } | |
| 163 | + | |
| 164 | + /** | |
| 165 | + * 更新设备在线 | |
| 166 | + * | |
| 167 | + * @param deviceId 设备ID | |
| 168 | + * @return true:更新成功 false:更新失败 | |
| 169 | + */ | |
| 170 | + @Override | |
| 171 | + public synchronized boolean online(String deviceId) { | |
| 172 | + Device device = deviceMapper.getDeviceByDeviceId(deviceId); | |
| 173 | + device.setOnline(1); | |
| 174 | + System.out.println("更新设备在线"); | |
| 175 | + if (device == null) { | |
| 176 | + return false; | |
| 177 | + } | |
| 178 | + return deviceMapper.update(device) > 0; | |
| 179 | + } | |
| 180 | + | |
| 181 | + /** | |
| 182 | + * 更新设备离线 | |
| 183 | + * | |
| 184 | + * @param deviceId 设备ID | |
| 185 | + * @return true:更新成功 false:更新失败 | |
| 186 | + */ | |
| 187 | + @Override | |
| 188 | + public synchronized boolean outline(String deviceId) { | |
| 189 | + Device device = deviceMapper.getDeviceByDeviceId(deviceId); | |
| 190 | + device.setOnline(0); | |
| 191 | + System.out.println("更新设备离线"); | |
| 192 | + return deviceMapper.update(device) > 0; | |
| 193 | + } | |
| 194 | + | |
| 195 | + | |
| 196 | + @Override | |
| 197 | + public void cleanChannelsForDevice(String deviceId) { | |
| 198 | + int result = deviceChannelMapper.cleanChannelsByDeviceId(deviceId); | |
| 199 | + } | |
| 200 | + | |
| 201 | + | |
| 202 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.storager.jdbc; | |
| 2 | - | |
| 3 | -import java.util.List; | |
| 4 | -import java.util.Map; | |
| 5 | - | |
| 6 | -import com.genersoft.iot.vmp.common.PageResult; | |
| 7 | -import com.genersoft.iot.vmp.common.StreamInfo; | |
| 8 | -import com.genersoft.iot.vmp.conf.MediaServerConfig; | |
| 9 | -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 10 | -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | |
| 11 | -import org.springframework.stereotype.Component; | |
| 12 | -import org.springframework.stereotype.Service; | |
| 13 | - | |
| 14 | -import com.genersoft.iot.vmp.common.VideoManagerConstants; | |
| 15 | -import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 16 | -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | |
| 17 | - | |
| 18 | -/** | |
| 19 | - * @Description:视频设备数据存储-jdbc实现 | |
| 20 | - * @author: swwheihei | |
| 21 | - * @date: 2020年5月6日 下午2:28:12 | |
| 22 | - */ | |
| 23 | -@Component("jdbcStorager") | |
| 24 | -public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager { | |
| 25 | - | |
| 26 | - @Override | |
| 27 | - public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { | |
| 28 | - return false; | |
| 29 | - } | |
| 30 | - | |
| 31 | - @Override | |
| 32 | - public MediaServerConfig getMediaInfo() { | |
| 33 | - return null; | |
| 34 | - } | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * 根据设备ID判断设备是否存在 | |
| 38 | - * | |
| 39 | - * @param deviceId 设备ID | |
| 40 | - * @return true:存在 false:不存在 | |
| 41 | - */ | |
| 42 | - @Override | |
| 43 | - public boolean exists(String deviceId) { | |
| 44 | - // TODO Auto-generated method stub | |
| 45 | - return false; | |
| 46 | - } | |
| 47 | - | |
| 48 | - /** | |
| 49 | - * 视频设备创建 | |
| 50 | - * | |
| 51 | - * @param device 设备对象 | |
| 52 | - * @return true:创建成功 false:创建失败 | |
| 53 | - */ | |
| 54 | - @Override | |
| 55 | - public boolean create(Device device) { | |
| 56 | - // TODO Auto-generated method stub | |
| 57 | - return false; | |
| 58 | - } | |
| 59 | - | |
| 60 | - @Override | |
| 61 | - public boolean updateDevice(Device device) { | |
| 62 | - return false; | |
| 63 | - } | |
| 64 | - | |
| 65 | - @Override | |
| 66 | - public void updateChannel(String deviceId, DeviceChannel channel) { | |
| 67 | - | |
| 68 | - } | |
| 69 | - | |
| 70 | - | |
| 71 | - /** | |
| 72 | - * 获取设备 | |
| 73 | - * | |
| 74 | - * @param deviceId 设备ID | |
| 75 | - * @return Device 设备对象 | |
| 76 | - */ | |
| 77 | - @Override | |
| 78 | - public Device queryVideoDevice(String deviceId) { | |
| 79 | - // TODO Auto-generated method stub | |
| 80 | - return null; | |
| 81 | - } | |
| 82 | - | |
| 83 | - @Override | |
| 84 | - public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) { | |
| 85 | - return null; | |
| 86 | - } | |
| 87 | - | |
| 88 | - | |
| 89 | - @Override | |
| 90 | - public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { | |
| 91 | - return null; | |
| 92 | - } | |
| 93 | - | |
| 94 | - @Override | |
| 95 | - public DeviceChannel queryChannel(String deviceId, String channelId) { | |
| 96 | - return null; | |
| 97 | - } | |
| 98 | - | |
| 99 | - @Override | |
| 100 | - public PageResult<Device> queryVideoDeviceList(String[] deviceIds, int page, int count) { | |
| 101 | - return null; | |
| 102 | - } | |
| 103 | - | |
| 104 | - /** | |
| 105 | - * 获取多个设备 | |
| 106 | - * | |
| 107 | - * @param deviceIds 设备ID数组 | |
| 108 | - * @return List<Device> 设备对象数组 | |
| 109 | - */ | |
| 110 | - @Override | |
| 111 | - public List<Device> queryVideoDeviceList(String[] deviceIds) { | |
| 112 | - // TODO Auto-generated method stub | |
| 113 | - return null; | |
| 114 | - } | |
| 115 | - | |
| 116 | - /** | |
| 117 | - * 删除设备 | |
| 118 | - * | |
| 119 | - * @param deviceId 设备ID | |
| 120 | - * @return true:删除成功 false:删除失败 | |
| 121 | - */ | |
| 122 | - @Override | |
| 123 | - public boolean delete(String deviceId) { | |
| 124 | - // TODO Auto-generated method stub | |
| 125 | - return false; | |
| 126 | - } | |
| 127 | - | |
| 128 | - /** | |
| 129 | - * 更新设备在线 | |
| 130 | - * | |
| 131 | - * @param deviceId 设备ID | |
| 132 | - * @return true:更新成功 false:更新失败 | |
| 133 | - */ | |
| 134 | - @Override | |
| 135 | - public boolean online(String deviceId) { | |
| 136 | - // TODO Auto-generated method stub | |
| 137 | - return false; | |
| 138 | - } | |
| 139 | - | |
| 140 | - /** | |
| 141 | - * 更新设备离线 | |
| 142 | - * | |
| 143 | - * @param deviceId 设备ID | |
| 144 | - * @return true:更新成功 false:更新失败 | |
| 145 | - */ | |
| 146 | - @Override | |
| 147 | - public boolean outline(String deviceId) { | |
| 148 | - // TODO Auto-generated method stub | |
| 149 | - return false; | |
| 150 | - } | |
| 151 | - | |
| 152 | - @Override | |
| 153 | - public boolean stopPlay(StreamInfo streamInfo) { | |
| 154 | - return false; | |
| 155 | - } | |
| 156 | - | |
| 157 | - @Override | |
| 158 | - public StreamInfo queryPlay(StreamInfo streamInfo) { | |
| 159 | - return null; | |
| 160 | - } | |
| 161 | - | |
| 162 | - @Override | |
| 163 | - public PageResult querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String online, int page, int count) { | |
| 164 | - return null; | |
| 165 | - } | |
| 166 | - | |
| 167 | - @Override | |
| 168 | - public void updateCatch() { | |
| 169 | - System.out.println("##################"); | |
| 170 | - } | |
| 171 | - | |
| 172 | - @Override | |
| 173 | - public void cleanChannelsForDevice(String deviceId) { | |
| 174 | - | |
| 175 | - } | |
| 176 | - | |
| 177 | - @Override | |
| 178 | - public boolean startPlay(StreamInfo stream) { | |
| 179 | - return false; | |
| 180 | - } | |
| 181 | - | |
| 182 | - @Override | |
| 183 | - public StreamInfo queryPlayBySSRC(String ssrc) { | |
| 184 | - return null; | |
| 185 | - } | |
| 186 | - | |
| 187 | - @Override | |
| 188 | - public StreamInfo queryPlayByDevice(String deviceId, String code) { | |
| 189 | - return null; | |
| 190 | - } | |
| 191 | - | |
| 192 | - @Override | |
| 193 | - public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) { | |
| 194 | - | |
| 195 | - return null; | |
| 196 | - } | |
| 197 | - | |
| 198 | - @Override | |
| 199 | - public boolean startPlayback(StreamInfo streamInfo) { | |
| 200 | - return false; | |
| 201 | - } | |
| 202 | - | |
| 203 | - @Override | |
| 204 | - public boolean stopPlayback(StreamInfo streamInfo) { | |
| 205 | - return false; | |
| 206 | - } | |
| 207 | - | |
| 208 | - @Override | |
| 209 | - public StreamInfo queryPlaybackByDevice(String deviceId, String channelId) { | |
| 210 | - return null; | |
| 211 | - } | |
| 212 | - | |
| 213 | - @Override | |
| 214 | - public StreamInfo queryPlaybackBySSRC(String ssrc) { | |
| 215 | - return null; | |
| 216 | - } | |
| 217 | - | |
| 218 | - @Override | |
| 219 | - public boolean updateParentPlatform(ParentPlatform parentPlatform) { | |
| 220 | - return false; | |
| 221 | - } | |
| 222 | - | |
| 223 | - @Override | |
| 224 | - public boolean deleteParentPlatform(ParentPlatform parentPlatform) { | |
| 225 | - return false; | |
| 226 | - } | |
| 227 | - | |
| 228 | - @Override | |
| 229 | - public PageResult<ParentPlatform> queryParentPlatformList(int page, int count) { | |
| 230 | - return null; | |
| 231 | - } | |
| 232 | - | |
| 233 | - @Override | |
| 234 | - public ParentPlatform queryParentPlatById(String platformGbId) { | |
| 235 | - return null; | |
| 236 | - } | |
| 237 | -} |
src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.storager.redis; | |
| 2 | - | |
| 3 | -import java.util.*; | |
| 4 | - | |
| 5 | -import com.alibaba.fastjson.JSON; | |
| 6 | -import com.alibaba.fastjson.JSONObject; | |
| 7 | -import com.genersoft.iot.vmp.common.PageResult; | |
| 8 | -import com.genersoft.iot.vmp.common.StreamInfo; | |
| 9 | -import com.genersoft.iot.vmp.conf.MediaServerConfig; | |
| 10 | -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 11 | -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; | |
| 12 | -import org.springframework.beans.factory.annotation.Autowired; | |
| 13 | -import org.springframework.stereotype.Component; | |
| 14 | - | |
| 15 | -import com.genersoft.iot.vmp.common.VideoManagerConstants; | |
| 16 | -import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 17 | -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | |
| 18 | -import com.genersoft.iot.vmp.utils.redis.RedisUtil; | |
| 19 | -import org.springframework.util.StringUtils; | |
| 20 | - | |
| 21 | -/** | |
| 22 | - * @Description:视频设备数据存储-redis实现 | |
| 23 | - * @author: swwheihei | |
| 24 | - * @date: 2020年5月6日 下午2:31:42 | |
| 25 | - */ | |
| 26 | -@Component("redisStorager") | |
| 27 | -public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager { | |
| 28 | - | |
| 29 | - @Autowired | |
| 30 | - private RedisUtil redis; | |
| 31 | - | |
| 32 | - private HashMap<String, HashMap<String, HashSet<String>>> deviceMap = new HashMap<>(); | |
| 33 | - | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 根据设备ID判断设备是否存在 | |
| 37 | - * | |
| 38 | - * @param deviceId 设备ID | |
| 39 | - * @return true:存在 false:不存在 | |
| 40 | - */ | |
| 41 | - @Override | |
| 42 | - public boolean exists(String deviceId) { | |
| 43 | - return redis.hasKey(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 44 | - } | |
| 45 | - | |
| 46 | - /** | |
| 47 | - * 视频设备创建 | |
| 48 | - * | |
| 49 | - * @param device 设备对象 | |
| 50 | - * @return true:创建成功 false:创建失败 | |
| 51 | - */ | |
| 52 | - @Override | |
| 53 | - public boolean create(Device device) { | |
| 54 | - return redis.set(VideoManagerConstants.DEVICE_PREFIX+device.getDeviceId(), device); | |
| 55 | - } | |
| 56 | - | |
| 57 | - | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * 视频设备更新 | |
| 61 | - * | |
| 62 | - * @param device 设备对象 | |
| 63 | - * @return true:更新成功 false:更新失败 | |
| 64 | - */ | |
| 65 | - @Override | |
| 66 | - public boolean updateDevice(Device device) { | |
| 67 | - if (deviceMap.get(device.getDeviceId()) == null) { | |
| 68 | - deviceMap.put(device.getDeviceId(), new HashMap<String, HashSet<String>>()); | |
| 69 | - } | |
| 70 | - // 更新device中的通道数量 | |
| 71 | - device.setChannelCount(deviceMap.get(device.getDeviceId()).size()); | |
| 72 | - // 存储device | |
| 73 | - return redis.set(VideoManagerConstants.DEVICE_PREFIX+device.getDeviceId(), device); | |
| 74 | - | |
| 75 | - | |
| 76 | - } | |
| 77 | - | |
| 78 | - @Override | |
| 79 | - public void updateChannel(String deviceId, DeviceChannel channel) { | |
| 80 | - String channelId = channel.getChannelId(); | |
| 81 | - HashMap<String, HashSet<String>> channelMap = deviceMap.get(deviceId); | |
| 82 | - if (channelMap == null) return; | |
| 83 | - // 作为父设备, 确定自己的子节点数 | |
| 84 | - if (channelMap.get(channelId) == null) { | |
| 85 | - channelMap.put(channelId, new HashSet<String>()); | |
| 86 | - }else if (channelMap.get(channelId).size() > 0) { | |
| 87 | - channel.setSubCount(channelMap.get(channelId).size()); | |
| 88 | - } | |
| 89 | - | |
| 90 | - // 存储通道 | |
| 91 | - redis.set(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 92 | - "_" + channel.getChannelId() + | |
| 93 | - "_" + (channel.getStatus() == 1 ? "on":"off") + | |
| 94 | - "_" + (channelMap.get(channelId).size() > 0)+ | |
| 95 | - "_" + (StringUtils.isEmpty(channel.getParentId())?null:channel.getParentId()), | |
| 96 | - channel); | |
| 97 | - // 更新device中的通道数量 | |
| 98 | - Device device = (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 99 | - device.setChannelCount(deviceMap.get(deviceId).size()); | |
| 100 | - redis.set(VideoManagerConstants.DEVICE_PREFIX+device.getDeviceId(), device); | |
| 101 | - | |
| 102 | - | |
| 103 | - // 如果有父设备,更新父设备内子节点数 | |
| 104 | - String parentId = channel.getParentId(); | |
| 105 | - if (!StringUtils.isEmpty(parentId) && !parentId.equals(deviceId)) { | |
| 106 | - | |
| 107 | - if (channelMap.get(parentId) == null) { | |
| 108 | - channelMap.put(parentId, new HashSet<String>()); | |
| 109 | - } | |
| 110 | - channelMap.get(parentId).add(channelId); | |
| 111 | - | |
| 112 | - DeviceChannel deviceChannel = queryChannel(deviceId, parentId); | |
| 113 | - if (deviceChannel != null) { | |
| 114 | - deviceChannel.setSubCount(channelMap.get(parentId).size()); | |
| 115 | - redis.set(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 116 | - "_" + deviceChannel.getChannelId() + | |
| 117 | - "_" + (deviceChannel.getStatus() == 1 ? "on":"off") + | |
| 118 | - "_" + (channelMap.get(deviceChannel.getChannelId()).size() > 0)+ | |
| 119 | - "_" + (StringUtils.isEmpty(deviceChannel.getParentId())?null:deviceChannel.getParentId()), | |
| 120 | - deviceChannel); | |
| 121 | - | |
| 122 | - } | |
| 123 | - } | |
| 124 | - | |
| 125 | - } | |
| 126 | - | |
| 127 | - /** | |
| 128 | - * 获取设备 | |
| 129 | - * | |
| 130 | - * @param deviceId 设备ID | |
| 131 | - * @return Device 设备对象 | |
| 132 | - */ | |
| 133 | - @Override | |
| 134 | - public Device queryVideoDevice(String deviceId) { | |
| 135 | - return (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 136 | - } | |
| 137 | - | |
| 138 | - @Override | |
| 139 | - public PageResult queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, String online, int page, int count) { | |
| 140 | - // 获取到所有正在播放的流 | |
| 141 | - Map<String, StreamInfo> stringStreamInfoMap = queryPlayByDeviceId(deviceId); | |
| 142 | - List<DeviceChannel> result = new ArrayList<>(); | |
| 143 | - PageResult pageResult = new PageResult<DeviceChannel>(); | |
| 144 | - String queryContent = "*"; | |
| 145 | - if (!StringUtils.isEmpty(query)) queryContent = String.format("*%S*",query); | |
| 146 | - String queryHasSubChannel = "*"; | |
| 147 | - if (hasSubChannel != null) queryHasSubChannel = hasSubChannel?"true":"false"; | |
| 148 | - String queryOnline = "*"; | |
| 149 | - if (!StringUtils.isEmpty(online)) queryOnline = online; | |
| 150 | - String queryStr = VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 151 | - "_" + queryContent + // 搜索编号和名称 | |
| 152 | - "_" + queryOnline + // 搜索是否在线 | |
| 153 | - "_" + queryHasSubChannel + // 搜索是否含有子节点 | |
| 154 | - "_" + "*"; | |
| 155 | -// List<Object> deviceChannelList = redis.keys(queryStr); | |
| 156 | - List<Object> deviceChannelList = redis.scan(queryStr); | |
| 157 | - //对查询结果排序,避免出现通道排列顺序乱序的情况 | |
| 158 | - Collections.sort(deviceChannelList,new Comparator<Object>(){ | |
| 159 | - @Override | |
| 160 | - public int compare(Object o1, Object o2) { | |
| 161 | - return o1.toString().compareToIgnoreCase(o2.toString()); | |
| 162 | - } | |
| 163 | - }); | |
| 164 | - pageResult.setPage(page); | |
| 165 | - pageResult.setCount(count); | |
| 166 | - pageResult.setTotal(deviceChannelList.size()); | |
| 167 | - int maxCount = (page + 1 ) * count; | |
| 168 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 169 | - for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) { | |
| 170 | - DeviceChannel deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(i)); | |
| 171 | - StreamInfo streamInfo = stringStreamInfoMap.get(deviceId + "_" + deviceChannel.getChannelId()); | |
| 172 | - deviceChannel.setPlay(streamInfo != null); | |
| 173 | - if (streamInfo != null) deviceChannel.setSsrc(streamInfo.getSsrc()); | |
| 174 | - result.add(deviceChannel); | |
| 175 | - } | |
| 176 | - pageResult.setData(result); | |
| 177 | - } | |
| 178 | - | |
| 179 | - return pageResult; | |
| 180 | - } | |
| 181 | - | |
| 182 | - | |
| 183 | - | |
| 184 | - @Override | |
| 185 | - public List<DeviceChannel> queryChannelsByDeviceId(String deviceId) { | |
| 186 | - List<DeviceChannel> result = new ArrayList<>(); | |
| 187 | -// List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 188 | - List<Object> deviceChannelList = redis.scan(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 189 | - | |
| 190 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 191 | - for (int i = 0; i < deviceChannelList.size(); i++) { | |
| 192 | - result.add((DeviceChannel)redis.get((String) deviceChannelList.get(i))); | |
| 193 | - } | |
| 194 | - } | |
| 195 | - return result; | |
| 196 | - } | |
| 197 | - | |
| 198 | - @Override | |
| 199 | - public PageResult querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) { | |
| 200 | - List<DeviceChannel> allDeviceChannels = new ArrayList<>(); | |
| 201 | - String queryContent = "*"; | |
| 202 | - if (!StringUtils.isEmpty(query)) queryContent = String.format("*%S*",query); | |
| 203 | - String queryHasSubChannel = "*"; | |
| 204 | - if (hasSubChannel != null) queryHasSubChannel = hasSubChannel?"true":"false"; | |
| 205 | - String queryOnline = "*"; | |
| 206 | - if (!StringUtils.isEmpty(online)) queryOnline = online; | |
| 207 | - String queryStr = VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 208 | - "_" + queryContent + // 搜索编号和名称 | |
| 209 | - "_" + queryOnline + // 搜索是否在线 | |
| 210 | - "_" + queryHasSubChannel + // 搜索是否含有子节点 | |
| 211 | - "_" + parentChannelId; | |
| 212 | - | |
| 213 | -// List<Object> deviceChannelList = redis.keys(queryStr); | |
| 214 | - List<Object> deviceChannelList = redis.scan(queryStr); | |
| 215 | - | |
| 216 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 217 | - for (int i = 0; i < deviceChannelList.size(); i++) { | |
| 218 | - DeviceChannel deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(i)); | |
| 219 | - if (deviceChannel.getParentId() != null && deviceChannel.getParentId().equals(parentChannelId)) { | |
| 220 | - allDeviceChannels.add(deviceChannel); | |
| 221 | - } | |
| 222 | - } | |
| 223 | - } | |
| 224 | - int maxCount = (page + 1 ) * count; | |
| 225 | - PageResult pageResult = new PageResult<DeviceChannel>(); | |
| 226 | - pageResult.setPage(page); | |
| 227 | - pageResult.setCount(count); | |
| 228 | - pageResult.setTotal(allDeviceChannels.size()); | |
| 229 | - | |
| 230 | - if (allDeviceChannels.size() > 0) { | |
| 231 | - pageResult.setData(allDeviceChannels.subList( | |
| 232 | - page * count, pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() | |
| 233 | - )); | |
| 234 | - } | |
| 235 | - return pageResult; | |
| 236 | - } | |
| 237 | - | |
| 238 | - public List<DeviceChannel> querySubChannels(String deviceId, String parentChannelId) { | |
| 239 | - List<DeviceChannel> allDeviceChannels = new ArrayList<>(); | |
| 240 | -// List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 241 | - List<Object> deviceChannelList = redis.scan(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 242 | - | |
| 243 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 244 | - for (int i = 0; i < deviceChannelList.size(); i++) { | |
| 245 | - DeviceChannel deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(i)); | |
| 246 | - if (deviceChannel.getParentId() != null && deviceChannel.getParentId().equals(parentChannelId)) { | |
| 247 | - allDeviceChannels.add(deviceChannel); | |
| 248 | - } | |
| 249 | - } | |
| 250 | - } | |
| 251 | - | |
| 252 | - return allDeviceChannels; | |
| 253 | - } | |
| 254 | - | |
| 255 | - @Override | |
| 256 | - public DeviceChannel queryChannel(String deviceId, String channelId) { | |
| 257 | - DeviceChannel deviceChannel = null; | |
| 258 | -// List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 259 | - List<Object> deviceChannelList = redis.scan(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + | |
| 260 | - "_" + channelId + "*"); | |
| 261 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 262 | - deviceChannel = (DeviceChannel)redis.get((String)deviceChannelList.get(0)); | |
| 263 | - } | |
| 264 | - return deviceChannel; | |
| 265 | - } | |
| 266 | - | |
| 267 | - | |
| 268 | - /** | |
| 269 | - * 获取多个设备 | |
| 270 | - * | |
| 271 | - * @param deviceIds 设备ID数组 | |
| 272 | - * @return List<Device> 设备对象数组 | |
| 273 | - */ | |
| 274 | - @Override | |
| 275 | - public PageResult<Device> queryVideoDeviceList(String[] deviceIds, int page, int count) { | |
| 276 | - List<Device> devices = new ArrayList<>(); | |
| 277 | - PageResult pageResult = new PageResult<Device>(); | |
| 278 | - pageResult.setPage(page); | |
| 279 | - pageResult.setCount(count); | |
| 280 | - Device device = null; | |
| 281 | - | |
| 282 | - if (deviceIds == null || deviceIds.length == 0) { | |
| 283 | - | |
| 284 | -// List<Object> deviceIdList = redis.keys(VideoManagerConstants.DEVICE_PREFIX+"*"); | |
| 285 | - List<Object> deviceIdList = redis.scan(VideoManagerConstants.DEVICE_PREFIX+"*"); | |
| 286 | - pageResult.setTotal(deviceIdList.size()); | |
| 287 | - int maxCount = (page + 1)* count; | |
| 288 | - for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) { | |
| 289 | - // devices.add((Device)redis.get((String)deviceIdList.get(i))); | |
| 290 | - device =(Device)redis.get((String)deviceIdList.get(i)); | |
| 291 | - if (redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX+device.getDeviceId()).size() == 0){ | |
| 292 | - // outline(device.getDeviceId()); | |
| 293 | - } | |
| 294 | - devices.add(device); | |
| 295 | - } | |
| 296 | - } else { | |
| 297 | - for (int i = 0; i < deviceIds.length; i++) { | |
| 298 | - // devices.add((Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceIds[i])); | |
| 299 | - device = (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceIds[i]); | |
| 300 | - if (redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX+device.getDeviceId()).size() == 0){ | |
| 301 | - // outline(device.getDeviceId()); | |
| 302 | - } | |
| 303 | - devices.add(device); | |
| 304 | - } | |
| 305 | - } | |
| 306 | - pageResult.setData(devices); | |
| 307 | - return pageResult; | |
| 308 | - } | |
| 309 | - | |
| 310 | - /** | |
| 311 | - * 获取多个设备 | |
| 312 | - * | |
| 313 | - * @param deviceIds 设备ID数组 | |
| 314 | - * @return List<Device> 设备对象数组 | |
| 315 | - */ | |
| 316 | - @Override | |
| 317 | - public List<Device> queryVideoDeviceList(String[] deviceIds) { | |
| 318 | - List<Device> devices = new ArrayList<>(); | |
| 319 | - Device device = null; | |
| 320 | - | |
| 321 | - if (deviceIds == null || deviceIds.length == 0) { | |
| 322 | -// List<Object> deviceIdList = redis.keys(VideoManagerConstants.DEVICE_PREFIX+"*"); | |
| 323 | - List<Object> deviceIdList = redis.scan(VideoManagerConstants.DEVICE_PREFIX+"*"); | |
| 324 | - for (int i = 0; i < deviceIdList.size(); i++) { | |
| 325 | - device =(Device)redis.get((String)deviceIdList.get(i)); | |
| 326 | - if (redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX+device.getDeviceId()).size() == 0){ | |
| 327 | - outline(device.getDeviceId()); | |
| 328 | - } | |
| 329 | - devices.add(device); | |
| 330 | - } | |
| 331 | - } else { | |
| 332 | - for (int i = 0; i < deviceIds.length; i++) { | |
| 333 | - device = (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceIds[i]); | |
| 334 | - if (redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX+device.getDeviceId()).size() == 0){ | |
| 335 | - outline(device.getDeviceId()); | |
| 336 | - } | |
| 337 | - devices.add(device); | |
| 338 | - } | |
| 339 | - } | |
| 340 | - return devices; | |
| 341 | - } | |
| 342 | - | |
| 343 | - /** | |
| 344 | - * 删除设备 | |
| 345 | - * | |
| 346 | - * @param deviceId 设备ID | |
| 347 | - * @return true:删除成功 false:删除失败 | |
| 348 | - */ | |
| 349 | - @Override | |
| 350 | - public boolean delete(String deviceId) { | |
| 351 | - return redis.del(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 352 | - } | |
| 353 | - | |
| 354 | - /** | |
| 355 | - * 更新设备在线 | |
| 356 | - * | |
| 357 | - * @param deviceId 设备ID | |
| 358 | - * @return true:更新成功 false:更新失败 | |
| 359 | - */ | |
| 360 | - @Override | |
| 361 | - public boolean online(String deviceId) { | |
| 362 | - Device device = (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 363 | - device.setOnline(1); | |
| 364 | - return redis.set(VideoManagerConstants.DEVICE_PREFIX+device.getDeviceId(), device); | |
| 365 | - } | |
| 366 | - | |
| 367 | - /** | |
| 368 | - * 更新设备离线 | |
| 369 | - * | |
| 370 | - * @param deviceId 设备ID | |
| 371 | - * @return true:更新成功 false:更新失败 | |
| 372 | - */ | |
| 373 | - @Override | |
| 374 | - public boolean outline(String deviceId) { | |
| 375 | - Device device = (Device)redis.get(VideoManagerConstants.DEVICE_PREFIX+deviceId); | |
| 376 | - if (device == null) return false; | |
| 377 | - device.setOnline(0); | |
| 378 | - return redis.set(VideoManagerConstants.DEVICE_PREFIX+device.getDeviceId(), device); | |
| 379 | - } | |
| 380 | - | |
| 381 | - /** | |
| 382 | - * 开始播放时将流存入redis | |
| 383 | - * | |
| 384 | - * @return | |
| 385 | - */ | |
| 386 | - @Override | |
| 387 | - public boolean startPlay(StreamInfo stream) { | |
| 388 | - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, stream.getSsrc(),stream.getDeviceID(), stream.getCahnnelId()), | |
| 389 | - stream); | |
| 390 | - } | |
| 391 | - | |
| 392 | - /** | |
| 393 | - * 停止播放时从redis删除 | |
| 394 | - * | |
| 395 | - * @return | |
| 396 | - */ | |
| 397 | - @Override | |
| 398 | - public boolean stopPlay(StreamInfo streamInfo) { | |
| 399 | - if (streamInfo == null) return false; | |
| 400 | - DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId()); | |
| 401 | - if (deviceChannel != null) { | |
| 402 | - deviceChannel.setSsrc(null); | |
| 403 | - deviceChannel.setPlay(false); | |
| 404 | - updateChannel(streamInfo.getDeviceID(), deviceChannel); | |
| 405 | - } | |
| 406 | - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 407 | - streamInfo.getSsrc(), | |
| 408 | - streamInfo.getDeviceID(), | |
| 409 | - streamInfo.getCahnnelId())); | |
| 410 | - } | |
| 411 | - | |
| 412 | - /** | |
| 413 | - * 查询播放列表 | |
| 414 | - * @return | |
| 415 | - */ | |
| 416 | - @Override | |
| 417 | - public StreamInfo queryPlay(StreamInfo streamInfo) { | |
| 418 | - return (StreamInfo)redis.get(String.format("%S_%s_%s_%s", | |
| 419 | - VideoManagerConstants.PLAYER_PREFIX, | |
| 420 | - streamInfo.getSsrc(), | |
| 421 | - streamInfo.getDeviceID(), | |
| 422 | - streamInfo.getCahnnelId())); | |
| 423 | - } | |
| 424 | - @Override | |
| 425 | - public StreamInfo queryPlayBySSRC(String ssrc) { | |
| 426 | -// List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc)); | |
| 427 | - List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc)); | |
| 428 | - if (playLeys == null || playLeys.size() == 0) return null; | |
| 429 | - return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 430 | - } | |
| 431 | - | |
| 432 | - @Override | |
| 433 | - public StreamInfo queryPlaybackBySSRC(String ssrc) { | |
| 434 | -// List<Object> playLeys = redis.keys(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, ssrc)); | |
| 435 | - List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, ssrc)); | |
| 436 | - if (playLeys == null || playLeys.size() == 0) return null; | |
| 437 | - return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 438 | - } | |
| 439 | - | |
| 440 | - @Override | |
| 441 | - public StreamInfo queryPlayByDevice(String deviceId, String code) { | |
| 442 | -// List<Object> playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 443 | - List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, | |
| 444 | - deviceId, | |
| 445 | - code)); | |
| 446 | - if (playLeys == null || playLeys.size() == 0) return null; | |
| 447 | - return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 448 | - } | |
| 449 | - | |
| 450 | - /** | |
| 451 | - * 更新流媒体信息 | |
| 452 | - * @param mediaServerConfig | |
| 453 | - * @return | |
| 454 | - */ | |
| 455 | - @Override | |
| 456 | - public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { | |
| 457 | - return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); | |
| 458 | - } | |
| 459 | - | |
| 460 | - /** | |
| 461 | - * 获取流媒体信息 | |
| 462 | - * @return | |
| 463 | - */ | |
| 464 | - @Override | |
| 465 | - public MediaServerConfig getMediaInfo() { | |
| 466 | - return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); | |
| 467 | - } | |
| 468 | - | |
| 469 | - @Override | |
| 470 | - public void updateCatch() { | |
| 471 | - deviceMap = new HashMap<>(); | |
| 472 | - // 更新设备 | |
| 473 | - List<Device> devices = queryVideoDeviceList(null); | |
| 474 | - if (devices == null && devices.size() == 0) return; | |
| 475 | - for (Device device : devices) { | |
| 476 | - // 更新设备下的通道 | |
| 477 | - HashMap<String, HashSet<String>> channelMap = new HashMap<String, HashSet<String>>(); | |
| 478 | - List<Object> deviceChannelList = redis.scan(VideoManagerConstants.CACHEKEY_PREFIX + | |
| 479 | - device.getDeviceId() + "_" + "*"); | |
| 480 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 481 | - for (int i = 0; i < deviceChannelList.size(); i++) { | |
| 482 | - String key = (String)deviceChannelList.get(i); | |
| 483 | - String[] s = key.split("_"); | |
| 484 | - String channelId = s[3]; | |
| 485 | - HashSet<String> subChannel = channelMap.get(channelId); | |
| 486 | - if (subChannel == null) { | |
| 487 | - subChannel = new HashSet<>(); | |
| 488 | - } | |
| 489 | - System.out.println(key); | |
| 490 | - if (s.length == 6 && !"null".equals(s[5])) { | |
| 491 | - subChannel.add(s[5]); | |
| 492 | - } | |
| 493 | - channelMap.put(channelId, subChannel); | |
| 494 | - } | |
| 495 | - } | |
| 496 | - deviceMap.put(device.getDeviceId(),channelMap); | |
| 497 | - } | |
| 498 | - System.out.println(); | |
| 499 | - } | |
| 500 | - | |
| 501 | - @Override | |
| 502 | - public void cleanChannelsForDevice(String deviceId) { | |
| 503 | - List<DeviceChannel> result = new ArrayList<>(); | |
| 504 | -// List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 505 | - List<Object> deviceChannelList = redis.scan(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); | |
| 506 | - if (deviceChannelList != null && deviceChannelList.size() > 0 ) { | |
| 507 | - for (int i = 0; i < deviceChannelList.size(); i++) { | |
| 508 | - redis.del((String)deviceChannelList.get(i)); | |
| 509 | - } | |
| 510 | - } | |
| 511 | - } | |
| 512 | - | |
| 513 | - @Override | |
| 514 | - public Map<String, StreamInfo> queryPlayByDeviceId(String deviceId) { | |
| 515 | - Map<String, StreamInfo> streamInfos = new HashMap<>(); | |
| 516 | -// List<Object> playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); | |
| 517 | - List<Object> playLeys = redis.scan(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); | |
| 518 | - if (playLeys.size() == 0) return streamInfos; | |
| 519 | - for (int i = 0; i < playLeys.size(); i++) { | |
| 520 | - String key = (String) playLeys.get(i); | |
| 521 | - StreamInfo streamInfo = (StreamInfo)redis.get(key); | |
| 522 | - streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getCahnnelId(), streamInfo); | |
| 523 | - } | |
| 524 | - return streamInfos; | |
| 525 | - } | |
| 526 | - | |
| 527 | - | |
| 528 | - @Override | |
| 529 | - public boolean startPlayback(StreamInfo stream) { | |
| 530 | - return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getSsrc(),stream.getDeviceID(), stream.getCahnnelId()), | |
| 531 | - stream); | |
| 532 | - } | |
| 533 | - | |
| 534 | - | |
| 535 | - @Override | |
| 536 | - public boolean stopPlayback(StreamInfo streamInfo) { | |
| 537 | - if (streamInfo == null) return false; | |
| 538 | - DeviceChannel deviceChannel = queryChannel(streamInfo.getDeviceID(), streamInfo.getCahnnelId()); | |
| 539 | - if (deviceChannel != null) { | |
| 540 | - deviceChannel.setSsrc(null); | |
| 541 | - deviceChannel.setPlay(false); | |
| 542 | - updateChannel(streamInfo.getDeviceID(), deviceChannel); | |
| 543 | - } | |
| 544 | - return redis.del(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 545 | - streamInfo.getSsrc(), | |
| 546 | - streamInfo.getDeviceID(), | |
| 547 | - streamInfo.getCahnnelId())); | |
| 548 | - } | |
| 549 | - | |
| 550 | - @Override | |
| 551 | - public StreamInfo queryPlaybackByDevice(String deviceId, String code) { | |
| 552 | - String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 553 | - deviceId, | |
| 554 | - code); | |
| 555 | - List<Object> playLeys = redis.scan(String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 556 | - deviceId, | |
| 557 | - code)); | |
| 558 | - if (playLeys == null || playLeys.size() == 0) { | |
| 559 | - playLeys = redis.scan(String.format("%S_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, | |
| 560 | - deviceId)); | |
| 561 | - } | |
| 562 | - if (playLeys == null || playLeys.size() == 0) return null; | |
| 563 | - return (StreamInfo)redis.get(playLeys.get(0).toString()); | |
| 564 | - } | |
| 565 | - | |
| 566 | - @Override | |
| 567 | - public boolean updateParentPlatform(ParentPlatform parentPlatform) { | |
| 568 | - | |
| 569 | - // 存储device | |
| 570 | - return redis.set(VideoManagerConstants.PLATFORM_PREFIX + parentPlatform.getDeviceGBId(), parentPlatform); | |
| 571 | - } | |
| 572 | - | |
| 573 | - @Override | |
| 574 | - public boolean deleteParentPlatform(ParentPlatform parentPlatform) { | |
| 575 | - return redis.del(VideoManagerConstants.PLATFORM_PREFIX + parentPlatform.getDeviceGBId()); | |
| 576 | - } | |
| 577 | - | |
| 578 | - @Override | |
| 579 | - public PageResult<ParentPlatform> queryParentPlatformList(int page, int count) { | |
| 580 | - PageResult pageResult = new PageResult<Device>(); | |
| 581 | - pageResult.setPage(page); | |
| 582 | - pageResult.setCount(count); | |
| 583 | - List<ParentPlatform> resultData = new ArrayList<>(); | |
| 584 | - List<Object> parentPlatformList = redis.scan(VideoManagerConstants.PLATFORM_PREFIX + "*"); | |
| 585 | - pageResult.setTotal(parentPlatformList.size()); | |
| 586 | - int maxCount = (page + 1)* count; | |
| 587 | - for (int i = page * count; i < (pageResult.getTotal() > maxCount ? maxCount : pageResult.getTotal() ); i++) { | |
| 588 | - ParentPlatform parentPlatform =(ParentPlatform)redis.get((String)parentPlatformList.get(i)); | |
| 589 | - resultData.add(parentPlatform); | |
| 590 | - | |
| 591 | - } | |
| 592 | - pageResult.setData(resultData); | |
| 593 | - return pageResult; | |
| 594 | - } | |
| 595 | - | |
| 596 | - @Override | |
| 597 | - public ParentPlatform queryParentPlatById(String platformGbId) { | |
| 598 | - return (ParentPlatform)redis.get(VideoManagerConstants.PLATFORM_PREFIX + platformGbId); | |
| 599 | - } | |
| 600 | -} |
src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java
| ... | ... | @@ -34,6 +34,7 @@ public class SpringBeanFactory implements ApplicationContextAware { |
| 34 | 34 | * 获取对象 这里重写了bean方法,起主要作用 |
| 35 | 35 | */ |
| 36 | 36 | public static Object getBean(String beanId) throws BeansException { |
| 37 | + if (applicationContext == null) return null; | |
| 37 | 38 | return applicationContext.getBean(beanId); |
| 38 | 39 | } |
| 39 | 40 | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
| 1 | 1 | package com.genersoft.iot.vmp.vmanager.device; |
| 2 | 2 | |
| 3 | -import java.util.List; | |
| 4 | - | |
| 5 | -import com.genersoft.iot.vmp.common.PageResult; | |
| 6 | 3 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 4 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | |
| 5 | +import com.github.pagehelper.PageInfo; | |
| 7 | 6 | import org.slf4j.Logger; |
| 8 | 7 | import org.slf4j.LoggerFactory; |
| 9 | 8 | import org.springframework.beans.factory.annotation.Autowired; |
| 10 | 9 | import org.springframework.http.HttpStatus; |
| 11 | 10 | import org.springframework.http.ResponseEntity; |
| 11 | +import org.springframework.util.StringUtils; | |
| 12 | 12 | import org.springframework.web.bind.annotation.*; |
| 13 | 13 | import org.springframework.web.context.request.async.DeferredResult; |
| 14 | 14 | |
| ... | ... | @@ -19,6 +19,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 19 | 19 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 20 | 20 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 21 | 21 | |
| 22 | +import javax.sip.message.Response; | |
| 23 | + | |
| 22 | 24 | @CrossOrigin |
| 23 | 25 | @RestController |
| 24 | 26 | @RequestMapping("/api") |
| ... | ... | @@ -50,13 +52,13 @@ public class DeviceController { |
| 50 | 52 | } |
| 51 | 53 | |
| 52 | 54 | @GetMapping("/devices") |
| 53 | - public PageResult<Device> devices(int page, int count){ | |
| 55 | + public PageInfo<Device> devices(int page, int count){ | |
| 54 | 56 | |
| 55 | 57 | if (logger.isDebugEnabled()) { |
| 56 | 58 | logger.debug("查询所有视频设备API调用"); |
| 57 | 59 | } |
| 58 | 60 | |
| 59 | - return storager.queryVideoDeviceList(null, page, count); | |
| 61 | + return storager.queryVideoDeviceList(page, count); | |
| 60 | 62 | } |
| 61 | 63 | |
| 62 | 64 | /** |
| ... | ... | @@ -66,18 +68,33 @@ public class DeviceController { |
| 66 | 68 | * @param count 每页条数 |
| 67 | 69 | * @return 通道列表 |
| 68 | 70 | */ |
| 71 | + /** | |
| 72 | + * 分页查询通道数 | |
| 73 | + * | |
| 74 | + * @param deviceId 设备id | |
| 75 | + * @param page 当前页 | |
| 76 | + * @param count 每页条数 | |
| 77 | + * @param query 查询内容 | |
| 78 | + * @param online 是否在线 在线 true / 离线 false | |
| 79 | + * @param channelType 设备 false/子目录 true | |
| 80 | + * @return 通道列表 | |
| 81 | + */ | |
| 69 | 82 | @GetMapping("/devices/{deviceId}/channels") |
| 70 | - public ResponseEntity<PageResult> channels(@PathVariable String deviceId, | |
| 83 | + public ResponseEntity<PageInfo> channels(@PathVariable String deviceId, | |
| 71 | 84 | int page, int count, |
| 72 | 85 | @RequestParam(required = false) String query, |
| 73 | - @RequestParam(required = false) String online, | |
| 86 | + @RequestParam(required = false) Boolean online, | |
| 74 | 87 | @RequestParam(required = false) Boolean channelType |
| 75 | 88 | ){ |
| 76 | 89 | |
| 77 | 90 | if (logger.isDebugEnabled()) { |
| 78 | 91 | logger.debug("查询所有视频设备API调用"); |
| 79 | 92 | } |
| 80 | - PageResult pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); | |
| 93 | + if (StringUtils.isEmpty(query)) { | |
| 94 | + query = null; | |
| 95 | + } | |
| 96 | + | |
| 97 | + PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); | |
| 81 | 98 | return new ResponseEntity<>(pageResult,HttpStatus.OK); |
| 82 | 99 | } |
| 83 | 100 | |
| ... | ... | @@ -86,11 +103,25 @@ public class DeviceController { |
| 86 | 103 | |
| 87 | 104 | if (logger.isDebugEnabled()) { |
| 88 | 105 | } |
| 89 | - logger.debug("设备信息同步API调用,deviceId:" + deviceId); | |
| 106 | + logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); | |
| 90 | 107 | |
| 91 | 108 | Device device = storager.queryVideoDevice(deviceId); |
| 92 | - cmder.catalogQuery(device); | |
| 93 | - DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(); | |
| 109 | + cmder.catalogQuery(device, event -> { | |
| 110 | + Response response = event.getResponse(); | |
| 111 | + RequestMessage msg = new RequestMessage(); | |
| 112 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); | |
| 113 | + msg.setData(String.format("同步通道失败,错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); | |
| 114 | + resultHolder.invokeResult(msg); | |
| 115 | + }); | |
| 116 | + DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(2*1000L); | |
| 117 | + result.onTimeout(()->{ | |
| 118 | + logger.warn(String.format("设备通道信息同步超时")); | |
| 119 | + // 释放rtpserver | |
| 120 | + RequestMessage msg = new RequestMessage(); | |
| 121 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId); | |
| 122 | + msg.setData("Timeout"); | |
| 123 | + resultHolder.invokeResult(msg); | |
| 124 | + }); | |
| 94 | 125 | resultHolder.put(DeferredResultHolder.CALLBACK_CMD_CATALOG+deviceId, result); |
| 95 | 126 | return result; |
| 96 | 127 | } |
| ... | ... | @@ -124,7 +155,7 @@ public class DeviceController { |
| 124 | 155 | * @return 子通道列表 |
| 125 | 156 | */ |
| 126 | 157 | @GetMapping("/subChannels/{deviceId}/{channelId}/channels") |
| 127 | - public ResponseEntity<PageResult> subChannels(@PathVariable String deviceId, | |
| 158 | + public ResponseEntity<PageInfo> subChannels(@PathVariable String deviceId, | |
| 128 | 159 | @PathVariable String channelId, |
| 129 | 160 | int page, |
| 130 | 161 | int count, |
| ... | ... | @@ -137,23 +168,23 @@ public class DeviceController { |
| 137 | 168 | } |
| 138 | 169 | DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); |
| 139 | 170 | if (deviceChannel == null) { |
| 140 | - PageResult<DeviceChannel> deviceChannelPageResult = new PageResult<>(); | |
| 171 | + PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>(); | |
| 141 | 172 | return new ResponseEntity<>(deviceChannelPageResult,HttpStatus.OK); |
| 142 | 173 | } |
| 143 | 174 | |
| 144 | - PageResult pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); | |
| 175 | + PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); | |
| 145 | 176 | return new ResponseEntity<>(pageResult,HttpStatus.OK); |
| 146 | 177 | } |
| 147 | 178 | |
| 148 | 179 | @PostMapping("/channel/update/{deviceId}") |
| 149 | - public ResponseEntity<PageResult> updateChannel(@PathVariable String deviceId,DeviceChannel channel){ | |
| 180 | + public ResponseEntity<PageInfo> updateChannel(@PathVariable String deviceId,DeviceChannel channel){ | |
| 150 | 181 | storager.updateChannel(deviceId, channel); |
| 151 | 182 | return new ResponseEntity<>(null,HttpStatus.OK); |
| 152 | 183 | } |
| 153 | 184 | |
| 154 | 185 | @GetMapping("/devices/{deviceId}/transport/{streamMode}") |
| 155 | 186 | @PostMapping("/devices/{deviceId}/transport/{streamMode}") |
| 156 | - public ResponseEntity<PageResult> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ | |
| 187 | + public ResponseEntity<PageInfo> updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ | |
| 157 | 188 | Device device = storager.queryVideoDevice(deviceId); |
| 158 | 189 | device.setStreamMode(streamMode); |
| 159 | 190 | storager.updateDevice(device); | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/device/entity/Device.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.vmanager.device.entity; | |
| 2 | - | |
| 3 | -import java.util.List; | |
| 4 | - | |
| 5 | -import javax.persistence.Column; | |
| 6 | -import javax.persistence.Id; | |
| 7 | -import javax.persistence.Table; | |
| 8 | -import javax.persistence.Transient; | |
| 9 | -import javax.validation.constraints.Max; | |
| 10 | -import javax.validation.constraints.NotNull; | |
| 11 | -import javax.validation.constraints.Size; | |
| 12 | - | |
| 13 | -import io.swagger.annotations.ApiModel; | |
| 14 | -import io.swagger.annotations.ApiModelProperty; | |
| 15 | - | |
| 16 | -/** | |
| 17 | - * @Description:视频设备信息 | |
| 18 | - * @author: songww | |
| 19 | - * @date: 2020年5月8日 下午2:05:56 | |
| 20 | - */ | |
| 21 | -@ApiModel(value = "视频设备信息", description = "视频设备信息") | |
| 22 | -@Table(name="VMP_VIDEODEVICES") | |
| 23 | -public class Device { | |
| 24 | - | |
| 25 | - /** | |
| 26 | - * 设备Id | |
| 27 | - */ | |
| 28 | - @ApiModelProperty("设备编号") | |
| 29 | - @Id | |
| 30 | - @Column(name="DEVICE_ID") | |
| 31 | - @NotNull(message = "deviceId 不能为 null") | |
| 32 | - @Size(min = 4, max = 32, message = "deviceId 必须大于 4 位并且小于 32 位") | |
| 33 | - private String deviceId; | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 设备名称 | |
| 37 | - */ | |
| 38 | - @ApiModelProperty("设备名称") | |
| 39 | - @Column(name="DEVICE_NAME") | |
| 40 | - @Size(max = 32, message = "deviceName 必须小于 32 位") | |
| 41 | - private String deviceName; | |
| 42 | - | |
| 43 | - /** | |
| 44 | - * 生产厂商 | |
| 45 | - */ | |
| 46 | - @ApiModelProperty("生产厂商") | |
| 47 | - @Column(name="MANUFACTURER") | |
| 48 | - @Size(max = 64, message = "manufacturer 必须小于 64 位") | |
| 49 | - private String manufacturer; | |
| 50 | - | |
| 51 | - /** | |
| 52 | - * 型号 | |
| 53 | - */ | |
| 54 | - @ApiModelProperty("型号") | |
| 55 | - @Column(name="MODEL") | |
| 56 | - @Size(max = 64, message = "manufacturer 必须小于 64 位") | |
| 57 | - private String model; | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * 固件版本 | |
| 61 | - */ | |
| 62 | - @ApiModelProperty("固件版本") | |
| 63 | - @Column(name="FIRMWARE") | |
| 64 | - @Size(max = 64, message = "firmware 必须小于 64 位") | |
| 65 | - private String firmware; | |
| 66 | - | |
| 67 | - /** | |
| 68 | - * 通信协议 | |
| 69 | - * GB28181 ONVIF | |
| 70 | - */ | |
| 71 | - @ApiModelProperty("通信协议") | |
| 72 | - @Column(name="PROTOCOL") | |
| 73 | - @NotNull(message = "protocol 不能为 null") | |
| 74 | - @Size(max = 16, message = "protocol 必须小于 16 位") | |
| 75 | - private String protocol; | |
| 76 | - | |
| 77 | - /** | |
| 78 | - * SIP 传输协议 | |
| 79 | - * UDP/TCP | |
| 80 | - */ | |
| 81 | - @ApiModelProperty("SIP 传输协议") | |
| 82 | - @Column(name="TRANSPORT") | |
| 83 | - @Size(min = 3,max = 3 ,message = "transport 必须为 3 位") | |
| 84 | - private String transport; | |
| 85 | - | |
| 86 | - /** | |
| 87 | - * 数据流传输模式 | |
| 88 | - * UDP:udp传输 | |
| 89 | - * TCP-ACTIVE:tcp主动模式 | |
| 90 | - * TCP-PASSIVE:tcp被动模式 | |
| 91 | - */ | |
| 92 | - @ApiModelProperty("数据流传输模式") | |
| 93 | - @Column(name="STREAM_MODE") | |
| 94 | - @Size(max = 64, message = "streamMode 必须小于 16 位") | |
| 95 | - private String streamMode; | |
| 96 | - | |
| 97 | - /** | |
| 98 | - * IP地址 | |
| 99 | - */ | |
| 100 | - @ApiModelProperty("IP地址") | |
| 101 | - @Column(name="IP") | |
| 102 | - @Size(max = 15, message = "streamMode 必须小于 15 位") | |
| 103 | - private String ip; | |
| 104 | - | |
| 105 | - /** | |
| 106 | - * 端口号 | |
| 107 | - */ | |
| 108 | - @ApiModelProperty("端口号") | |
| 109 | - @Column(name="PORT") | |
| 110 | - @Max(value = 65535,message = "port 最大值为 65535") | |
| 111 | - private Integer port; | |
| 112 | - | |
| 113 | - /** | |
| 114 | - * 在线状态 1在线, 0离线 | |
| 115 | - */ | |
| 116 | - @ApiModelProperty("在线状态") | |
| 117 | - @Size(min = 1,max = 1 ,message = "online 必须为 1 位") | |
| 118 | - @Column(name="ONLINE") | |
| 119 | - private String online; | |
| 120 | - | |
| 121 | - /** | |
| 122 | - * 通道数量 | |
| 123 | - */ | |
| 124 | - @ApiModelProperty("通道数量") | |
| 125 | - @Column(name="CHANNEL_SUM") | |
| 126 | - @Max(value = 1000000000,message = "channelSum 最大值为 1000000000") | |
| 127 | - private Integer channelSum; | |
| 128 | - | |
| 129 | - @Override | |
| 130 | - public String toString() { | |
| 131 | - return "Device{" + | |
| 132 | - "deviceId='" + deviceId + '\'' + | |
| 133 | - ", deviceName='" + deviceName + '\'' + | |
| 134 | - ", manufacturer='" + manufacturer + '\'' + | |
| 135 | - ", model='" + model + '\'' + | |
| 136 | - ", firmware='" + firmware + '\'' + | |
| 137 | - ", protocol='" + protocol + '\'' + | |
| 138 | - ", transport='" + transport + '\'' + | |
| 139 | - ", streamMode='" + streamMode + '\'' + | |
| 140 | - ", ip='" + ip + '\'' + | |
| 141 | - ", port=" + port + | |
| 142 | - ", online='" + online + '\'' + | |
| 143 | - ", channelSum=" + channelSum + | |
| 144 | - ", createTime='" + createTime + '\'' + | |
| 145 | - ", registerTime='" + registerTime + '\'' + | |
| 146 | - ", heartbeatTime='" + heartbeatTime + '\'' + | |
| 147 | - ", updateTime='" + updateTime + '\'' + | |
| 148 | - ", updatePerson='" + updatePerson + '\'' + | |
| 149 | - ", syncTime='" + syncTime + '\'' + | |
| 150 | - ", syncPerson='" + syncPerson + '\'' + | |
| 151 | - ", username='" + username + '\'' + | |
| 152 | - ", password='" + password + '\'' + | |
| 153 | - ", channelList=" + channelList + | |
| 154 | - '}'; | |
| 155 | - } | |
| 156 | - | |
| 157 | - /** | |
| 158 | - * 创建时间 | |
| 159 | - */ | |
| 160 | - @ApiModelProperty("创建时间") | |
| 161 | - @Column(name="CREATE_TIME") | |
| 162 | - private String createTime; | |
| 163 | - | |
| 164 | - /** | |
| 165 | - * 注册时间 | |
| 166 | - */ | |
| 167 | - @ApiModelProperty("注册时间") | |
| 168 | - @Column(name="REGISTER_TIME") | |
| 169 | - private String registerTime; | |
| 170 | - | |
| 171 | - /** | |
| 172 | - * 心跳时间 | |
| 173 | - */ | |
| 174 | - @ApiModelProperty("心跳时间") | |
| 175 | - @Column(name="HEARTBEAT_TIME") | |
| 176 | - private String heartbeatTime; | |
| 177 | - | |
| 178 | - /** | |
| 179 | - * 修改时间 | |
| 180 | - */ | |
| 181 | - @ApiModelProperty("更新时间") | |
| 182 | - @Column(name="UPDATE_TIME") | |
| 183 | - private String updateTime; | |
| 184 | - | |
| 185 | - /** | |
| 186 | - * 修改人 | |
| 187 | - */ | |
| 188 | - @ApiModelProperty("修改人") | |
| 189 | - @Column(name="UPDATE_PERSON") | |
| 190 | - private String updatePerson; | |
| 191 | - | |
| 192 | - /** | |
| 193 | - * 同步时间 | |
| 194 | - */ | |
| 195 | - @ApiModelProperty("同步时间") | |
| 196 | - @Column(name="SYNC_TIME") | |
| 197 | - private String syncTime; | |
| 198 | - | |
| 199 | - /** | |
| 200 | - * 同步人 | |
| 201 | - */ | |
| 202 | - @ApiModelProperty("同步人") | |
| 203 | - @Column(name="SYNC_PERSON") | |
| 204 | - private String syncPerson; | |
| 205 | - | |
| 206 | - /** | |
| 207 | - * ONVIF协议-用户名 | |
| 208 | - */ | |
| 209 | - @ApiModelProperty("用户名") | |
| 210 | - @Column(name="USERNAME") | |
| 211 | - @Size(max = 32, message = "username 必须小于 32 位") | |
| 212 | - private String username; | |
| 213 | - | |
| 214 | - /** | |
| 215 | - * ONVIF协议-密码 | |
| 216 | - */ | |
| 217 | - @ApiModelProperty("密码") | |
| 218 | - @Size(max = 32, message = "password 必须小于 32 位") | |
| 219 | - @Column(name="PASSWORD") | |
| 220 | - private String password; | |
| 221 | - | |
| 222 | - @Transient | |
| 223 | - private List<DeviceChannel> channelList; | |
| 224 | - | |
| 225 | - | |
| 226 | - public String getDeviceId() { | |
| 227 | - return deviceId; | |
| 228 | - } | |
| 229 | - | |
| 230 | - public void setDeviceId(String deviceId) { | |
| 231 | - this.deviceId = deviceId; | |
| 232 | - } | |
| 233 | - | |
| 234 | - public String getDeviceName() { | |
| 235 | - return deviceName; | |
| 236 | - } | |
| 237 | - | |
| 238 | - public void setDeviceName(String deviceName) { | |
| 239 | - this.deviceName = deviceName; | |
| 240 | - } | |
| 241 | - | |
| 242 | - public String getManufacturer() { | |
| 243 | - return manufacturer; | |
| 244 | - } | |
| 245 | - | |
| 246 | - public void setManufacturer(String manufacturer) { | |
| 247 | - this.manufacturer = manufacturer; | |
| 248 | - } | |
| 249 | - | |
| 250 | - public String getModel() { | |
| 251 | - return model; | |
| 252 | - } | |
| 253 | - | |
| 254 | - public void setModel(String model) { | |
| 255 | - this.model = model; | |
| 256 | - } | |
| 257 | - | |
| 258 | - public String getFirmware() { | |
| 259 | - return firmware; | |
| 260 | - } | |
| 261 | - | |
| 262 | - public void setFirmware(String firmware) { | |
| 263 | - this.firmware = firmware; | |
| 264 | - } | |
| 265 | - | |
| 266 | - public String getProtocol() { | |
| 267 | - return protocol; | |
| 268 | - } | |
| 269 | - | |
| 270 | - public void setProtocol(String protocol) { | |
| 271 | - this.protocol = protocol; | |
| 272 | - } | |
| 273 | - | |
| 274 | - public String getTransport() { | |
| 275 | - return transport; | |
| 276 | - } | |
| 277 | - | |
| 278 | - public void setTransport(String transport) { | |
| 279 | - this.transport = transport; | |
| 280 | - } | |
| 281 | - | |
| 282 | - public String getStreamMode() { | |
| 283 | - return streamMode; | |
| 284 | - } | |
| 285 | - | |
| 286 | - public void setStreamMode(String streamMode) { | |
| 287 | - this.streamMode = streamMode; | |
| 288 | - } | |
| 289 | - | |
| 290 | - public String getIp() { | |
| 291 | - return ip; | |
| 292 | - } | |
| 293 | - | |
| 294 | - public void setIp(String ip) { | |
| 295 | - this.ip = ip; | |
| 296 | - } | |
| 297 | - | |
| 298 | - public Integer getPort() { | |
| 299 | - return port; | |
| 300 | - } | |
| 301 | - | |
| 302 | - public void setPort(Integer port) { | |
| 303 | - this.port = port; | |
| 304 | - } | |
| 305 | - | |
| 306 | - public String getOnline() { | |
| 307 | - return online; | |
| 308 | - } | |
| 309 | - | |
| 310 | - public void setOnline(String online) { | |
| 311 | - this.online = online; | |
| 312 | - } | |
| 313 | - | |
| 314 | - public Integer getChannelSum() { | |
| 315 | - return channelSum; | |
| 316 | - } | |
| 317 | - | |
| 318 | - public void setChannelSum(Integer channelSum) { | |
| 319 | - this.channelSum = channelSum; | |
| 320 | - } | |
| 321 | - | |
| 322 | - public String getCreateTime() { | |
| 323 | - return createTime; | |
| 324 | - } | |
| 325 | - | |
| 326 | - public void setCreateTime(String createTime) { | |
| 327 | - this.createTime = createTime; | |
| 328 | - } | |
| 329 | - | |
| 330 | - public String getRegisterTime() { | |
| 331 | - return registerTime; | |
| 332 | - } | |
| 333 | - | |
| 334 | - public void setRegisterTime(String registerTime) { | |
| 335 | - this.registerTime = registerTime; | |
| 336 | - } | |
| 337 | - | |
| 338 | - public String getHeartbeatTime() { | |
| 339 | - return heartbeatTime; | |
| 340 | - } | |
| 341 | - | |
| 342 | - public void setHeartbeatTime(String heartbeatTime) { | |
| 343 | - this.heartbeatTime = heartbeatTime; | |
| 344 | - } | |
| 345 | - | |
| 346 | - public String getUpdateTime() { | |
| 347 | - return updateTime; | |
| 348 | - } | |
| 349 | - | |
| 350 | - public void setUpdateTime(String updateTime) { | |
| 351 | - this.updateTime = updateTime; | |
| 352 | - } | |
| 353 | - | |
| 354 | - public String getUpdatePerson() { | |
| 355 | - return updatePerson; | |
| 356 | - } | |
| 357 | - | |
| 358 | - public void setUpdatePerson(String updatePerson) { | |
| 359 | - this.updatePerson = updatePerson; | |
| 360 | - } | |
| 361 | - | |
| 362 | - public String getSyncTime() { | |
| 363 | - return syncTime; | |
| 364 | - } | |
| 365 | - | |
| 366 | - public void setSyncTime(String syncTime) { | |
| 367 | - this.syncTime = syncTime; | |
| 368 | - } | |
| 369 | - | |
| 370 | - public String getSyncPerson() { | |
| 371 | - return syncPerson; | |
| 372 | - } | |
| 373 | - | |
| 374 | - public void setSyncPerson(String syncPerson) { | |
| 375 | - this.syncPerson = syncPerson; | |
| 376 | - } | |
| 377 | - | |
| 378 | - public String getUsername() { | |
| 379 | - return username; | |
| 380 | - } | |
| 381 | - | |
| 382 | - public void setUsername(String username) { | |
| 383 | - this.username = username; | |
| 384 | - } | |
| 385 | - | |
| 386 | - public String getPassword() { | |
| 387 | - return password; | |
| 388 | - } | |
| 389 | - | |
| 390 | - public void setPassword(String password) { | |
| 391 | - this.password = password; | |
| 392 | - } | |
| 393 | - | |
| 394 | - public List<DeviceChannel> getChannelList() { | |
| 395 | - return channelList; | |
| 396 | - } | |
| 397 | - | |
| 398 | - public void setChannelList(List<DeviceChannel> channelList) { | |
| 399 | - this.channelList = channelList; | |
| 400 | - } | |
| 401 | -} |
src/main/java/com/genersoft/iot/vmp/vmanager/device/entity/DeviceChannel.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.vmanager.device.entity; | |
| 2 | - | |
| 3 | -import javax.persistence.Column; | |
| 4 | -import javax.persistence.Id; | |
| 5 | -import javax.persistence.Table; | |
| 6 | - | |
| 7 | -import io.swagger.annotations.ApiModel; | |
| 8 | -import io.swagger.annotations.ApiModelProperty; | |
| 9 | - | |
| 10 | -/** | |
| 11 | - * @Description:设备通道信息 | |
| 12 | - * @author: songww | |
| 13 | - * @date: 2020年5月20日 下午9:00:46 | |
| 14 | - */ | |
| 15 | -@ApiModel(value = "设备通道信息", description = "设备通道信息") | |
| 16 | -@Table(name="VMP_VIDEOCHANNELS") | |
| 17 | -public class DeviceChannel { | |
| 18 | - | |
| 19 | - /** | |
| 20 | - * 通道编号 | |
| 21 | - */ | |
| 22 | - @ApiModelProperty("通道编号") | |
| 23 | - @Id | |
| 24 | - @Column(name="CHANNEL_ID") | |
| 25 | - private String channelId; | |
| 26 | - | |
| 27 | - /** | |
| 28 | - * 设备编号 | |
| 29 | - */ | |
| 30 | - @ApiModelProperty("设备编号") | |
| 31 | - @Column(name="DEVICE_ID") | |
| 32 | - private String deviceId; | |
| 33 | - | |
| 34 | - /** | |
| 35 | - * 通道名 | |
| 36 | - */ | |
| 37 | - @ApiModelProperty("通道名") | |
| 38 | - @Column(name="CHANNEL_NAME") | |
| 39 | - private String channelName; | |
| 40 | - | |
| 41 | - /** | |
| 42 | - * 生产厂商 | |
| 43 | - */ | |
| 44 | - @ApiModelProperty("生产厂商") | |
| 45 | - @Column(name="MANUFACTURER") | |
| 46 | - private String manufacture; | |
| 47 | - | |
| 48 | - /** | |
| 49 | - * 型号 | |
| 50 | - */ | |
| 51 | - @ApiModelProperty("型号") | |
| 52 | - @Column(name="MODEL") | |
| 53 | - private String model; | |
| 54 | - | |
| 55 | - /** | |
| 56 | - * 设备归属 | |
| 57 | - */ | |
| 58 | - @ApiModelProperty("设备归属") | |
| 59 | - @Column(name="OWNER") | |
| 60 | - private String owner; | |
| 61 | - | |
| 62 | - /** | |
| 63 | - * 行政区域 | |
| 64 | - */ | |
| 65 | - @ApiModelProperty("行政区域") | |
| 66 | - @Column(name="CIVIL_CODE") | |
| 67 | - private String civilCode; | |
| 68 | - | |
| 69 | - /** | |
| 70 | - * 警区 | |
| 71 | - */ | |
| 72 | - @ApiModelProperty("警区") | |
| 73 | - @Column(name="BLOCK") | |
| 74 | - private String block; | |
| 75 | - | |
| 76 | - /** | |
| 77 | - * 安装地址 | |
| 78 | - */ | |
| 79 | - @ApiModelProperty("安装地址") | |
| 80 | - @Column(name="ADDRESS") | |
| 81 | - private String address; | |
| 82 | - | |
| 83 | - /** | |
| 84 | - * 是否有子设备 1有, 0没有 | |
| 85 | - */ | |
| 86 | - @ApiModelProperty("是否有子设备") | |
| 87 | - @Column(name="PARENTAL") | |
| 88 | - private String parental; | |
| 89 | - | |
| 90 | - /** | |
| 91 | - * 父级id | |
| 92 | - */ | |
| 93 | - @ApiModelProperty("父级编码") | |
| 94 | - @Column(name="PARENT_ID") | |
| 95 | - private String parentId; | |
| 96 | - | |
| 97 | - /** | |
| 98 | - * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 | |
| 99 | - */ | |
| 100 | - @ApiModelProperty("信令安全模式") | |
| 101 | - @Column(name="SAFETY_WAY") | |
| 102 | - private String safetyWay; | |
| 103 | - | |
| 104 | - /** | |
| 105 | - * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 | |
| 106 | - */ | |
| 107 | - @ApiModelProperty("注册方式") | |
| 108 | - @Column(name="REGISTER_WAY") | |
| 109 | - private String registerWay; | |
| 110 | - | |
| 111 | - /** | |
| 112 | - * 证书序列号 | |
| 113 | - */ | |
| 114 | - @ApiModelProperty("证书序列号") | |
| 115 | - @Column(name="CERT_NUM") | |
| 116 | - private String certNum; | |
| 117 | - | |
| 118 | - /** | |
| 119 | - * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 | |
| 120 | - */ | |
| 121 | - @ApiModelProperty("证书有效标识") | |
| 122 | - @Column(name="CERT_VALID") | |
| 123 | - private String certValid; | |
| 124 | - | |
| 125 | - /** | |
| 126 | - * 证书无效原因码 | |
| 127 | - */ | |
| 128 | - @ApiModelProperty("证书无效原因码") | |
| 129 | - @Column(name="CERT_ERRCODE") | |
| 130 | - private String certErrCode; | |
| 131 | - | |
| 132 | - /** | |
| 133 | - * 证书终止有效期 | |
| 134 | - */ | |
| 135 | - @ApiModelProperty("证书终止有效期") | |
| 136 | - @Column(name="CERT_ENDTIME") | |
| 137 | - private String certEndTime; | |
| 138 | - | |
| 139 | - /** | |
| 140 | - * 保密属性 缺省为0; 0:不涉密, 1:涉密 | |
| 141 | - */ | |
| 142 | - @ApiModelProperty("保密属性") | |
| 143 | - @Column(name="SECRECY") | |
| 144 | - private String secrecy; | |
| 145 | - | |
| 146 | - /** | |
| 147 | - * IP地址 | |
| 148 | - */ | |
| 149 | - @ApiModelProperty("IP地址") | |
| 150 | - @Column(name="IP") | |
| 151 | - private String ip; | |
| 152 | - | |
| 153 | - /** | |
| 154 | - * 端口号 | |
| 155 | - */ | |
| 156 | - @ApiModelProperty("端口号") | |
| 157 | - @Column(name="PORT") | |
| 158 | - private Integer port; | |
| 159 | - | |
| 160 | - /** | |
| 161 | - * 密码 | |
| 162 | - */ | |
| 163 | - @ApiModelProperty("密码") | |
| 164 | - @Column(name="PASSWORD") | |
| 165 | - private String password; | |
| 166 | - | |
| 167 | - /** | |
| 168 | - * 在线/离线 | |
| 169 | - * 1在线,0离线 | |
| 170 | - * 默认在线 | |
| 171 | - * 信令: | |
| 172 | - * <Status>ON</Status> | |
| 173 | - * <Status>OFF</Status> | |
| 174 | - * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF | |
| 175 | - */ | |
| 176 | - @ApiModelProperty("状态") | |
| 177 | - @Column(name="ONLINE") | |
| 178 | - private String online; | |
| 179 | - | |
| 180 | - /** | |
| 181 | - * 经度 | |
| 182 | - */ | |
| 183 | - @ApiModelProperty("经度") | |
| 184 | - @Column(name="LONGITUDE") | |
| 185 | - private double longitude; | |
| 186 | - | |
| 187 | - /** | |
| 188 | - * 纬度 | |
| 189 | - */ | |
| 190 | - @ApiModelProperty("纬度") | |
| 191 | - @Column(name="LATITUDE") | |
| 192 | - private double latitude; | |
| 193 | - | |
| 194 | - public String getChannelId() { | |
| 195 | - return channelId; | |
| 196 | - } | |
| 197 | - | |
| 198 | - public void setChannelId(String channelId) { | |
| 199 | - this.channelId = channelId; | |
| 200 | - } | |
| 201 | - | |
| 202 | - public String getDeviceId() { | |
| 203 | - return deviceId; | |
| 204 | - } | |
| 205 | - | |
| 206 | - public void setDeviceId(String deviceId) { | |
| 207 | - this.deviceId = deviceId; | |
| 208 | - } | |
| 209 | - | |
| 210 | - public String getChannelName() { | |
| 211 | - return channelName; | |
| 212 | - } | |
| 213 | - | |
| 214 | - public void setChannelName(String channelName) { | |
| 215 | - this.channelName = channelName; | |
| 216 | - } | |
| 217 | - | |
| 218 | - public String getManufacture() { | |
| 219 | - return manufacture; | |
| 220 | - } | |
| 221 | - | |
| 222 | - public void setManufacture(String manufacture) { | |
| 223 | - this.manufacture = manufacture; | |
| 224 | - } | |
| 225 | - | |
| 226 | - public String getModel() { | |
| 227 | - return model; | |
| 228 | - } | |
| 229 | - | |
| 230 | - public void setModel(String model) { | |
| 231 | - this.model = model; | |
| 232 | - } | |
| 233 | - | |
| 234 | - public String getOwner() { | |
| 235 | - return owner; | |
| 236 | - } | |
| 237 | - | |
| 238 | - public void setOwner(String owner) { | |
| 239 | - this.owner = owner; | |
| 240 | - } | |
| 241 | - | |
| 242 | - public String getCivilCode() { | |
| 243 | - return civilCode; | |
| 244 | - } | |
| 245 | - | |
| 246 | - public void setCivilCode(String civilCode) { | |
| 247 | - this.civilCode = civilCode; | |
| 248 | - } | |
| 249 | - | |
| 250 | - public String getBlock() { | |
| 251 | - return block; | |
| 252 | - } | |
| 253 | - | |
| 254 | - public void setBlock(String block) { | |
| 255 | - this.block = block; | |
| 256 | - } | |
| 257 | - | |
| 258 | - public String getAddress() { | |
| 259 | - return address; | |
| 260 | - } | |
| 261 | - | |
| 262 | - public void setAddress(String address) { | |
| 263 | - this.address = address; | |
| 264 | - } | |
| 265 | - | |
| 266 | - public String getParental() { | |
| 267 | - return parental; | |
| 268 | - } | |
| 269 | - | |
| 270 | - public void setParental(String parental) { | |
| 271 | - this.parental = parental; | |
| 272 | - } | |
| 273 | - | |
| 274 | - public String getParentId() { | |
| 275 | - return parentId; | |
| 276 | - } | |
| 277 | - | |
| 278 | - public void setParentId(String parentId) { | |
| 279 | - this.parentId = parentId; | |
| 280 | - } | |
| 281 | - | |
| 282 | - public String getSafetyWay() { | |
| 283 | - return safetyWay; | |
| 284 | - } | |
| 285 | - | |
| 286 | - public void setSafetyWay(String safetyWay) { | |
| 287 | - this.safetyWay = safetyWay; | |
| 288 | - } | |
| 289 | - | |
| 290 | - public String getRegisterWay() { | |
| 291 | - return registerWay; | |
| 292 | - } | |
| 293 | - | |
| 294 | - public void setRegisterWay(String registerWay) { | |
| 295 | - this.registerWay = registerWay; | |
| 296 | - } | |
| 297 | - | |
| 298 | - public String getCertNum() { | |
| 299 | - return certNum; | |
| 300 | - } | |
| 301 | - | |
| 302 | - public void setCertNum(String certNum) { | |
| 303 | - this.certNum = certNum; | |
| 304 | - } | |
| 305 | - | |
| 306 | - public String getCertValid() { | |
| 307 | - return certValid; | |
| 308 | - } | |
| 309 | - | |
| 310 | - public void setCertValid(String certValid) { | |
| 311 | - this.certValid = certValid; | |
| 312 | - } | |
| 313 | - | |
| 314 | - public String getCertErrCode() { | |
| 315 | - return certErrCode; | |
| 316 | - } | |
| 317 | - | |
| 318 | - public void setCertErrCode(String certErrCode) { | |
| 319 | - this.certErrCode = certErrCode; | |
| 320 | - } | |
| 321 | - | |
| 322 | - public String getCertEndTime() { | |
| 323 | - return certEndTime; | |
| 324 | - } | |
| 325 | - | |
| 326 | - public void setCertEndTime(String certEndTime) { | |
| 327 | - this.certEndTime = certEndTime; | |
| 328 | - } | |
| 329 | - | |
| 330 | - public String getSecrecy() { | |
| 331 | - return secrecy; | |
| 332 | - } | |
| 333 | - | |
| 334 | - public void setSecrecy(String secrecy) { | |
| 335 | - this.secrecy = secrecy; | |
| 336 | - } | |
| 337 | - | |
| 338 | - public String getIp() { | |
| 339 | - return ip; | |
| 340 | - } | |
| 341 | - | |
| 342 | - public void setIp(String ip) { | |
| 343 | - this.ip = ip; | |
| 344 | - } | |
| 345 | - | |
| 346 | - public Integer getPort() { | |
| 347 | - return port; | |
| 348 | - } | |
| 349 | - | |
| 350 | - public void setPort(Integer port) { | |
| 351 | - this.port = port; | |
| 352 | - } | |
| 353 | - | |
| 354 | - public String getPassword() { | |
| 355 | - return password; | |
| 356 | - } | |
| 357 | - | |
| 358 | - public void setPassword(String password) { | |
| 359 | - this.password = password; | |
| 360 | - } | |
| 361 | - | |
| 362 | - public String getOnline() { | |
| 363 | - return online; | |
| 364 | - } | |
| 365 | - | |
| 366 | - public void setOnline(String online) { | |
| 367 | - this.online = online; | |
| 368 | - } | |
| 369 | - | |
| 370 | - public double getLongitude() { | |
| 371 | - return longitude; | |
| 372 | - } | |
| 373 | - | |
| 374 | - public void setLongitude(double longitude) { | |
| 375 | - this.longitude = longitude; | |
| 376 | - } | |
| 377 | - | |
| 378 | - public double getLatitude() { | |
| 379 | - return latitude; | |
| 380 | - } | |
| 381 | - | |
| 382 | - public void setLatitude(double latitude) { | |
| 383 | - this.latitude = latitude; | |
| 384 | - } | |
| 385 | -} |
src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
| ... | ... | @@ -7,6 +7,8 @@ import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 7 | 7 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 8 | 8 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 9 | 9 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 10 | +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | |
| 11 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 10 | 12 | import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
| 11 | 13 | import org.slf4j.Logger; |
| 12 | 14 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -27,6 +29,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 27 | 29 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 28 | 30 | import org.springframework.web.context.request.async.DeferredResult; |
| 29 | 31 | |
| 32 | +import javax.sip.message.Response; | |
| 30 | 33 | import java.text.DecimalFormat; |
| 31 | 34 | import java.util.UUID; |
| 32 | 35 | |
| ... | ... | @@ -44,6 +47,9 @@ public class PlayController { |
| 44 | 47 | private IVideoManagerStorager storager; |
| 45 | 48 | |
| 46 | 49 | @Autowired |
| 50 | + private IRedisCatchStorage redisCatchStorage; | |
| 51 | + | |
| 52 | + @Autowired | |
| 47 | 53 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 48 | 54 | |
| 49 | 55 | @Autowired |
| ... | ... | @@ -58,18 +64,11 @@ public class PlayController { |
| 58 | 64 | |
| 59 | 65 | |
| 60 | 66 | Device device = storager.queryVideoDevice(deviceId); |
| 61 | - StreamInfo streamInfo = storager.queryPlayByDevice(deviceId, channelId); | |
| 67 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); | |
| 62 | 68 | |
| 63 | 69 | UUID uuid = UUID.randomUUID(); |
| 64 | 70 | DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); |
| 65 | - // 超时处理 | |
| 66 | - result.onTimeout(()->{ | |
| 67 | - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | |
| 68 | - RequestMessage msg = new RequestMessage(); | |
| 69 | - msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 70 | - msg.setData("Timeout"); | |
| 71 | - resultHolder.invokeResult(msg); | |
| 72 | - }); | |
| 71 | + | |
| 73 | 72 | // 录像查询以channelId作为deviceId查询 |
| 74 | 73 | resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); |
| 75 | 74 | |
| ... | ... | @@ -78,9 +77,15 @@ public class PlayController { |
| 78 | 77 | cmder.playStreamCmd(device, channelId, (JSONObject response) -> { |
| 79 | 78 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 80 | 79 | playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); |
| 80 | + }, event -> { | |
| 81 | + RequestMessage msg = new RequestMessage(); | |
| 82 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 83 | + Response response = event.getResponse(); | |
| 84 | + msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); | |
| 85 | + resultHolder.invokeResult(msg); | |
| 81 | 86 | }); |
| 82 | 87 | } else { |
| 83 | - String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); | |
| 88 | + String streamId = streamInfo.getStreamId(); | |
| 84 | 89 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); |
| 85 | 90 | if (rtpInfo.getBoolean("exist")) { |
| 86 | 91 | RequestMessage msg = new RequestMessage(); |
| ... | ... | @@ -88,58 +93,107 @@ public class PlayController { |
| 88 | 93 | msg.setData(JSON.toJSONString(streamInfo)); |
| 89 | 94 | resultHolder.invokeResult(msg); |
| 90 | 95 | } else { |
| 91 | - storager.stopPlay(streamInfo); | |
| 92 | - // TODO playStreamCmd 超时处理 | |
| 96 | + redisCatchStorage.stopPlay(streamInfo); | |
| 93 | 97 | cmder.playStreamCmd(device, channelId, (JSONObject response) -> { |
| 94 | 98 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 95 | 99 | playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString()); |
| 100 | + }, event -> { | |
| 101 | + RequestMessage msg = new RequestMessage(); | |
| 102 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 103 | + Response response = event.getResponse(); | |
| 104 | + msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); | |
| 105 | + resultHolder.invokeResult(msg); | |
| 96 | 106 | }); |
| 97 | 107 | } |
| 98 | 108 | } |
| 109 | + | |
| 110 | + // 超时处理 | |
| 111 | + result.onTimeout(()->{ | |
| 112 | + logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); | |
| 113 | + // 释放rtpserver | |
| 114 | + cmder.closeRTPServer(device, channelId); | |
| 115 | + RequestMessage msg = new RequestMessage(); | |
| 116 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 117 | + msg.setData("Timeout"); | |
| 118 | + resultHolder.invokeResult(msg); | |
| 119 | + }); | |
| 99 | 120 | return result; |
| 100 | 121 | } |
| 101 | 122 | |
| 102 | - @PostMapping("/play/{ssrc}/stop") | |
| 103 | - public ResponseEntity<String> playStop(@PathVariable String ssrc) { | |
| 123 | + @PostMapping("/play/{streamId}/stop") | |
| 124 | + public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String streamId) { | |
| 104 | 125 | |
| 105 | - cmder.streamByeCmd(ssrc); | |
| 106 | - StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); | |
| 107 | - if (streamInfo == null) | |
| 108 | - return new ResponseEntity<String>("ssrc not found", HttpStatus.OK); | |
| 109 | - storager.stopPlay(streamInfo); | |
| 110 | - if (logger.isDebugEnabled()) { | |
| 111 | - logger.debug(String.format("设备预览停止API调用,ssrc:%s", ssrc)); | |
| 112 | - } | |
| 126 | + logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId)); | |
| 127 | + | |
| 128 | + UUID uuid = UUID.randomUUID(); | |
| 129 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(); | |
| 113 | 130 | |
| 114 | - if (ssrc != null) { | |
| 131 | + // 录像查询以channelId作为deviceId查询 | |
| 132 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result); | |
| 133 | + | |
| 134 | + cmder.streamByeCmd(streamId, event -> { | |
| 135 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 136 | + if (streamInfo == null) { | |
| 137 | + RequestMessage msg = new RequestMessage(); | |
| 138 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 139 | + msg.setData("streamId not found"); | |
| 140 | + resultHolder.invokeResult(msg); | |
| 141 | + redisCatchStorage.stopPlay(streamInfo); | |
| 142 | + } | |
| 143 | + | |
| 144 | + RequestMessage msg = new RequestMessage(); | |
| 145 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); | |
| 146 | + Response response = event.getResponse(); | |
| 147 | + msg.setData(String.format("success")); | |
| 148 | + resultHolder.invokeResult(msg); | |
| 149 | + }); | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + if (streamId != null) { | |
| 115 | 154 | JSONObject json = new JSONObject(); |
| 116 | - json.put("ssrc", ssrc); | |
| 117 | - return new ResponseEntity<String>(json.toString(), HttpStatus.OK); | |
| 155 | + json.put("streamId", streamId); | |
| 156 | + RequestMessage msg = new RequestMessage(); | |
| 157 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 158 | + msg.setData(json.toString()); | |
| 159 | + resultHolder.invokeResult(msg); | |
| 118 | 160 | } else { |
| 119 | - logger.warn("设备预览停止API调用失败!"); | |
| 120 | - return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); | |
| 161 | + logger.warn("设备预览/回放停止API调用失败!"); | |
| 162 | + RequestMessage msg = new RequestMessage(); | |
| 163 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 164 | + msg.setData("streamId null"); | |
| 165 | + resultHolder.invokeResult(msg); | |
| 121 | 166 | } |
| 167 | + | |
| 168 | + // 超时处理 | |
| 169 | + result.onTimeout(()->{ | |
| 170 | + logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId)); | |
| 171 | + RequestMessage msg = new RequestMessage(); | |
| 172 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); | |
| 173 | + msg.setData("Timeout"); | |
| 174 | + resultHolder.invokeResult(msg); | |
| 175 | + }); | |
| 176 | + return result; | |
| 122 | 177 | } |
| 123 | 178 | |
| 124 | 179 | /** |
| 125 | 180 | * 将不是h264的视频通过ffmpeg 转码为h264 + aac |
| 126 | - * @param ssrc | |
| 181 | + * @param streamId 流ID | |
| 127 | 182 | * @return |
| 128 | 183 | */ |
| 129 | - @PostMapping("/play/{ssrc}/convert") | |
| 130 | - public ResponseEntity<String> playConvert(@PathVariable String ssrc) { | |
| 131 | - StreamInfo streamInfo = storager.queryPlayBySSRC(ssrc); | |
| 184 | + @PostMapping("/play/{streamId}/convert") | |
| 185 | + public ResponseEntity<String> playConvert(@PathVariable String streamId) { | |
| 186 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); | |
| 132 | 187 | if (streamInfo == null) { |
| 133 | 188 | logger.warn("视频转码API调用失败!, 视频流已经停止!"); |
| 134 | 189 | return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); |
| 135 | 190 | } |
| 136 | - String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); | |
| 137 | 191 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId); |
| 138 | 192 | if (!rtpInfo.getBoolean("exist")) { |
| 139 | 193 | logger.warn("视频转码API调用失败!, 视频流已停止推流!"); |
| 140 | 194 | return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); |
| 141 | 195 | } else { |
| 142 | - MediaServerConfig mediaInfo = storager.getMediaInfo(); | |
| 196 | + MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); | |
| 143 | 197 | String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), |
| 144 | 198 | streamId ); |
| 145 | 199 | String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
| ... | ... | @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; |
| 6 | 6 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 7 | 7 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 8 | 8 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 9 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 9 | 10 | import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
| 10 | 11 | import org.slf4j.Logger; |
| 11 | 12 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -27,6 +28,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 27 | 28 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 28 | 29 | import org.springframework.web.context.request.async.DeferredResult; |
| 29 | 30 | |
| 31 | +import javax.sip.message.Response; | |
| 30 | 32 | import java.util.UUID; |
| 31 | 33 | |
| 32 | 34 | @CrossOrigin |
| ... | ... | @@ -43,6 +45,9 @@ public class PlaybackController { |
| 43 | 45 | private IVideoManagerStorager storager; |
| 44 | 46 | |
| 45 | 47 | @Autowired |
| 48 | + private IRedisCatchStorage redisCatchStorage; | |
| 49 | + | |
| 50 | + @Autowired | |
| 46 | 51 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 47 | 52 | |
| 48 | 53 | @Autowired |
| ... | ... | @@ -69,15 +74,21 @@ public class PlaybackController { |
| 69 | 74 | resultHolder.invokeResult(msg); |
| 70 | 75 | }); |
| 71 | 76 | Device device = storager.queryVideoDevice(deviceId); |
| 72 | - StreamInfo streamInfo = storager.queryPlaybackByDevice(deviceId, channelId); | |
| 77 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); | |
| 73 | 78 | if (streamInfo != null) { |
| 74 | 79 | // 停止之前的回放 |
| 75 | - cmder.streamByeCmd(streamInfo.getSsrc()); | |
| 80 | + cmder.streamByeCmd(streamInfo.getStreamId()); | |
| 76 | 81 | } |
| 77 | 82 | resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result); |
| 78 | 83 | cmder.playbackStreamCmd(device, channelId, startTime, endTime, (JSONObject response) -> { |
| 79 | 84 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 80 | 85 | playService.onPublishHandlerForPlayBack(response, deviceId, channelId, uuid.toString()); |
| 86 | + }, event -> { | |
| 87 | + Response response = event.getResponse(); | |
| 88 | + RequestMessage msg = new RequestMessage(); | |
| 89 | + msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); | |
| 90 | + msg.setData(String.format("回放失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase())); | |
| 91 | + resultHolder.invokeResult(msg); | |
| 81 | 92 | }); |
| 82 | 93 | |
| 83 | 94 | return result; | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/ptz/PtzController.java
| ... | ... | @@ -29,15 +29,14 @@ public class PtzController { |
| 29 | 29 | private IVideoManagerStorager storager; |
| 30 | 30 | |
| 31 | 31 | /*** |
| 32 | - * http://localhost:8080/api/ptz/34020000001320000002_34020000001320000008?leftRight=1&upDown=0&inOut=0&moveSpeed=50&zoomSpeed=0 | |
| 33 | - * @param deviceId | |
| 34 | - * @param channelId | |
| 35 | - * @param leftRight | |
| 36 | - * @param upDown | |
| 37 | - * @param inOut | |
| 38 | - * @param moveSpeed | |
| 39 | - * @param zoomSpeed | |
| 40 | - * @return | |
| 32 | + * 云台控制 | |
| 33 | + * @param deviceId 设备id | |
| 34 | + * @param channelId 通道id | |
| 35 | + * @param cmdCode 指令码 | |
| 36 | + * @param horizonSpeed 水平移动速度 | |
| 37 | + * @param verticalSpeed 垂直移动速度 | |
| 38 | + * @param zoomSpeed 缩放速度 | |
| 39 | + * @return String 控制结果 | |
| 41 | 40 | */ |
| 42 | 41 | @PostMapping("/ptz/{deviceId}/{channelId}") |
| 43 | 42 | public ResponseEntity<String> ptz(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int horizonSpeed, int verticalSpeed, int zoomSpeed){ | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java
| 1 | 1 | package com.genersoft.iot.vmp.vmanager.record; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | |
| 3 | 4 | import org.slf4j.Logger; |
| 4 | 5 | import org.slf4j.LoggerFactory; |
| 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -32,7 +33,7 @@ public class RecordController { |
| 32 | 33 | |
| 33 | 34 | @Autowired |
| 34 | 35 | private DeferredResultHolder resultHolder; |
| 35 | - | |
| 36 | + | |
| 36 | 37 | @GetMapping("/record/{deviceId}/{channelId}") |
| 37 | 38 | public DeferredResult<ResponseEntity<RecordInfo>> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ |
| 38 | 39 | |
| ... | ... | @@ -42,9 +43,17 @@ public class RecordController { |
| 42 | 43 | |
| 43 | 44 | Device device = storager.queryVideoDevice(deviceId); |
| 44 | 45 | cmder.recordInfoQuery(device, channelId, startTime, endTime); |
| 45 | - DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<ResponseEntity<RecordInfo>>(); | |
| 46 | + // 指定超时时间 1分钟30秒 | |
| 47 | + DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<ResponseEntity<RecordInfo>>(90*1000L); | |
| 46 | 48 | // 录像查询以channelId作为deviceId查询 |
| 47 | 49 | resultHolder.put(DeferredResultHolder.CALLBACK_CMD_RECORDINFO+channelId, result); |
| 50 | + result.onTimeout(()->{ | |
| 51 | + RequestMessage msg = new RequestMessage(); | |
| 52 | + msg.setDeviceId(deviceId); | |
| 53 | + msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO); | |
| 54 | + msg.setData("timeout"); | |
| 55 | + resultHolder.invokeResult(msg); | |
| 56 | + }); | |
| 48 | 57 | return result; |
| 49 | 58 | } |
| 50 | 59 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/service/impl/PlayServiceImpl.java
| ... | ... | @@ -4,8 +4,10 @@ import com.alibaba.fastjson.JSON; |
| 4 | 4 | import com.alibaba.fastjson.JSONObject; |
| 5 | 5 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 6 | 6 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 7 | +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | |
| 7 | 8 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 8 | 9 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 10 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 9 | 11 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 10 | 12 | import com.genersoft.iot.vmp.vmanager.play.PlayController; |
| 11 | 13 | import com.genersoft.iot.vmp.vmanager.service.IPlayService; |
| ... | ... | @@ -25,6 +27,9 @@ public class PlayServiceImpl implements IPlayService { |
| 25 | 27 | private IVideoManagerStorager storager; |
| 26 | 28 | |
| 27 | 29 | @Autowired |
| 30 | + private IRedisCatchStorage redisCatchStorage; | |
| 31 | + | |
| 32 | + @Autowired | |
| 28 | 33 | private DeferredResultHolder resultHolder; |
| 29 | 34 | |
| 30 | 35 | @Override |
| ... | ... | @@ -33,7 +38,13 @@ public class PlayServiceImpl implements IPlayService { |
| 33 | 38 | msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
| 34 | 39 | StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); |
| 35 | 40 | if (streamInfo != null) { |
| 36 | - storager.startPlay(streamInfo); | |
| 41 | + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 42 | + if (deviceChannel != null) { | |
| 43 | + deviceChannel.setStreamId(streamInfo.getStreamId()); | |
| 44 | + storager.updateChannel(deviceId, deviceChannel); | |
| 45 | + } | |
| 46 | + | |
| 47 | + redisCatchStorage.startPlay(streamInfo); | |
| 37 | 48 | msg.setData(JSON.toJSONString(streamInfo)); |
| 38 | 49 | resultHolder.invokeResult(msg); |
| 39 | 50 | } else { |
| ... | ... | @@ -49,7 +60,7 @@ public class PlayServiceImpl implements IPlayService { |
| 49 | 60 | msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid); |
| 50 | 61 | StreamInfo streamInfo = onPublishHandler(resonse, deviceId, channelId, uuid); |
| 51 | 62 | if (streamInfo != null) { |
| 52 | - storager.startPlayback(streamInfo); | |
| 63 | + redisCatchStorage.startPlayback(streamInfo); | |
| 53 | 64 | msg.setData(JSON.toJSONString(streamInfo)); |
| 54 | 65 | resultHolder.invokeResult(msg); |
| 55 | 66 | } else { |
| ... | ... | @@ -61,13 +72,11 @@ public class PlayServiceImpl implements IPlayService { |
| 61 | 72 | |
| 62 | 73 | public StreamInfo onPublishHandler(JSONObject resonse, String deviceId, String channelId, String uuid) { |
| 63 | 74 | String streamId = resonse.getString("id"); |
| 64 | - String ssrc = new DecimalFormat("0000000000").format(Integer.parseInt(streamId, 16)); | |
| 65 | 75 | StreamInfo streamInfo = new StreamInfo(); |
| 66 | - streamInfo.setSsrc(ssrc); | |
| 67 | 76 | streamInfo.setStreamId(streamId); |
| 68 | 77 | streamInfo.setDeviceID(deviceId); |
| 69 | 78 | streamInfo.setCahnnelId(channelId); |
| 70 | - MediaServerConfig mediaServerConfig = storager.getMediaInfo(); | |
| 79 | + MediaServerConfig mediaServerConfig = redisCatchStorage.getMediaInfo(); | |
| 71 | 80 | |
| 72 | 81 | streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); |
| 73 | 82 | streamInfo.setWs_flv(String.format("ws://%s:%s/rtp/%s.flv", mediaServerConfig.getWanIp(), mediaServerConfig.getHttpPort(), streamId)); | ... | ... |
src/main/java/com/genersoft/iot/vmp/web/ApiDeviceController.java
| 1 | 1 | package com.genersoft.iot.vmp.web; |
| 2 | 2 | |
| 3 | -import com.alibaba.fastjson.JSON; | |
| 4 | 3 | import com.alibaba.fastjson.JSONArray; |
| 5 | 4 | import com.alibaba.fastjson.JSONObject; |
| 6 | -import com.genersoft.iot.vmp.common.PageResult; | |
| 7 | 5 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 8 | 6 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 9 | 7 | import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; |
| 10 | 8 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 11 | 9 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 12 | 10 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 13 | -import com.genersoft.iot.vmp.vmanager.device.DeviceController; | |
| 11 | +import com.github.pagehelper.PageInfo; | |
| 14 | 12 | import org.slf4j.Logger; |
| 15 | 13 | import org.slf4j.LoggerFactory; |
| 16 | 14 | import org.springframework.beans.factory.annotation.Autowired; |
| 17 | -import org.springframework.http.HttpStatus; | |
| 18 | -import org.springframework.http.ResponseEntity; | |
| 19 | -import org.springframework.stereotype.Controller; | |
| 20 | 15 | import org.springframework.web.bind.annotation.*; |
| 21 | 16 | |
| 22 | 17 | import java.util.List; |
| ... | ... | @@ -65,12 +60,12 @@ public class ApiDeviceController { |
| 65 | 60 | JSONObject result = new JSONObject(); |
| 66 | 61 | List<Device> devices; |
| 67 | 62 | if (start == null || limit ==null) { |
| 68 | - devices = storager.queryVideoDeviceList(null); | |
| 63 | + devices = storager.queryVideoDeviceList(); | |
| 69 | 64 | result.put("DeviceCount", devices.size()); |
| 70 | 65 | }else { |
| 71 | - PageResult<Device> deviceList = storager.queryVideoDeviceList(null, start/limit, limit); | |
| 66 | + PageInfo<Device> deviceList = storager.queryVideoDeviceList(start/limit, limit); | |
| 72 | 67 | result.put("DeviceCount", deviceList.getTotal()); |
| 73 | - devices = deviceList.getData(); | |
| 68 | + devices = deviceList.getList(); | |
| 74 | 69 | } |
| 75 | 70 | |
| 76 | 71 | JSONArray deviceJSONList = new JSONArray(); |
| ... | ... | @@ -86,8 +81,8 @@ public class ApiDeviceController { |
| 86 | 81 | deviceJsonObject.put("Online", device.getOnline() == 1); |
| 87 | 82 | deviceJsonObject.put("Password", ""); |
| 88 | 83 | deviceJsonObject.put("MediaTransport", device.getTransport()); |
| 89 | - deviceJsonObject.put("RemoteIP", device.getHost().getIp()); | |
| 90 | - deviceJsonObject.put("RemotePort", device.getHost().getPort()); | |
| 84 | + deviceJsonObject.put("RemoteIP", device.getIp()); | |
| 85 | + deviceJsonObject.put("RemotePort", device.getPort()); | |
| 91 | 86 | deviceJsonObject.put("LastRegisterAt", ""); |
| 92 | 87 | deviceJsonObject.put("LastKeepaliveAt", ""); |
| 93 | 88 | deviceJsonObject.put("UpdatedAt", ""); |
| ... | ... | @@ -123,9 +118,9 @@ public class ApiDeviceController { |
| 123 | 118 | deviceChannels = storager.queryChannelsByDeviceId(serial); |
| 124 | 119 | result.put("ChannelCount", deviceChannels.size()); |
| 125 | 120 | }else { |
| 126 | - PageResult<DeviceChannel> pageResult = storager.queryChannelsByDeviceId(serial, null, null, null,start/limit, limit); | |
| 121 | + PageInfo<DeviceChannel> pageResult = storager.queryChannelsByDeviceId(serial, null, null, null,start/limit, limit); | |
| 127 | 122 | result.put("ChannelCount", pageResult.getTotal()); |
| 128 | - deviceChannels = pageResult.getData(); | |
| 123 | + deviceChannels = pageResult.getList(); | |
| 129 | 124 | } |
| 130 | 125 | |
| 131 | 126 | JSONArray channleJSONList = new JSONArray(); |
| ... | ... | @@ -159,7 +154,7 @@ public class ApiDeviceController { |
| 159 | 154 | deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, |
| 160 | 155 | // 3 - 固定枪机, 4 - 遥控枪机 |
| 161 | 156 | deviceJOSNChannel.put("CustomPTZType", ""); |
| 162 | - deviceJOSNChannel.put("StreamID", deviceChannel.getSsrc()); // StreamID 直播流ID, 有值表示正在直播 | |
| 157 | + deviceJOSNChannel.put("StreamID", deviceChannel.getStreamId()); // StreamID 直播流ID, 有值表示正在直播 | |
| 163 | 158 | deviceJOSNChannel.put("NumOutputs ", -1); // 直播在线人数 |
| 164 | 159 | channleJSONList.add(deviceJOSNChannel); |
| 165 | 160 | } | ... | ... |