Commit 9a96597e666fa32caefcfff5246b4cb722e9b1bc

Authored by 648540858
2 parents c32ba1de 674ab18c

Merge branch 'wvp-28181-2.0' into main-dev

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
Showing 84 changed files with 2088 additions and 651 deletions

Too many changes to show.

To preserve performance only 84 of 116 files are displayed.

libs/jdbc-x86/bcprov-jdk15on-1.70.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jre6.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/kingbase8-8.6.0.jre7.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jre6.jar 0 → 100644
No preview for this file type
libs/jdbc-x86/postgresql-42.2.9.jre7.jar 0 → 100644
No preview for this file type
... ... @@ -11,7 +11,7 @@
11 11  
12 12 <groupId>com.genersoft</groupId>
13 13 <artifactId>wvp-pro</artifactId>
14   - <version>2.6.9</version>
  14 + <version>2.7.0</version>
15 15 <name>web video platform</name>
16 16 <description>国标28181视频平台</description>
17 17 <packaging>${project.packaging}</packaging>
... ... @@ -143,17 +143,24 @@
143 143 <version>42.5.1</version>
144 144 </dependency>
145 145  
146   - <!-- kingbase人大金仓 -->
147   - <!-- 手动下载驱动后安装 -->
148   - <!-- mvn install:install-file -Dfile=/home/lin/soft/kingbase/jdbc-aarch/kingbase8-8.6.0.jar -DgroupId=com.kingbase -DartifactId=kingbase8
149   - -Dversion=8.6.0 -Dpackaging=jar -->
150   - <dependency>
151   - <groupId>com.kingbase</groupId>
152   - <artifactId>kingbase8</artifactId>
153   - <version>8.6.0</version>
154   - <scope>system</scope>
155   - <systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath>
156   - </dependency>
  146 + <!-- kingbase人大金仓 -->
  147 + <!-- 手动下载驱动后安装 -->
  148 + <!-- mvn install:install-file -Dfile=/home/lin/soft/kingbase/jdbc-aarch/kingbase8-8.6.0.jar -DgroupId=com.kingbase -DartifactId=kingbase8 -Dversion=8.6.0 -Dpackaging=jar
  149 + -->
  150 + <dependency>
  151 + <groupId>com.kingbase</groupId>
  152 + <artifactId>kingbase8</artifactId>
  153 + <version>8.6.0</version>
  154 + <scope>system</scope>
  155 + <systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath>
  156 + </dependency>
  157 + <dependency>
  158 + <groupId>com.kingbase</groupId>
  159 + <artifactId>kingbase8</artifactId>
  160 + <version>8.6.0</version>
  161 + <scope>system</scope>
  162 + <systemPath>${basedir}/libs/jdbc-x86/kingbase8-8.6.0.jar</systemPath>
  163 + </dependency>
157 164  
158 165 <!--Mybatis分页插件 -->
159 166 <dependency>
... ... @@ -163,21 +170,16 @@
163 170 </dependency>
164 171  
165 172 <!--在线文档 -->
  173 + <!--在线文档 -->
166 174 <dependency>
167 175 <groupId>org.springdoc</groupId>
168 176 <artifactId>springdoc-openapi-ui</artifactId>
169   - <version>1.7.0</version>
170   - <exclusions>
171   - <exclusion>
172   - <groupId>org.yaml</groupId>
173   - <artifactId>snakeyaml</artifactId>
174   - </exclusion>
175   - </exclusions>
  177 + <version>1.6.10</version>
176 178 </dependency>
177 179 <dependency>
178   - <groupId>org.yaml</groupId>
179   - <artifactId>snakeyaml</artifactId>
180   - <version>2.2</version>
  180 + <groupId>org.springdoc</groupId>
  181 + <artifactId>springdoc-openapi-security</artifactId>
  182 + <version>1.6.10</version>
181 183 </dependency>
182 184  
183 185 <dependency>
... ...
sql/2.6.9更新.sql deleted 100644 → 0
1   -alter table wvp_device_channel
2   - change stream_id stream_id varying(255)
3   -
4   -alter table wvp_platform
5   - add auto_push_channel bool default false
6   -
7   -alter table wvp_stream_proxy
8   - add stream_key character varying(255)
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
1 1 package com.genersoft.iot.vmp.common;
2 2  
  3 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5  
5 6 import java.io.Serializable;
... ... @@ -76,6 +77,8 @@ public class StreamInfo implements Serializable, Cloneable{
76 77 private String endTime;
77 78 @Schema(description = "进度(录像下载使用)")
78 79 private double progress;
  80 + @Schema(description = "文件下载地址(录像下载使用)")
  81 + private DownloadFileInfo downLoadFilePath;
79 82  
80 83 @Schema(description = "是否暂停(录像回放使用)")
81 84 private boolean pause;
... ... @@ -605,5 +608,11 @@ public class StreamInfo implements Serializable, Cloneable{
605 608 this.subStream = subStream;
606 609 }
607 610  
  611 + public DownloadFileInfo getDownLoadFilePath() {
  612 + return downLoadFilePath;
  613 + }
608 614  
  615 + public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
  616 + this.downLoadFilePath = downLoadFilePath;
  617 + }
609 618 }
... ...
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
... ... @@ -53,7 +53,7 @@ public class VideoManagerConstants {
53 53  
54 54 public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_";
55 55  
56   - public static final String MEDIA_STREAM_AUTHORITY = "MEDIA_STREAM_AUTHORITY_";
  56 + public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_";
57 57  
58 58 public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_";
59 59  
... ... @@ -71,6 +71,7 @@ public class VideoManagerConstants {
71 71 public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_";
72 72  
73 73 public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_";
  74 + public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
74 75  
75 76  
76 77  
... ...
src/main/java/com/genersoft/iot/vmp/conf/CloudRecordTimer.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
  6 +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  7 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  8 +import com.genersoft.iot.vmp.service.IMediaServerService;
  9 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  10 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  11 +import com.genersoft.iot.vmp.vmanager.cloudRecord.CloudRecordController;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +import org.springframework.beans.factory.annotation.Autowired;
  15 +import org.springframework.scheduling.annotation.Scheduled;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import java.io.File;
  19 +import java.util.ArrayList;
  20 +import java.util.Calendar;
  21 +import java.util.Date;
  22 +import java.util.List;
  23 +
  24 +/**
  25 + * 录像文件定时删除
  26 + */
  27 +@Component
  28 +public class CloudRecordTimer {
  29 +
  30 + private final static Logger logger = LoggerFactory.getLogger(CloudRecordTimer.class);
  31 +
  32 + @Autowired
  33 + private IMediaServerService mediaServerService;
  34 +
  35 + @Autowired
  36 + private CloudRecordServiceMapper cloudRecordServiceMapper;
  37 +
  38 + @Autowired
  39 + private ZLMRESTfulUtils zlmresTfulUtils;
  40 +
  41 + /**
  42 + * 定时查询待删除的录像文件
  43 + */
  44 +// @Scheduled(fixedRate = 10000) //每五秒执行一次,方便测试
  45 + @Scheduled(cron = "0 0 0 * * ?") //每天的0点执行
  46 + public void execute(){
  47 + logger.info("[录像文件定时清理] 开始清理过期录像文件");
  48 + // 获取配置了assist的流媒体节点
  49 + List<MediaServerItem> mediaServerItemList = mediaServerService.getAllOnline();
  50 + if (mediaServerItemList.isEmpty()) {
  51 + return;
  52 + }
  53 + long result = 0;
  54 + for (MediaServerItem mediaServerItem : mediaServerItemList) {
  55 +
  56 + Calendar lastCalendar = Calendar.getInstance();
  57 + if (mediaServerItem.getRecordDay() > 0) {
  58 + lastCalendar.setTime(new Date());
  59 + // 获取保存的最后截至日[期,因为每个节点都有一个日期,也就是支持每个节点设置不同的保存日期,
  60 + lastCalendar.add(Calendar.DAY_OF_MONTH, -mediaServerItem.getRecordDay());
  61 + Long lastDate = lastCalendar.getTimeInMillis();
  62 +
  63 + // 获取到截至日期之前的录像文件列表,文件列表满足未被收藏和保持的。这两个字段目前共能一致,
  64 + // 为我自己业务系统相关的代码,大家使用的时候直接使用收藏(collect)这一个类型即可
  65 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordListForDelete(lastDate, mediaServerItem.getId());
  66 + if (cloudRecordItemList.isEmpty()) {
  67 + continue;
  68 + }
  69 + // TODO 后续可以删除空了的过期日期文件夹
  70 + for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
  71 + String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
  72 + JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
  73 + cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
  74 + if (jsonObject.getInteger("code") != 0) {
  75 + logger.warn("[录像文件定时清理] 删除磁盘文件错误: {}:{}", cloudRecordItem.getFilePath(), jsonObject);
  76 + }
  77 + }
  78 + result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
  79 + }
  80 + }
  81 + logger.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
  82 + }
  83 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
... ... @@ -81,6 +81,12 @@ public class MediaConfig{
81 81 @Value("${media.record-assist-port:0}")
82 82 private Integer recordAssistPort = 0;
83 83  
  84 + @Value("${media.record-day:7}")
  85 + private Integer recordDay;
  86 +
  87 + @Value("${media.record-path:}")
  88 + private String recordPath;
  89 +
84 90 public String getId() {
85 91 return id;
86 92 }
... ... @@ -212,13 +218,32 @@ public class MediaConfig{
212 218 mediaServerItem.setSendRtpPortRange(rtpSendPortRange);
213 219 mediaServerItem.setRecordAssistPort(recordAssistPort);
214 220 mediaServerItem.setHookAliveInterval(30.00f);
215   -
  221 + mediaServerItem.setRecordDay(recordDay);
  222 + if (recordPath != null) {
  223 + mediaServerItem.setRecordPath(recordPath);
  224 + }
216 225 mediaServerItem.setCreateTime(DateUtil.getNow());
217 226 mediaServerItem.setUpdateTime(DateUtil.getNow());
218 227  
219 228 return mediaServerItem;
220 229 }
221 230  
  231 + public Integer getRecordDay() {
  232 + return recordDay;
  233 + }
  234 +
  235 + public void setRecordDay(Integer recordDay) {
  236 + this.recordDay = recordDay;
  237 + }
  238 +
  239 + public String getRecordPath() {
  240 + return recordPath;
  241 + }
  242 +
  243 + public void setRecordPath(String recordPath) {
  244 + this.recordPath = recordPath;
  245 + }
  246 +
222 247 public String getRtpSendPortRange() {
223 248 return rtpSendPortRange;
224 249 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java
1 1 package com.genersoft.iot.vmp.conf;
2 2  
  3 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
  4 +import io.swagger.v3.oas.models.Components;
3 5 import io.swagger.v3.oas.models.OpenAPI;
4 6 import io.swagger.v3.oas.models.info.Contact;
5 7 import io.swagger.v3.oas.models.info.Info;
6 8 import io.swagger.v3.oas.models.info.License;
  9 +import io.swagger.v3.oas.models.security.SecurityScheme;
7 10 import org.springframework.core.annotation.Order;
8 11 import org.springdoc.core.GroupedOpenApi;
9 12 import org.springframework.beans.factory.annotation.Value;
... ... @@ -26,10 +29,14 @@ public class SpringDocConfig {
26 29 contact.setName("pan");
27 30 contact.setEmail("648540858@qq.com");
28 31 return new OpenAPI()
  32 + .components(new Components()
  33 + .addSecuritySchemes(JwtUtils.HEADER, new SecurityScheme()
  34 + .type(SecurityScheme.Type.HTTP)
  35 + .bearerFormat("JWT")))
29 36 .info(new Info().title("WVP-PRO 接口文档")
30 37 .contact(contact)
31 38 .description("开箱即用的28181协议视频平台")
32   - .version("v2.0")
  39 + .version("v3.1.0")
33 40 .license(new License().name("Apache 2.0").url("http://springdoc.org")));
34 41 }
35 42  
... ...
src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java
... ... @@ -39,4 +39,6 @@ public class SystemInfoTimerTask {
39 39 }
40 40  
41 41 }
  42 +
  43 +
42 44 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -56,8 +56,6 @@ public class UserSetting {
56 56  
57 57 private String serverId = "000000";
58 58  
59   - private String recordPath = null;
60   -
61 59 private String thirdPartyGBIdReg = "[\\s\\S]*";
62 60  
63 61 private String broadcastForPlatform = "UDP";
... ... @@ -262,14 +260,6 @@ public class UserSetting {
262 260 this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
263 261 }
264 262  
265   - public String getRecordPath() {
266   - return recordPath;
267   - }
268   -
269   - public void setRecordPath(String recordPath) {
270   - this.recordPath = recordPath;
271   - }
272   -
273 263 public int getMaxNotifyCountQueue() {
274 264 return maxNotifyCountQueue;
275 265 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
... ... @@ -28,7 +28,7 @@ public class JwtUtils implements InitializingBean {
28 28  
29 29 private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
30 30  
31   - private static final String HEADER = "access-token";
  31 + public static final String HEADER = "access-token";
32 32  
33 33 private static final String AUDIENCE = "Audience";
34 34  
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
... ... @@ -68,6 +68,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
68 68 matchers.add("/");
69 69 matchers.add("/#/**");
70 70 matchers.add("/static/**");
  71 + matchers.add("/swagger-ui.html");
  72 + matchers.add("/swagger-ui/");
71 73 matchers.add("/index.html");
72 74 matchers.add("/doc.html");
73 75 matchers.add("/webjars/**");
... ... @@ -77,7 +79,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
77 79 matchers.add("/api/device/query/snap/**");
78 80 matchers.add("/record_proxy/*/**");
79 81 matchers.add("/api/emit");
80   - matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
  82 + matchers.add("/favicon.ico");
81 83 // 可以直接访问的静态数据
82 84 web.ignoring().antMatchers(matchers.toArray(new String[0]));
83 85 }
... ... @@ -114,7 +116,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
114 116 .authorizeRequests()
115 117 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
116 118 .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
117   - .antMatchers("/api/user/login", "/index/hook/**").permitAll()
  119 + .antMatchers("/api/user/login", "/index/hook/**", "/swagger-ui/**", "/doc.html").permitAll()
118 120 .anyRequest().authenticated()
119 121 // 异常处理器
120 122 .and()
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
... ... @@ -148,13 +148,13 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
148 148 if (event.getDeviceChannels() != null) {
149 149 deviceChannelList.addAll(event.getDeviceChannels());
150 150 }
151   - if (event.getGbStreams() != null && event.getGbStreams().size() > 0){
  151 + if (event.getGbStreams() != null && !event.getGbStreams().isEmpty()){
152 152 for (GbStream gbStream : event.getGbStreams()) {
153 153 deviceChannelList.add(
154 154 gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform));
155 155 }
156 156 }
157   - if (deviceChannelList.size() > 0) {
  157 + if (!deviceChannelList.isEmpty()) {
158 158 logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size());
159 159 try {
160 160 sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null);
... ... @@ -163,10 +163,10 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
163 163 logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
164 164 }
165 165 }
166   - }else if (parentPlatformMap.keySet().size() > 0) {
  166 + }else if (!parentPlatformMap.keySet().isEmpty()) {
167 167 for (String gbId : parentPlatformMap.keySet()) {
168 168 List<ParentPlatform> parentPlatforms = parentPlatformMap.get(gbId);
169   - if (parentPlatforms != null && parentPlatforms.size() > 0) {
  169 + if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
170 170 for (ParentPlatform platform : parentPlatforms) {
171 171 SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
172 172 if (subscribeInfo == null) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
... ... @@ -75,6 +75,33 @@ public class VideoStreamSessionManager {
75 75 return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
76 76 }
77 77  
  78 + public SsrcTransaction getSsrcTransactionByCallId(String callId){
  79 +
  80 + if (ObjectUtils.isEmpty(callId)) {
  81 + return null;
  82 + }
  83 + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_" + callId+ "_*";
  84 + List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
  85 + if (!scanResult.isEmpty()) {
  86 + return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0));
  87 + }else {
  88 + key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_*_*_play_*";
  89 + scanResult = RedisUtil.scan(redisTemplate, key);
  90 + if (scanResult.isEmpty()) {
  91 + return null;
  92 + }
  93 + for (Object keyObj : scanResult) {
  94 + SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj);
  95 + if (ssrcTransaction.getSipTransactionInfo() != null &&
  96 + ssrcTransaction.getSipTransactionInfo().getCallId().equals(callId)) {
  97 + return ssrcTransaction;
  98 + }
  99 + }
  100 + return null;
  101 + }
  102 +
  103 + }
  104 +
78 105 public List<SsrcTransaction> getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){
79 106 if (ObjectUtils.isEmpty(deviceId)) {
80 107 deviceId ="*";
... ... @@ -117,8 +144,19 @@ public class VideoStreamSessionManager {
117 144 }
118 145  
119 146 public void remove(String deviceId, String channelId, String stream) {
120   - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream);
121   - if (ssrcTransaction == null) {
  147 + List<SsrcTransaction> ssrcTransactionList = getSsrcTransactionForAll(deviceId, channelId, null, stream);
  148 + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
  149 + return;
  150 + }
  151 + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
  152 + redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"
  153 + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream());
  154 + }
  155 + }
  156 +
  157 + public void removeByCallId(String deviceId, String channelId, String callId) {
  158 + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, callId, null);
  159 + if (ssrcTransaction == null ) {
122 160 return;
123 161 }
124 162 redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_"
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
... ... @@ -129,4 +129,6 @@ public class SipRunner implements CommandLineRunner {
129 129 }
130 130 }
131 131 }
  132 +
  133 +
132 134 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
... ... @@ -164,6 +164,7 @@ public class SIPRequestHeaderProvider {
164 164 Request request = null;
165 165 //请求行
166 166 SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress());
  167 +// SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
167 168 // via
168 169 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
169 170 ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
... ... @@ -174,6 +175,7 @@ public class SIPRequestHeaderProvider {
174 175 FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
175 176 //to
176 177 SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress());
  178 +// SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(),device.getHostAddress());
177 179 Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
178 180 ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag());
179 181  
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -40,6 +40,8 @@ import javax.sip.SipFactory;
40 40 import javax.sip.header.CallIdHeader;
41 41 import javax.sip.message.Request;
42 42 import java.text.ParseException;
  43 +import java.util.ArrayList;
  44 +import java.util.List;
43 45  
44 46 /**
45 47 * @description:设备能力接口,用于定义设备的控制、查询能力
... ... @@ -677,22 +679,21 @@ public class SIPCommander implements ISIPCommander {
677 679 */
678 680 @Override
679 681 public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException {
680   - SsrcTransaction ssrcTransaction;
681   - if (callId != null) {
682   - ssrcTransaction = streamSession.getSsrcTransaction(null, null, callId, null);
683   - }else {
684   - ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, stream);
685   - }
686   - if (ssrcTransaction == null) {
  682 + List<SsrcTransaction> ssrcTransactionList = streamSession.getSsrcTransactionForAll(device.getDeviceId(), channelId, callId, stream);
  683 + if (ssrcTransactionList == null || ssrcTransactionList.isEmpty()) {
  684 + logger.info("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceId(), channelId);
687 685 throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream);
688 686 }
689 687  
690   - mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
691   - mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
692   - streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  688 + for (SsrcTransaction ssrcTransaction : ssrcTransactionList) {
  689 + logger.info("[发送BYE] 设备: device: {}, channel: {}, callId: {}", device.getDeviceId(), channelId, ssrcTransaction.getCallId());
  690 + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
693 691  
694   - Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
695   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  692 + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
  693 + streamSession.removeByCallId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
  694 + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
  695 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
  696 + }
696 697 }
697 698  
698 699 @Override
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
... ... @@ -579,7 +579,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
579 579  
580 580 @Override
581 581 public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List<DeviceChannel> deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException {
582   - if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) {
  582 + if (parentPlatform == null || deviceChannels == null || deviceChannels.isEmpty() || subscribeInfo == null) {
583 583 return;
584 584 }
585 585 if (index == null) {
... ... @@ -597,6 +597,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
597 597 Integer finalIndex = index;
598 598 String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels,
599 599 deviceChannels.size(), type, subscribeInfo);
  600 + logger.info("[发送NOTIFY通知]类型: {},发送数量: {}", type, channels.size());
600 601 sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> {
601 602 logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg);
602 603 }, (eventResult -> {
... ... @@ -620,7 +621,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
620 621  
621 622 SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
622 623  
623   - sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest);
  624 + sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent);
624 625 }
625 626  
626 627 private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
... ... @@ -632,9 +633,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
632 633 .append("<CmdType>Catalog</CmdType>\r\n")
633 634 .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
634 635 .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
635   - .append("<SumNum>1</SumNum>\r\n")
  636 + .append("<SumNum>"+ sumNum +"</SumNum>\r\n")
636 637 .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
637   - if (channels.size() > 0) {
  638 + if (!channels.isEmpty()) {
638 639 for (DeviceChannel channel : channels) {
639 640 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
640 641 channel.setParentId(parentPlatform.getDeviceGBId());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
... ... @@ -33,6 +33,7 @@ import javax.sip.header.CallIdHeader;
33 33 import javax.sip.message.Response;
34 34 import java.text.ParseException;
35 35 import java.util.HashMap;
  36 +import java.util.List;
36 37 import java.util.Map;
37 38  
38 39 /**
... ... @@ -167,14 +168,12 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
167 168 }
168 169 }
169 170  
170   - // 发流端发送的停止
171   - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
172   - if (ssrcTransaction == null ) {
173   - logger.info("[收到bye] 但是无法获取推流信息和发流信息,忽略此请求");
174   - logger.info(request.toString());
175   - return;
176   - }
177   -
  171 + // 可能是设备发送的停止
  172 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransactionByCallId(callIdHeader.getCallId());
  173 + if (ssrcTransaction == null) {
  174 + return;
  175 + }
  176 + logger.info("[收到bye] 来自设备:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
178 177  
179 178 ParentPlatform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId());
180 179 if (platform != null ) {
... ... @@ -216,7 +215,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
216 215 if (mediaServerItem != null) {
217 216 mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransaction.getSsrc());
218 217 }
219   - streamSession.remove(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getStream());
  218 + streamSession.removeByCallId(device.getDeviceId(), channel.getChannelId(), ssrcTransaction.getCallId());
220 219 if (ssrcTransaction.getType() == InviteSessionType.BROADCAST) {
221 220 // 查找来源的对讲设备,发送停止
222 221 Device sourceDevice = storager.queryVideoDeviceByPlatformIdAndChannelId(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
... ... @@ -152,7 +152,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
152 152 String requesterId = SipUtils.getUserIdFromFromHeader(request);
153 153 CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
154 154 if (requesterId == null || channelId == null) {
155   - logger.info("无法从FromHeader的Address中获取到平台id,返回400");
  155 + logger.info("无法从请求中获取到平台id,返回400");
156 156 // 参数不全, 发400,请求错误
157 157 try {
158 158 responseAck(request, Response.BAD_REQUEST);
... ... @@ -745,13 +745,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
745 745 dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
746 746 logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
747 747 try {
  748 + redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
748 749 mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
749 750 responseAck(request, Response.REQUEST_TIMEOUT); // 超时
750   - } catch (SipException e) {
751   - logger.error("未处理的异常 ", e);
752   - } catch (InvalidArgumentException e) {
753   - logger.error("未处理的异常 ", e);
754   - } catch (ParseException e) {
  751 + } catch (SipException | InvalidArgumentException | ParseException e) {
755 752 logger.error("未处理的异常 ", e);
756 753 }
757 754 }, userSetting.getPlatformPlayTimeout());
... ... @@ -762,6 +759,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
762 759 // 添加在本机上线的通知
763 760 mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> {
764 761 dynamicTask.stop(callIdHeader.getCallId());
  762 + redisPushStreamResponseListener.removeEvent(gbStream.getApp(), gbStream.getStream());
765 763 if (serverId.equals(userSetting.getServerId())) {
766 764 SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId,
767 765 app, stream, channelId, mediaTransmissionTCP, platform.isRtcp());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java
... ... @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
13 13 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
14 14 import com.genersoft.iot.vmp.service.IDeviceChannelService;
15 15 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  16 +import com.genersoft.iot.vmp.utils.DateUtil;
16 17 import org.dom4j.DocumentException;
17 18 import org.dom4j.Element;
18 19 import org.slf4j.Logger;
... ... @@ -185,6 +186,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
185 186 // 判断此通道是否存在
186 187 DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
187 188 if (deviceChannel != null) {
  189 + logger.info("[增加通道] 已存在,不发送通知只更新,设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
188 190 channel.setId(deviceChannel.getId());
189 191 updateChannelMap.put(channel.getChannelId(), channel);
190 192 if (updateChannelMap.keySet().size() > 300) {
... ... @@ -222,6 +224,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
222 224 DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
223 225 if (deviceChannelForUpdate != null) {
224 226 channel.setId(deviceChannelForUpdate.getId());
  227 + channel.setUpdateTime(DateUtil.getNow());
225 228 updateChannelMap.put(channel.getChannelId(), channel);
226 229 if (updateChannelMap.keySet().size() > 300) {
227 230 executeSaveForUpdate();
... ... @@ -244,11 +247,11 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
244 247 // 转发变化信息
245 248 eventPublisher.catalogEventPublish(null, channel, event);
246 249  
247   - if (updateChannelMap.keySet().size() > 0
248   - || addChannelMap.keySet().size() > 0
249   - || updateChannelOnlineList.size() > 0
250   - || updateChannelOfflineList.size() > 0
251   - || deleteChannelList.size() > 0) {
  250 + if (!updateChannelMap.keySet().isEmpty()
  251 + || !addChannelMap.keySet().isEmpty()
  252 + || !updateChannelOnlineList.isEmpty()
  253 + || !updateChannelOfflineList.isEmpty()
  254 + || !deleteChannelList.isEmpty()) {
252 255  
253 256 if (!dynamicTask.contains(talkKey)) {
254 257 dynamicTask.startDelay(talkKey, this::executeSave, 1000);
... ... @@ -262,16 +265,36 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
262 265 }
263 266  
264 267 private void executeSave(){
265   - executeSaveForAdd();
266   - executeSaveForUpdate();
267   - executeSaveForDelete();
268   - executeSaveForOnline();
269   - executeSaveForOffline();
  268 + try {
  269 + executeSaveForAdd();
  270 + } catch (Exception e) {
  271 + logger.error("[存储收到的增加通道] 异常: ", e );
  272 + }
  273 + try {
  274 + executeSaveForUpdate();
  275 + } catch (Exception e) {
  276 + logger.error("[存储收到的更新通道] 异常: ", e );
  277 + }
  278 + try {
  279 + executeSaveForDelete();
  280 + } catch (Exception e) {
  281 + logger.error("[存储收到的删除通道] 异常: ", e );
  282 + }
  283 + try {
  284 + executeSaveForOnline();
  285 + } catch (Exception e) {
  286 + logger.error("[存储收到的通道上线] 异常: ", e );
  287 + }
  288 + try {
  289 + executeSaveForOffline();
  290 + } catch (Exception e) {
  291 + logger.error("[存储收到的通道离线] 异常: ", e );
  292 + }
270 293 dynamicTask.stop(talkKey);
271 294 }
272 295  
273 296 private void executeSaveForUpdate(){
274   - if (updateChannelMap.values().size() > 0) {
  297 + if (!updateChannelMap.values().isEmpty()) {
275 298 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
276 299 updateChannelMap.clear();
277 300 deviceChannelService.batchUpdateChannel(deviceChannels);
... ... @@ -280,7 +303,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
280 303 }
281 304  
282 305 private void executeSaveForAdd(){
283   - if (addChannelMap.values().size() > 0) {
  306 + if (!addChannelMap.values().isEmpty()) {
284 307 ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
285 308 addChannelMap.clear();
286 309 deviceChannelService.batchAddChannel(deviceChannels);
... ... @@ -288,21 +311,21 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
288 311 }
289 312  
290 313 private void executeSaveForDelete(){
291   - if (deleteChannelList.size() > 0) {
  314 + if (!deleteChannelList.isEmpty()) {
292 315 deviceChannelService.deleteChannels(deleteChannelList);
293 316 deleteChannelList.clear();
294 317 }
295 318 }
296 319  
297 320 private void executeSaveForOnline(){
298   - if (updateChannelOnlineList.size() > 0) {
  321 + if (!updateChannelOnlineList.isEmpty()) {
299 322 deviceChannelService.channelsOnline(updateChannelOnlineList);
300 323 updateChannelOnlineList.clear();
301 324 }
302 325 }
303 326  
304 327 private void executeSaveForOffline(){
305   - if (updateChannelOfflineList.size() > 0) {
  328 + if (!updateChannelOfflineList.isEmpty()) {
306 329 deviceChannelService.channelsOffline(updateChannelOfflineList);
307 330 updateChannelOfflineList.clear();
308 331 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
... ... @@ -76,7 +76,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
76 76  
77 77 RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
78 78 if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
79   - logger.info("[心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
  79 + logger.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort());
80 80 device.setPort(remoteAddressInfo.getPort());
81 81 device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
82 82 device.setIp(remoteAddressInfo.getIp());
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
... ... @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device;
8 8 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
9 9 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
10 10 import com.genersoft.iot.vmp.utils.DateUtil;
  11 +import org.apache.commons.lang3.StringUtils;
11 12 import org.apache.commons.lang3.math.NumberUtils;
12 13 import org.dom4j.Attribute;
13 14 import org.dom4j.Document;
... ... @@ -214,8 +215,11 @@ public class XmlUtil {
214 215 return deviceChannel;
215 216 }
216 217 Element nameElement = itemDevice.element("Name");
217   - if (nameElement != null) {
  218 + // 当通道名称为空时,设置通道名称为通道编码,避免级联时因通道名称为空导致上级接收通道失败
  219 + if (nameElement != null && StringUtils.isNotBlank(nameElement.getText())) {
218 220 deviceChannel.setName(nameElement.getText());
  221 + } else {
  222 + deviceChannel.setName(channelId);
219 223 }
220 224 if(channelId.length() <= 8) {
221 225 deviceChannel.setHasAudio(false);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
... ... @@ -9,33 +9,58 @@ import org.jetbrains.annotations.NotNull;
9 9 import org.slf4j.Logger;
10 10 import org.slf4j.LoggerFactory;
11 11 import org.springframework.stereotype.Component;
  12 +import org.springframework.util.ObjectUtils;
12 13  
13 14 import java.io.IOException;
14 15 import java.net.ConnectException;
  16 +import java.net.SocketTimeoutException;
15 17 import java.util.HashMap;
  18 +import java.util.List;
16 19 import java.util.Map;
17 20 import java.util.Objects;
  21 +import java.util.concurrent.TimeUnit;
18 22  
19 23 @Component
20 24 public class AssistRESTfulUtils {
21 25  
22 26 private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class);
23 27  
  28 +
  29 + private OkHttpClient client;
  30 +
  31 +
24 32 public interface RequestCallback{
25 33 void run(JSONObject response);
26 34 }
27 35  
28 36 private OkHttpClient getClient(){
29   - OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
30   - if (logger.isDebugEnabled()) {
31   - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
32   - logger.debug("http请求参数:" + message);
33   - });
34   - logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
35   - // OkHttp進行添加攔截器loggingInterceptor
36   - httpClientBuilder.addInterceptor(logging);
  37 + return getClient(null);
  38 + }
  39 +
  40 + private OkHttpClient getClient(Integer readTimeOut){
  41 + if (client == null) {
  42 + if (readTimeOut == null) {
  43 + readTimeOut = 10;
  44 + }
  45 + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
  46 + // 设置连接超时时间
  47 + httpClientBuilder.connectTimeout(8, TimeUnit.SECONDS);
  48 + // 设置读取超时时间
  49 + httpClientBuilder.readTimeout(readTimeOut,TimeUnit.SECONDS);
  50 + // 设置连接池
  51 + httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
  52 + if (logger.isDebugEnabled()) {
  53 + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
  54 + logger.debug("http请求参数:" + message);
  55 + });
  56 + logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
  57 + // OkHttp進行添加攔截器loggingInterceptor
  58 + httpClientBuilder.addInterceptor(logging);
  59 + }
  60 + client = httpClientBuilder.build();
37 61 }
38   - return httpClientBuilder.build();
  62 + return client;
  63 +
39 64 }
40 65  
41 66  
... ... @@ -123,13 +148,91 @@ public class AssistRESTfulUtils {
123 148 return responseJSON;
124 149 }
125 150  
  151 + public JSONObject sendPost(MediaServerItem mediaServerItem, String api, JSONObject param, ZLMRESTfulUtils.RequestCallback callback, Integer readTimeOut) {
  152 + OkHttpClient client = getClient(readTimeOut);
126 153  
127   - public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){
128   - Map<String, Object> param = new HashMap<>();
129   - param.put("app",app);
130   - param.put("stream",stream);
131   - param.put("recordIng",true);
132   - return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
  154 + if (mediaServerItem == null) {
  155 + return null;
  156 + }
  157 + String url = String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api);
  158 + JSONObject responseJSON = new JSONObject();
  159 + //-2自定义流媒体 调用错误码
  160 + responseJSON.put("code",-2);
  161 + responseJSON.put("msg","ASSIST调用失败");
  162 +
  163 + RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param.toString());
  164 +
  165 + Request request = new Request.Builder()
  166 + .post(requestBodyJson)
  167 + .url(url)
  168 + .addHeader("Content-Type", "application/json")
  169 + .build();
  170 + if (callback == null) {
  171 + try {
  172 + Response response = client.newCall(request).execute();
  173 + if (response.isSuccessful()) {
  174 + ResponseBody responseBody = response.body();
  175 + if (responseBody != null) {
  176 + String responseStr = responseBody.string();
  177 + responseJSON = JSON.parseObject(responseStr);
  178 + }
  179 + }else {
  180 + response.close();
  181 + Objects.requireNonNull(response.body()).close();
  182 + }
  183 + }catch (IOException e) {
  184 + logger.error(String.format("[ %s ]ASSIST请求失败: %s", url, e.getMessage()));
  185 +
  186 + if(e instanceof SocketTimeoutException){
  187 + //读取超时超时异常
  188 + logger.error(String.format("读取ASSIST数据失败: %s, %s", url, e.getMessage()));
  189 + }
  190 + if(e instanceof ConnectException){
  191 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  192 + logger.error(String.format("连接ASSIST失败: %s, %s", url, e.getMessage()));
  193 + }
  194 +
  195 + }catch (Exception e){
  196 + logger.error(String.format("访问ASSIST失败: %s, %s", url, e.getMessage()));
  197 + }
  198 + }else {
  199 + client.newCall(request).enqueue(new Callback(){
  200 +
  201 + @Override
  202 + public void onResponse(@NotNull Call call, @NotNull Response response){
  203 + if (response.isSuccessful()) {
  204 + try {
  205 + String responseStr = Objects.requireNonNull(response.body()).string();
  206 + callback.run(JSON.parseObject(responseStr));
  207 + } catch (IOException e) {
  208 + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
  209 + }
  210 +
  211 + }else {
  212 + response.close();
  213 + Objects.requireNonNull(response.body()).close();
  214 + }
  215 + }
  216 +
  217 + @Override
  218 + public void onFailure(@NotNull Call call, @NotNull IOException e) {
  219 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  220 +
  221 + if(e instanceof SocketTimeoutException){
  222 + //读取超时超时异常
  223 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage()));
  224 + }
  225 + if(e instanceof ConnectException){
  226 + //判断连接异常,我这里是报Failed to connect to 10.7.5.144
  227 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage()));
  228 + }
  229 + }
  230 + });
  231 + }
  232 +
  233 +
  234 +
  235 + return responseJSON;
133 236 }
134 237  
135 238 public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
... ... @@ -137,33 +240,41 @@ public class AssistRESTfulUtils {
137 240 return sendGet(mediaServerItem, "api/record/info",param, callback);
138 241 }
139 242  
140   - public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
141   - Map<String, Object> param = new HashMap<>();
142   - param.put("app",app);
143   - param.put("stream",stream);
144   - param.put("callId",callId);
145   - return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback);
146   - }
  243 + public JSONObject addTask(MediaServerItem mediaServerItem, String app, String stream, String startTime,
  244 + String endTime, String callId, List<String> filePathList, String remoteHost) {
147 245  
148   - public JSONObject getDateList(MediaServerItem mediaServerItem, String app, String stream, int year, int month) {
149   - Map<String, Object> param = new HashMap<>();
150   - param.put("app", app);
151   - param.put("stream", stream);
152   - param.put("year", year);
153   - param.put("month", month);
154   - return sendGet(mediaServerItem, "api/record/date/list", param, null);
  246 + JSONObject videoTaskInfoJSON = new JSONObject();
  247 + videoTaskInfoJSON.put("app", app);
  248 + videoTaskInfoJSON.put("stream", stream);
  249 + videoTaskInfoJSON.put("startTime", startTime);
  250 + videoTaskInfoJSON.put("endTime", endTime);
  251 + videoTaskInfoJSON.put("callId", callId);
  252 + videoTaskInfoJSON.put("filePathList", filePathList);
  253 + if (!ObjectUtils.isEmpty(remoteHost)) {
  254 + videoTaskInfoJSON.put("remoteHost", remoteHost);
  255 + }
  256 +
  257 + return sendPost(mediaServerItem, "api/record/file/download/task/add", videoTaskInfoJSON, null, 30);
155 258 }
156 259  
157   - public JSONObject getFileList(MediaServerItem mediaServerItem, int page, int count, String app, String stream,
158   - String startTime, String endTime) {
  260 + public JSONObject queryTaskList(MediaServerItem mediaServerItem, String app, String stream, String callId, String taskId, Boolean isEnd) {
159 261 Map<String, Object> param = new HashMap<>();
160   - param.put("app", app);
161   - param.put("stream", stream);
162   - param.put("page", page);
163   - param.put("count", count);
164   - param.put("startTime", startTime);
165   - param.put("endTime", endTime);
166   - return sendGet(mediaServerItem, "api/record/file/listWithDate", param, null);
167   - }
  262 + if (!ObjectUtils.isEmpty(app)) {
  263 + param.put("app", app);
  264 + }
  265 + if (!ObjectUtils.isEmpty(stream)) {
  266 + param.put("stream", stream);
  267 + }
  268 + if (!ObjectUtils.isEmpty(callId)) {
  269 + param.put("callId", callId);
  270 + }
  271 + if (!ObjectUtils.isEmpty(taskId)) {
  272 + param.put("taskId", taskId);
  273 + }
  274 + if (!ObjectUtils.isEmpty(isEnd)) {
  275 + param.put("isEnd", isEnd);
  276 + }
168 277  
  278 + return sendGet(mediaServerItem, "api/record/file/download/task/list", param, null);
  279 + }
169 280 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -118,6 +118,9 @@ public class ZLMHttpHookListener {
118 118 private IUserService userService;
119 119  
120 120 @Autowired
  121 + private ICloudRecordService cloudRecordService;
  122 +
  123 + @Autowired
121 124 private VideoStreamSessionManager sessionManager;
122 125  
123 126 @Autowired
... ... @@ -238,12 +241,6 @@ public class ZLMHttpHookListener {
238 241 streamAuthorityInfo.setSign(sign);
239 242 // 鉴权通过
240 243 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
241   - // 通知assist新的callId
242   - if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
243   - taskExecutor.execute(() -> {
244   - assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
245   - });
246   - }
247 244 }
248 245 } else {
249 246 zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
... ... @@ -251,6 +248,7 @@ public class ZLMHttpHookListener {
251 248  
252 249  
253 250 HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
  251 + result.setEnable_audio(true);
254 252 taskExecutor.execute(() -> {
255 253 ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
256 254 if (subscribe != null) {
... ... @@ -268,7 +266,6 @@ public class ZLMHttpHookListener {
268 266 } else {
269 267 result.setEnable_mp4(userSetting.isRecordPushLive());
270 268 }
271   -
272 269 // 国标流
273 270 if ("rtp".equals(param.getApp()) ) {
274 271  
... ... @@ -278,14 +275,24 @@ public class ZLMHttpHookListener {
278 275 if (!mediaInfo.isRtpEnable() && inviteInfo == null) {
279 276 String ssrc = String.format("%010d", Long.parseLong(param.getStream(), 16));
280 277 inviteInfo = inviteStreamService.getInviteInfoBySSRC(ssrc);
281   - result.setStream_replace(inviteInfo.getStream());
282   - logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
  278 + if (inviteInfo != null) {
  279 + result.setStream_replace(inviteInfo.getStream());
  280 + logger.info("[ZLM HOOK]推流鉴权 stream: {} 替换为 {}", param.getStream(), inviteInfo.getStream());
  281 + }
283 282 }
284 283  
285 284 // 设置音频信息及录制信息
286   - List<SsrcTransaction> ssrcTransactionForAll = (inviteInfo == null ? null :
287   - sessionManager.getSsrcTransactionForAll(inviteInfo.getDeviceId(), inviteInfo.getChannelId(), null, null));
  285 + List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
288 286 if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
  287 +
  288 + // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用
  289 + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
  290 + streamAuthorityInfo.setApp(param.getApp());
  291 + streamAuthorityInfo.setStream(ssrcTransactionForAll.get(0).getStream());
  292 + streamAuthorityInfo.setCallId(ssrcTransactionForAll.get(0).getSipTransactionInfo().getCallId());
  293 +
  294 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), ssrcTransactionForAll.get(0).getStream(), streamAuthorityInfo);
  295 +
289 296 String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
290 297 String channelId = ssrcTransactionForAll.get(0).getChannelId();
291 298 DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
... ... @@ -294,38 +301,29 @@ public class ZLMHttpHookListener {
294 301 }
295 302 // 如果是录像下载就设置视频间隔十秒
296 303 if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
297   - result.setMp4_max_second(10);
298   - result.setEnable_mp4(true);
  304 + // 获取录像的总时长,然后设置为这个视频的时长
  305 + InviteInfo inviteInfoForDownload = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, param.getStream());
  306 + if (inviteInfoForDownload != null && inviteInfoForDownload.getStreamInfo() != null) {
  307 + String startTime = inviteInfoForDownload.getStreamInfo().getStartTime();
  308 + String endTime = inviteInfoForDownload.getStreamInfo().getEndTime();
  309 + long difference = DateUtil.getDifference(startTime, endTime) / 1000;
  310 + result.setMp4_max_second((int) difference);
  311 + result.setEnable_mp4(true);
  312 + // 设置为2保证得到的mp4的时长是正常的
  313 + result.setModify_stamp(2);
  314 + }
299 315 }
300 316 // 如果是talk对讲,则默认获取声音
301 317 if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.TALK) {
302 318 result.setEnable_audio(true);
303 319 }
304 320 }
305   - }else if (param.getApp().equals("broadcast")) {
  321 + }
  322 + else if (param.getApp().equals("broadcast")) {
306 323 result.setEnable_audio(true);
307 324 }else if (param.getApp().equals("talk")) {
308 325 result.setEnable_audio(true);
309 326 }
310   -
311   - if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
312   - logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
313   - JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
314   - if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0) {
315   - JSONObject dataJson = info.getJSONObject("data");
316   - if (dataJson != null) {
317   - String recordPath = dataJson.getString("record");
318   - userSetting.setRecordPath(recordPath);
319   - result.setMp4_save_path(recordPath);
320   - // 修改zlm中的录像路径
321   - if (mediaInfo.isAutoConfig()) {
322   - taskExecutor.execute(() -> {
323   - mediaServerService.setZLMConfig(mediaInfo, false);
324   - });
325   - }
326   - }
327   - }
328   - }
329 327 if (param.getApp().equalsIgnoreCase("rtp")) {
330 328 String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + param.getStream();
331 329 OtherRtpSendInfo otherRtpSendInfo = (OtherRtpSendInfo)redisTemplate.opsForValue().get(receiveKey);
... ... @@ -371,13 +369,11 @@ public class ZLMHttpHookListener {
371 369  
372 370 List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
373 371 // TODO 重构此处逻辑
374   - boolean isPush = false;
375 372 if (param.isRegist()) {
376   - // 处理流注册的鉴权信息
  373 + // 处理流注册的鉴权信息, 流注销这里不再删除鉴权信息,下次来了新的鉴权信息会对就的进行覆盖
377 374 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
378 375 || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
379 376 || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
380   - isPush = true;
381 377 StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
382 378 if (streamAuthorityInfo == null) {
383 379 streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
... ... @@ -387,8 +383,6 @@ public class ZLMHttpHookListener {
387 383 }
388 384 redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
389 385 }
390   - } else {
391   - redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
392 386 }
393 387  
394 388 if ("rtsp".equals(param.getSchema())) {
... ... @@ -470,35 +464,40 @@ public class ZLMHttpHookListener {
470 464 } else {
471 465 if (!"rtp".equals(param.getApp())) {
472 466 String type = OriginType.values()[param.getOriginType()].getType();
473   - MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
474   -
475   - if (mediaServerItem != null) {
476   - if (param.isRegist()) {
477   - StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
478   - String callId = null;
479   - if (streamAuthorityInfo != null) {
480   - callId = streamAuthorityInfo.getCallId();
481   - }
482   - StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
483   - param.getApp(), param.getStream(), param.getTracks(), callId);
484   - param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
485   - redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
486   - if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
487   - || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
488   - || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
489   - param.setSeverId(userSetting.getServerId());
490   - zlmMediaListManager.addPush(param);
491   - }
492   - } else {
493   - // 兼容流注销时类型从redis记录获取
494   - OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
495   - param.getApp(), param.getStream(), param.getMediaServerId());
496   - if (onStreamChangedHookParam != null) {
497   - type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
498   - redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
  467 + if (param.isRegist()) {
  468 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(
  469 + param.getApp(), param.getStream());
  470 + String callId = null;
  471 + if (streamAuthorityInfo != null) {
  472 + callId = streamAuthorityInfo.getCallId();
  473 + }
  474 + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaInfo,
  475 + param.getApp(), param.getStream(), tracks, callId);
  476 + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream));
  477 + redisCatchStorage.addStream(mediaInfo, type, param.getApp(), param.getStream(), param);
  478 + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
  479 + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
  480 + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
  481 + param.setSeverId(userSetting.getServerId());
  482 + zlmMediaListManager.addPush(param);
  483 +
  484 + // 冗余数据,自己系统中自用
  485 + redisCatchStorage.addPushListItem(param.getApp(), param.getStream(), param);
  486 + }
  487 + } else {
  488 + // 兼容流注销时类型从redis记录获取
  489 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(
  490 + param.getApp(), param.getStream(), param.getMediaServerId());
  491 + if (onStreamChangedHookParam != null) {
  492 + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
  493 + redisCatchStorage.removeStream(mediaInfo.getId(), type, param.getApp(), param.getStream());
  494 + if ("PUSH".equalsIgnoreCase(type)) {
  495 + // 冗余数据,自己系统中自用
  496 + redisCatchStorage.removePushListItem(param.getApp(), param.getStream(), param.getMediaServerId());
499 497 }
500   - GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
501   - if (gbStream != null) {
  498 + }
  499 + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
  500 + if (gbStream != null) {
502 501 // eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
503 502 }
504 503 zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
... ... @@ -618,11 +617,15 @@ public class ZLMHttpHookListener {
618 617 if (info != null) {
619 618 cmder.streamByeCmd(device, inviteInfo.getChannelId(),
620 619 inviteInfo.getStream(), null);
  620 + }else {
  621 + logger.info("[无人观看] 未找到设备的点播信息: {}, 流:{}", inviteInfo.getDeviceId(), param.getStream());
621 622 }
622 623 } catch (InvalidArgumentException | ParseException | SipException |
623 624 SsrcTransactionNotFoundException e) {
624 625 logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage());
625 626 }
  627 + }else {
  628 + logger.info("[无人观看] 未找到设备: {},流:{}", inviteInfo.getDeviceId(), param.getStream());
626 629 }
627 630  
628 631 inviteStreamService.removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(),
... ... @@ -858,7 +861,7 @@ public class ZLMHttpHookListener {
858 861 taskExecutor.execute(() -> {
859 862 JSONObject json = (JSONObject) JSON.toJSON(param);
860 863 List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
861   - if (subscribes != null && subscribes.size() > 0) {
  864 + if (subscribes != null && !subscribes.isEmpty()) {
862 865 for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
863 866 subscribe.response(null, param);
864 867 }
... ... @@ -868,6 +871,28 @@ public class ZLMHttpHookListener {
868 871 return HookResult.SUCCESS();
869 872 }
870 873  
  874 + /**
  875 + * 录像完成事件
  876 + */
  877 + @ResponseBody
  878 + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
  879 + public HookResult onRecordMp4(HttpServletRequest request, @RequestBody OnRecordMp4HookParam param) {
  880 + logger.info("[ZLM HOOK] 录像完成事件:{}->{}", param.getMediaServerId(), param.getFile_path());
  881 +
  882 + taskExecutor.execute(() -> {
  883 + List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_record_mp4);
  884 + if (subscribes != null && !subscribes.isEmpty()) {
  885 + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
  886 + subscribe.response(null, param);
  887 + }
  888 + }
  889 + cloudRecordService.addRecord(param);
  890 +
  891 + });
  892 +
  893 + return HookResult.SUCCESS();
  894 + }
  895 +
871 896 private Map<String, String> urlParamToMap(String params) {
872 897 HashMap<String, String> map = new HashMap<>();
873 898 if (ObjectUtils.isEmpty(params)) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -25,8 +25,6 @@ public class ZLMRESTfulUtils {
25 25  
26 26 private OkHttpClient client;
27 27  
28   -
29   -
30 28 public interface RequestCallback{
31 29 void run(JSONObject response);
32 30 }
... ... @@ -405,4 +403,14 @@ public class ZLMRESTfulUtils {
405 403 param.put("stream_id", streamId);
406 404 return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null);
407 405 }
  406 +
  407 + public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) {
  408 + Map<String, Object> param = new HashMap<>(1);
  409 + param.put("vhost", "__defaultVhost__");
  410 + param.put("app", app);
  411 + param.put("stream", stream);
  412 + param.put("period", date);
  413 + param.put("name", fileName);
  414 + return sendPost(mediaServerItem, "deleteRecordDirectory",param, null);
  415 + }
408 416 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
... ... @@ -57,4 +57,15 @@ public class HookSubscribeFactory {
57 57 return hookSubscribe;
58 58 }
59 59  
  60 + public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) {
  61 + HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4();
  62 + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
  63 + subscribeKey.put("app", app);
  64 + subscribeKey.put("stream", stream);
  65 + subscribeKey.put("mediaServerId", mediaServerId);
  66 + hookSubscribe.setContent(subscribeKey);
  67 +
  68 + return hookSubscribe;
  69 + }
  70 +
60 71 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRecordMp4.java 0 → 100755
  1 +package com.genersoft.iot.vmp.media.zlm.dto;
  2 +
  3 +import com.alibaba.fastjson2.JSONObject;
  4 +import com.alibaba.fastjson2.annotation.JSONField;
  5 +
  6 +import java.time.Instant;
  7 +
  8 +/**
  9 + * hook订阅-录像完成
  10 + * @author lin
  11 + */
  12 +public class HookSubscribeForRecordMp4 implements IHookSubscribe{
  13 +
  14 + private HookType hookType = HookType.on_record_mp4;
  15 +
  16 + private JSONObject content;
  17 +
  18 + @JSONField(format="yyyy-MM-dd HH:mm:ss")
  19 + private Instant expires;
  20 +
  21 + @Override
  22 + public HookType getHookType() {
  23 + return hookType;
  24 + }
  25 +
  26 + @Override
  27 + public JSONObject getContent() {
  28 + return content;
  29 + }
  30 +
  31 + public void setContent(JSONObject content) {
  32 + this.content = content;
  33 + }
  34 +
  35 + @Override
  36 + public Instant getExpires() {
  37 + return expires;
  38 + }
  39 +
  40 + @Override
  41 + public void setExpires(Instant expires) {
  42 + this.expires = expires;
  43 + }
  44 +}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
... ... @@ -80,9 +80,11 @@ public class MediaServerItem{
80 80 @Schema(description = "是否是默认ZLM")
81 81 private boolean defaultServer;
82 82  
83   - @Schema(description = "当前使用到的端口")
84   - private int currentPort;
  83 + @Schema(description = "录像存储时长")
  84 + private int recordDay;
85 85  
  86 + @Schema(description = "录像存储路径")
  87 + private String recordPath;
86 88  
87 89 public MediaServerItem() {
88 90 }
... ... @@ -269,14 +271,6 @@ public class MediaServerItem{
269 271 this.updateTime = updateTime;
270 272 }
271 273  
272   - public int getCurrentPort() {
273   - return currentPort;
274   - }
275   -
276   - public void setCurrentPort(int currentPort) {
277   - this.currentPort = currentPort;
278   - }
279   -
280 274 public boolean isStatus() {
281 275 return status;
282 276 }
... ... @@ -308,4 +302,20 @@ public class MediaServerItem{
308 302 public void setSendRtpPortRange(String sendRtpPortRange) {
309 303 this.sendRtpPortRange = sendRtpPortRange;
310 304 }
  305 +
  306 + public int getRecordDay() {
  307 + return recordDay;
  308 + }
  309 +
  310 + public void setRecordDay(int recordDay) {
  311 + this.recordDay = recordDay;
  312 + }
  313 +
  314 + public String getRecordPath() {
  315 + return recordPath;
  316 + }
  317 +
  318 + public void setRecordPath(String recordPath) {
  319 + this.recordPath = recordPath;
  320 + }
311 321 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
... ... @@ -7,6 +7,7 @@ public class HookResultForOnPublish extends HookResult{
7 7 private int mp4_max_second;
8 8 private String mp4_save_path;
9 9 private String stream_replace;
  10 + private Integer modify_stamp;
10 11  
11 12 public HookResultForOnPublish() {
12 13 }
... ... @@ -60,14 +61,23 @@ public class HookResultForOnPublish extends HookResult{
60 61 this.stream_replace = stream_replace;
61 62 }
62 63  
  64 + public Integer getModify_stamp() {
  65 + return modify_stamp;
  66 + }
  67 +
  68 + public void setModify_stamp(Integer modify_stamp) {
  69 + this.modify_stamp = modify_stamp;
  70 + }
  71 +
63 72 @Override
64 73 public String toString() {
65 74 return "HookResultForOnPublish{" +
66 75 "enable_audio=" + enable_audio +
67 76 ", enable_mp4=" + enable_mp4 +
68 77 ", mp4_max_second=" + mp4_max_second +
69   - ", stream_replace=" + stream_replace +
70 78 ", mp4_save_path='" + mp4_save_path + '\'' +
  79 + ", stream_replace='" + stream_replace + '\'' +
  80 + ", modify_stamp='" + modify_stamp + '\'' +
71 81 '}';
72 82 }
73 83 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRecordMp4HookParam.java 0 → 100755
  1 +package com.genersoft.iot.vmp.media.zlm.dto.hook;
  2 +
  3 +/**
  4 + * zlm hook事件中的on_rtp_server_timeout事件的参数
  5 + * @author lin
  6 + */
  7 +public class OnRecordMp4HookParam extends HookParam{
  8 + private String app;
  9 + private String stream;
  10 + private String file_name;
  11 + private String file_path;
  12 + private long file_size;
  13 + private String folder;
  14 + private String url;
  15 + private String vhost;
  16 + private long start_time;
  17 + private double time_len;
  18 +
  19 + public String getApp() {
  20 + return app;
  21 + }
  22 +
  23 + public void setApp(String app) {
  24 + this.app = app;
  25 + }
  26 +
  27 + public String getStream() {
  28 + return stream;
  29 + }
  30 +
  31 + public void setStream(String stream) {
  32 + this.stream = stream;
  33 + }
  34 +
  35 + public String getFile_name() {
  36 + return file_name;
  37 + }
  38 +
  39 + public void setFile_name(String file_name) {
  40 + this.file_name = file_name;
  41 + }
  42 +
  43 + public String getFile_path() {
  44 + return file_path;
  45 + }
  46 +
  47 + public void setFile_path(String file_path) {
  48 + this.file_path = file_path;
  49 + }
  50 +
  51 + public long getFile_size() {
  52 + return file_size;
  53 + }
  54 +
  55 + public void setFile_size(long file_size) {
  56 + this.file_size = file_size;
  57 + }
  58 +
  59 + public String getFolder() {
  60 + return folder;
  61 + }
  62 +
  63 + public void setFolder(String folder) {
  64 + this.folder = folder;
  65 + }
  66 +
  67 + public String getUrl() {
  68 + return url;
  69 + }
  70 +
  71 + public void setUrl(String url) {
  72 + this.url = url;
  73 + }
  74 +
  75 + public String getVhost() {
  76 + return vhost;
  77 + }
  78 +
  79 + public void setVhost(String vhost) {
  80 + this.vhost = vhost;
  81 + }
  82 +
  83 + public long getStart_time() {
  84 + return start_time;
  85 + }
  86 +
  87 + public void setStart_time(long start_time) {
  88 + this.start_time = start_time;
  89 + }
  90 +
  91 + public double getTime_len() {
  92 + return time_len;
  93 + }
  94 +
  95 + public void setTime_len(double time_len) {
  96 + this.time_len = time_len;
  97 + }
  98 +
  99 + @Override
  100 + public String toString() {
  101 + return "OnRecordMp4HookParam{" +
  102 + "app='" + app + '\'' +
  103 + ", stream='" + stream + '\'' +
  104 + ", file_name='" + file_name + '\'' +
  105 + ", file_path='" + file_path + '\'' +
  106 + ", file_size='" + file_size + '\'' +
  107 + ", folder='" + folder + '\'' +
  108 + ", url='" + url + '\'' +
  109 + ", vhost='" + vhost + '\'' +
  110 + ", start_time=" + start_time +
  111 + ", time_len=" + time_len +
  112 + '}';
  113 + }
  114 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java 0 → 100755
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.alibaba.fastjson2.JSONArray;
  4 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  5 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  6 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  7 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
  8 +import com.github.pagehelper.PageInfo;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 云端录像管理
  14 + * @author lin
  15 + */
  16 +public interface ICloudRecordService {
  17 +
  18 + /**
  19 + * 分页回去云端录像列表
  20 + */
  21 + PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
  22 +
  23 + /**
  24 + * 根据hook消息增加一条记录
  25 + */
  26 + void addRecord(OnRecordMp4HookParam param);
  27 +
  28 + /**
  29 + * 获取所有的日期
  30 + */
  31 + List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
  32 +
  33 + /**
  34 + * 添加合并任务
  35 + */
  36 + String addTask(String app, String stream, String mediaServerId, String startTime, String endTime, String callId, String remoteHost);
  37 +
  38 +
  39 + /**
  40 + * 查询合并任务列表
  41 + */
  42 + JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId, Boolean isEnd);
  43 +
  44 + /**
  45 + * 收藏视频,收藏的视频过期不会删除
  46 + */
  47 + int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId);
  48 +
  49 + /**
  50 + * 添加指定录像收藏
  51 + */
  52 + int changeCollectById(Integer recordId, boolean result);
  53 +
  54 + /**
  55 + * 获取播放地址
  56 + */
  57 + DownloadFileInfo getPlayUrlPath(Integer recordId);
  58 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
... ... @@ -89,21 +89,12 @@ public interface IMediaServerService {
89 89  
90 90 void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data);
91 91  
92   - boolean checkRtpServer(MediaServerItem mediaServerItem, String rtp, String stream);
93   -
94 92 /**
95 93 * 获取负载信息
96 94 * @return
97 95 */
98 96 MediaServerLoad getLoad(MediaServerItem mediaServerItem);
99 97  
100   - /**
101   - * 按时间查找录像文件
102   - */
103   - List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
  98 + List<MediaServerItem> getAllWithAssistPort();
104 99  
105   - /**
106   - * 查找存在录像文件的时间
107   - */
108   - List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems);
109 100 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
... ... @@ -33,11 +33,6 @@ public interface IPlayService {
33 33  
34 34 MediaServerItem getNewMediaServerItem(Device device);
35 35  
36   - /**
37   - * 获取包含assist服务的节点
38   - */
39   - MediaServerItem getNewMediaServerItemHasAssist(Device device);
40   -
41 36 void playBack(String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
42 37 void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, ErrorCallback<Object> callback);
43 38 void zlmServerOffline(String mediaServerId);
... ... @@ -72,5 +67,4 @@ public interface IPlayService {
72 67  
73 68 void getSnap(String deviceId, String channelId, String fileName, ErrorCallback errorCallback);
74 69  
75   -
76 70 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
... ... @@ -114,4 +114,5 @@ public interface IStreamPushService {
114 114 * @return
115 115 */
116 116 ResourceBaseInfo getOverview();
  117 +
117 118 }
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/CloudRecordItem.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  4 +
  5 +/**
  6 + * 云端录像数据
  7 + */
  8 +public class CloudRecordItem {
  9 + /**
  10 + * 主键
  11 + */
  12 + private int id;
  13 +
  14 + /**
  15 + * 应用名
  16 + */
  17 + private String app;
  18 +
  19 + /**
  20 + * 流
  21 + */
  22 + private String stream;
  23 +
  24 + /**
  25 + * 健全ID
  26 + */
  27 + private String callId;
  28 +
  29 + /**
  30 + * 开始时间
  31 + */
  32 + private long startTime;
  33 +
  34 + /**
  35 + * 结束时间
  36 + */
  37 + private long endTime;
  38 +
  39 + /**
  40 + * ZLM Id
  41 + */
  42 + private String mediaServerId;
  43 +
  44 + /**
  45 + * 文件名称
  46 + */
  47 + private String fileName;
  48 +
  49 + /**
  50 + * 文件路径
  51 + */
  52 + private String filePath;
  53 +
  54 + /**
  55 + * 文件夹
  56 + */
  57 + private String folder;
  58 +
  59 + /**
  60 + * 收藏,收藏的文件不移除
  61 + */
  62 + private Boolean collect;
  63 +
  64 + /**
  65 + * 保留,收藏的文件不移除
  66 + */
  67 + private Boolean reserve;
  68 +
  69 + /**
  70 + * 文件大小
  71 + */
  72 + private long fileSize;
  73 +
  74 + /**
  75 + * 文件时长
  76 + */
  77 + private long timeLen;
  78 +
  79 + public static CloudRecordItem getInstance(OnRecordMp4HookParam param) {
  80 + CloudRecordItem cloudRecordItem = new CloudRecordItem();
  81 + cloudRecordItem.setApp(param.getApp());
  82 + cloudRecordItem.setStream(param.getStream());
  83 + cloudRecordItem.setStartTime(param.getStart_time()*1000);
  84 + cloudRecordItem.setFileName(param.getFile_name());
  85 + cloudRecordItem.setFolder(param.getFolder());
  86 + cloudRecordItem.setFileSize(param.getFile_size());
  87 + cloudRecordItem.setFilePath(param.getFile_path());
  88 + cloudRecordItem.setMediaServerId(param.getMediaServerId());
  89 + cloudRecordItem.setTimeLen((long) param.getTime_len() * 1000);
  90 + cloudRecordItem.setEndTime((param.getStart_time() + (long)param.getTime_len()) * 1000);
  91 + return cloudRecordItem;
  92 + }
  93 +
  94 + public int getId() {
  95 + return id;
  96 + }
  97 +
  98 + public void setId(int id) {
  99 + this.id = id;
  100 + }
  101 +
  102 + public String getApp() {
  103 + return app;
  104 + }
  105 +
  106 + public void setApp(String app) {
  107 + this.app = app;
  108 + }
  109 +
  110 + public String getStream() {
  111 + return stream;
  112 + }
  113 +
  114 + public void setStream(String stream) {
  115 + this.stream = stream;
  116 + }
  117 +
  118 + public String getCallId() {
  119 + return callId;
  120 + }
  121 +
  122 + public void setCallId(String callId) {
  123 + this.callId = callId;
  124 + }
  125 +
  126 + public long getStartTime() {
  127 + return startTime;
  128 + }
  129 +
  130 + public void setStartTime(long startTime) {
  131 + this.startTime = startTime;
  132 + }
  133 +
  134 + public long getEndTime() {
  135 + return endTime;
  136 + }
  137 +
  138 + public void setEndTime(long endTime) {
  139 + this.endTime = endTime;
  140 + }
  141 +
  142 + public String getMediaServerId() {
  143 + return mediaServerId;
  144 + }
  145 +
  146 + public void setMediaServerId(String mediaServerId) {
  147 + this.mediaServerId = mediaServerId;
  148 + }
  149 +
  150 + public String getFileName() {
  151 + return fileName;
  152 + }
  153 +
  154 + public void setFileName(String fileName) {
  155 + this.fileName = fileName;
  156 + }
  157 +
  158 + public String getFilePath() {
  159 + return filePath;
  160 + }
  161 +
  162 + public void setFilePath(String filePath) {
  163 + this.filePath = filePath;
  164 + }
  165 +
  166 + public String getFolder() {
  167 + return folder;
  168 + }
  169 +
  170 + public void setFolder(String folder) {
  171 + this.folder = folder;
  172 + }
  173 +
  174 + public long getFileSize() {
  175 + return fileSize;
  176 + }
  177 +
  178 + public void setFileSize(long fileSize) {
  179 + this.fileSize = fileSize;
  180 + }
  181 +
  182 + public long getTimeLen() {
  183 + return timeLen;
  184 + }
  185 +
  186 + public void setTimeLen(long timeLen) {
  187 + this.timeLen = timeLen;
  188 + }
  189 +
  190 + public Boolean getCollect() {
  191 + return collect;
  192 + }
  193 +
  194 + public void setCollect(Boolean collect) {
  195 + this.collect = collect;
  196 + }
  197 +
  198 + public Boolean getReserve() {
  199 + return reserve;
  200 + }
  201 +
  202 + public void setReserve(Boolean reserve) {
  203 + this.reserve = reserve;
  204 + }
  205 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/DownloadFileInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.bean;
  2 +
  3 +public class DownloadFileInfo {
  4 +
  5 + private String httpPath;
  6 + private String httpsPath;
  7 + private String httpDomainPath;
  8 + private String httpsDomainPath;
  9 +
  10 + public String getHttpPath() {
  11 + return httpPath;
  12 + }
  13 +
  14 + public void setHttpPath(String httpPath) {
  15 + this.httpPath = httpPath;
  16 + }
  17 +
  18 + public String getHttpsPath() {
  19 + return httpsPath;
  20 + }
  21 +
  22 + public void setHttpsPath(String httpsPath) {
  23 + this.httpsPath = httpsPath;
  24 + }
  25 +
  26 + public String getHttpDomainPath() {
  27 + return httpDomainPath;
  28 + }
  29 +
  30 + public void setHttpDomainPath(String httpDomainPath) {
  31 + this.httpDomainPath = httpDomainPath;
  32 + }
  33 +
  34 + public String getHttpsDomainPath() {
  35 + return httpsDomainPath;
  36 + }
  37 +
  38 + public void setHttpsDomainPath(String httpsDomainPath) {
  39 + this.httpsDomainPath = httpsDomainPath;
  40 + }
  41 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java
... ... @@ -29,12 +29,12 @@ public class WvpRedisMsg {
29 29 * 消息的ID
30 30 */
31 31 private String serial;
32   - private Object content;
  32 + private String content;
33 33  
34 34 private final static String requestTag = "req";
35 35 private final static String responseTag = "res";
36 36  
37   - public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) {
  37 + public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, String content) {
38 38 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
39 39 wvpRedisMsg.setType(requestTag);
40 40 wvpRedisMsg.setFromId(fromId);
... ... @@ -51,7 +51,7 @@ public class WvpRedisMsg {
51 51 return wvpRedisMsg;
52 52 }
53 53  
54   - public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) {
  54 + public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, String content) {
55 55 WvpRedisMsg wvpRedisMsg = new WvpRedisMsg();
56 56 wvpRedisMsg.setType(responseTag);
57 57 wvpRedisMsg.setFromId(fromId);
... ... @@ -106,11 +106,11 @@ public class WvpRedisMsg {
106 106 this.cmd = cmd;
107 107 }
108 108  
109   - public Object getContent() {
  109 + public String getContent() {
110 110 return content;
111 111 }
112 112  
113   - public void setContent(Object content) {
  113 + public void setContent(String content) {
114 114 this.content = content;
115 115 }
116 116 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.alibaba.fastjson2.JSONArray;
  4 +import com.alibaba.fastjson2.JSONObject;
  5 +import com.genersoft.iot.vmp.conf.exception.ControllerException;
  6 +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  7 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
  8 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  9 +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
  10 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
  11 +import com.genersoft.iot.vmp.service.ICloudRecordService;
  12 +import com.genersoft.iot.vmp.service.IMediaServerService;
  13 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  14 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
  15 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  16 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  17 +import com.genersoft.iot.vmp.utils.CloudRecordUtils;
  18 +import com.genersoft.iot.vmp.utils.DateUtil;
  19 +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
  20 +import com.github.pagehelper.PageHelper;
  21 +import com.github.pagehelper.PageInfo;
  22 +import org.apache.commons.lang3.ObjectUtils;
  23 +import org.slf4j.Logger;
  24 +import org.slf4j.LoggerFactory;
  25 +import org.springframework.beans.factory.annotation.Autowired;
  26 +import org.springframework.stereotype.Service;
  27 +
  28 +import java.time.*;
  29 +import java.util.*;
  30 +
  31 +@Service
  32 +public class CloudRecordServiceImpl implements ICloudRecordService {
  33 +
  34 + private final static Logger logger = LoggerFactory.getLogger(CloudRecordServiceImpl.class);
  35 +
  36 + @Autowired
  37 + private CloudRecordServiceMapper cloudRecordServiceMapper;
  38 +
  39 + @Autowired
  40 + private IMediaServerService mediaServerService;
  41 +
  42 + @Autowired
  43 + private IRedisCatchStorage redisCatchStorage;
  44 +
  45 + @Autowired
  46 + private AssistRESTfulUtils assistRESTfulUtils;
  47 +
  48 + @Autowired
  49 + private VideoStreamSessionManager streamSession;
  50 +
  51 + @Override
  52 + public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
  53 + // 开始时间和结束时间在数据库中都是以秒为单位的
  54 + Long startTimeStamp = null;
  55 + Long endTimeStamp = null;
  56 + if (startTime != null ) {
  57 + if (!DateUtil.verification(startTime, DateUtil.formatter)) {
  58 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
  59 + }
  60 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  61 +
  62 + }
  63 + if (endTime != null ) {
  64 + if (!DateUtil.verification(endTime, DateUtil.formatter)) {
  65 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
  66 + }
  67 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  68 +
  69 + }
  70 + PageHelper.startPage(page, count);
  71 + List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
  72 + null, mediaServerItems);
  73 + return new PageInfo<>(all);
  74 + }
  75 +
  76 + @Override
  77 + public List<String> getDateList(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
  78 + LocalDate startDate = LocalDate.of(year, month, 1);
  79 + LocalDate endDate;
  80 + if (month == 12) {
  81 + endDate = LocalDate.of(year + 1, 1, 1);
  82 + }else {
  83 + endDate = LocalDate.of(year, month + 1, 1);
  84 + }
  85 + long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
  86 + long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
  87 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
  88 + endTimeStamp, null, mediaServerItems);
  89 + if (cloudRecordItemList.isEmpty()) {
  90 + return new ArrayList<>();
  91 + }
  92 + Set<String> resultSet = new HashSet<>();
  93 + cloudRecordItemList.stream().forEach(cloudRecordItem -> {
  94 + String date = DateUtil.timestampTo_yyyy_MM_dd(cloudRecordItem.getStartTime());
  95 + resultSet.add(date);
  96 + });
  97 + return new ArrayList<>(resultSet);
  98 + }
  99 +
  100 + @Override
  101 + public void addRecord(OnRecordMp4HookParam param) {
  102 + CloudRecordItem cloudRecordItem = CloudRecordItem.getInstance(param);
  103 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
  104 + if (streamAuthorityInfo != null) {
  105 + cloudRecordItem.setCallId(streamAuthorityInfo.getCallId());
  106 + }
  107 + logger.info("[添加录像记录] {}/{} 文件大小:{}, 时长: {}秒", param.getApp(), param.getStream(), param.getFile_size(),param.getTime_len());
  108 + cloudRecordServiceMapper.add(cloudRecordItem);
  109 + }
  110 +
  111 + @Override
  112 + public String addTask(String app, String stream, String mediaServerId, String startTime, String endTime, String callId, String remoteHost) {
  113 + // 参数校验
  114 + assert app != null;
  115 + assert stream != null;
  116 + MediaServerItem mediaServerItem = null;
  117 + if (mediaServerId == null) {
  118 + mediaServerItem = mediaServerService.getDefaultMediaServer();
  119 + }else {
  120 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  121 + }
  122 + if (mediaServerItem == null) {
  123 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
  124 + }else {
  125 + if (remoteHost == null) {
  126 + remoteHost = "http://" + mediaServerItem.getStreamIp() + ":" + mediaServerItem.getRecordAssistPort();
  127 + }
  128 + }
  129 + if (mediaServerItem.getRecordAssistPort() == 0) {
  130 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "为配置Assist服务");
  131 + }
  132 + Long startTimeStamp = null;
  133 + Long endTimeStamp = null;
  134 + if (startTime != null) {
  135 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  136 + }
  137 + if (endTime != null) {
  138 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  139 + }
  140 +
  141 + List<MediaServerItem> mediaServers = new ArrayList<>();
  142 + mediaServers.add(mediaServerItem);
  143 + // 检索相关的录像文件
  144 + List<String> filePathList = cloudRecordServiceMapper.queryRecordFilePathList(app, stream, startTimeStamp, endTimeStamp, callId, mediaServers);
  145 + if (filePathList == null || filePathList.isEmpty()) {
  146 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未检索到视频文件");
  147 + }
  148 + JSONObject result = assistRESTfulUtils.addTask(mediaServerItem, app, stream, startTime, endTime, callId, filePathList, remoteHost);
  149 + if (result.getInteger("code") != 0) {
  150 + throw new ControllerException(result.getInteger("code"), result.getString("msg"));
  151 + }
  152 + return result.getString("data");
  153 + }
  154 +
  155 + @Override
  156 + public JSONArray queryTask(String app, String stream, String callId, String taskId, String mediaServerId, Boolean isEnd) {
  157 + MediaServerItem mediaServerItem = null;
  158 + if (mediaServerId == null) {
  159 + mediaServerItem = mediaServerService.getDefaultMediaServer();
  160 + }else {
  161 + mediaServerItem = mediaServerService.getOne(mediaServerId);
  162 + }
  163 + if (mediaServerItem == null) {
  164 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体");
  165 + }
  166 + JSONObject result = assistRESTfulUtils.queryTaskList(mediaServerItem, app, stream, callId, taskId, isEnd);
  167 + if (result.getInteger("code") != 0) {
  168 + throw new ControllerException(result.getInteger("code"), result.getString("msg"));
  169 + }
  170 + return result.getJSONArray("data");
  171 + }
  172 +
  173 + @Override
  174 + public int changeCollect(boolean result, String app, String stream, String mediaServerId, String startTime, String endTime, String callId) {
  175 + // 开始时间和结束时间在数据库中都是以秒为单位的
  176 + Long startTimeStamp = null;
  177 + Long endTimeStamp = null;
  178 + if (startTime != null ) {
  179 + if (!DateUtil.verification(startTime, DateUtil.formatter)) {
  180 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间格式错误,正确格式为: " + DateUtil.formatter);
  181 + }
  182 + startTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  183 +
  184 + }
  185 + if (endTime != null ) {
  186 + if (!DateUtil.verification(endTime, DateUtil.formatter)) {
  187 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "结束时间格式错误,正确格式为: " + DateUtil.formatter);
  188 + }
  189 + endTimeStamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  190 +
  191 + }
  192 +
  193 + List<MediaServerItem> mediaServerItems;
  194 + if (!ObjectUtils.isEmpty(mediaServerId)) {
  195 + mediaServerItems = new ArrayList<>();
  196 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  197 + if (mediaServerItem == null) {
  198 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
  199 + }
  200 + mediaServerItems.add(mediaServerItem);
  201 + } else {
  202 + mediaServerItems = null;
  203 + }
  204 +
  205 + List<CloudRecordItem> all = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp, endTimeStamp,
  206 + callId, mediaServerItems);
  207 + if (all.isEmpty()) {
  208 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到待收藏的视频");
  209 + }
  210 + int limitCount = 50;
  211 + int resultCount = 0;
  212 + if (all.size() > limitCount) {
  213 + for (int i = 0; i < all.size(); i += limitCount) {
  214 + int toIndex = i + limitCount;
  215 + if (i + limitCount > all.size()) {
  216 + toIndex = all.size();
  217 + }
  218 + resultCount += cloudRecordServiceMapper.updateCollectList(result, all.subList(i, toIndex));
  219 +
  220 + }
  221 + }else {
  222 + resultCount = cloudRecordServiceMapper.updateCollectList(result, all);
  223 + }
  224 + return resultCount;
  225 + }
  226 +
  227 + @Override
  228 + public int changeCollectById(Integer recordId, boolean result) {
  229 + return cloudRecordServiceMapper.changeCollectById(result, recordId);
  230 + }
  231 +
  232 + @Override
  233 + public DownloadFileInfo getPlayUrlPath(Integer recordId) {
  234 + CloudRecordItem recordItem = cloudRecordServiceMapper.queryOne(recordId);
  235 + if (recordItem == null) {
  236 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "资源不存在");
  237 + }
  238 + String filePath = recordItem.getFilePath();
  239 + MediaServerItem mediaServerItem = mediaServerService.getOne(recordItem.getMediaServerId());
  240 + return CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
  241 + }
  242 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
... ... @@ -162,6 +162,19 @@ public class DeviceServiceImpl implements IDeviceService {
162 162 sync(device);
163 163 // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台
164 164 }
  165 + // 上线添加订阅
  166 + if (device.getSubscribeCycleForCatalog() > 0) {
  167 + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
  168 + addCatalogSubscribe(device);
  169 + }
  170 + if (device.getSubscribeCycleForMobilePosition() > 0) {
  171 + addMobilePositionSubscribe(device);
  172 + }
  173 + if (userSetting.getDeviceStatusNotify()) {
  174 + // 发送redis消息
  175 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
  176 + }
  177 +
165 178 }else {
166 179 if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) {
167 180 logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId());
... ... @@ -174,22 +187,10 @@ public class DeviceServiceImpl implements IDeviceService {
174 187  
175 188 }
176 189  
177   - // 上线添加订阅
178   - if (device.getSubscribeCycleForCatalog() > 0) {
179   - // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅
180   - addCatalogSubscribe(device);
181   - }
182   - if (device.getSubscribeCycleForMobilePosition() > 0) {
183   - addMobilePositionSubscribe(device);
184   - }
185 190 // 刷新过期任务
186 191 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
187 192 // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
188 193 dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
189   - if (userSetting.getDeviceStatusNotify()) {
190   - // 发送redis消息
191   - redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true);
192   - }
193 194  
194 195 //
195 196 // try {
... ... @@ -213,6 +214,13 @@ public class DeviceServiceImpl implements IDeviceService {
213 214 }
214 215 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId;
215 216 dynamicTask.stop(registerExpireTaskKey);
  217 + if (device.isOnLine()) {
  218 + if (userSetting.getDeviceStatusNotify()) {
  219 + // 发送redis消息
  220 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false);
  221 + }
  222 + }
  223 +
216 224 device.setOnLine(false);
217 225 redisCatchStorage.updateDevice(device);
218 226 deviceMapper.update(device);
... ... @@ -224,7 +232,7 @@ public class DeviceServiceImpl implements IDeviceService {
224 232 for (SsrcTransaction ssrcTransaction : ssrcTransactions) {
225 233 mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc());
226 234 mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream());
227   - streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
  235 + streamSession.removeByCallId(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getCallId());
228 236 }
229 237 }
230 238 // 移除订阅
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
... ... @@ -250,9 +250,6 @@ public class GbStreamServiceImpl implements IGbStreamService {
250 250 if (platform == null) {
251 251 return ;
252 252 }
253   - if (ObjectUtils.isEmpty(catalogId)) {
254   - catalogId = platform.getDeviceGBId();
255   - }
256 253 if (platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId) > 0) {
257 254 List<GbStream> gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId);
258 255 List<DeviceChannel> deviceChannelList = new ArrayList<>();
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/InviteStreamServiceImpl.java
... ... @@ -116,9 +116,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
116 116 ":" + (stream != null ? stream : "*")
117 117 + ":*";
118 118 List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
119   - if (scanResult.size() != 1) {
  119 + if (scanResult.isEmpty()) {
120 120 return null;
121 121 }
  122 + if (scanResult.size() != 1) {
  123 + logger.warn("[获取InviteInfo] 发现 key: {}存在多条", key);
  124 + }
122 125  
123 126 return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0));
124 127 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -165,14 +165,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
165 165 if (streamId == null) {
166 166 streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase();
167 167 }
168   - int ssrcCheckParam = 0;
169   - if (ssrcCheck && tcpMode > 1) {
  168 + if (ssrcCheck && tcpMode > 0) {
170 169 // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验
171   - logger.warn("[openRTPServer] TCP被动/TCP主动收流时,默认关闭ssrc检验");
  170 + logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验");
172 171 }
173 172 int rtpServerPort;
174 173 if (mediaServerItem.isRtpEnable()) {
175   - rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, (ssrcCheck && tcpMode == 0) ? Long.parseLong(ssrc) : 0, port, onlyAuto, reUsePort, tcpMode);
  174 + rtpServerPort = zlmServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, reUsePort, tcpMode);
176 175 } else {
177 176 rtpServerPort = mediaServerItem.getRtpProxyPort();
178 177 }
... ... @@ -205,7 +204,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
205 204 @Override
206 205 public void closeRTPServer(String mediaServerId, String streamId) {
207 206 MediaServerItem mediaServerItem = this.getOne(mediaServerId);
208   - closeRTPServer(mediaServerItem, streamId);
  207 + if (mediaServerItem.isRtpEnable()) {
  208 + closeRTPServer(mediaServerItem, streamId);
  209 + }
  210 + zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId);
209 211 }
210 212  
211 213 @Override
... ... @@ -428,17 +430,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
428 430  
429 431  
430 432 if (serverItem.isAutoConfig()) {
431   - // 查看assist服务的录像路径配置
432   - if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
433   - JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
434   - if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
435   - JSONObject dataJson = info.getJSONObject("data");
436   - if (dataJson != null) {
437   - String recordPath = dataJson.getString("record");
438   - userSetting.setRecordPath(recordPath);
439   - }
440   - }
441   - }
442 433 setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
443 434 }
444 435 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
... ... @@ -573,7 +564,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
573 564 logger.info("[ZLM] 正在设置 :{} -> {}:{}",
574 565 mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
575 566 String protocol = sslEnabled ? "https" : "http";
576   - String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
  567 + String hookPrefix = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
577 568  
578 569 Map<String, Object> param = new HashMap<>();
579 570 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
... ... @@ -582,25 +573,21 @@ public class MediaServerServiceImpl implements IMediaServerService {
582 573 }
583 574 param.put("hook.enable","1");
584 575 param.put("hook.on_flow_report","");
585   - param.put("hook.on_play",String.format("%s/on_play", hookPrex));
  576 + param.put("hook.on_play",String.format("%s/on_play", hookPrefix));
586 577 param.put("hook.on_http_access","");
587   - param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
  578 + param.put("hook.on_publish", String.format("%s/on_publish", hookPrefix));
588 579 param.put("hook.on_record_ts","");
589 580 param.put("hook.on_rtsp_auth","");
590 581 param.put("hook.on_rtsp_realm","");
591   - param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex));
  582 + param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrefix));
592 583 param.put("hook.on_shell_login","");
593   - param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex));
594   - param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex));
595   - param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex));
596   - param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex));
597   - param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex));
598   - param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex));
599   - if (mediaServerItem.getRecordAssistPort() > 0) {
600   - param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort()));
601   - }else {
602   - param.put("hook.on_record_mp4","");
603   - }
  584 + param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrefix));
  585 + param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrefix));
  586 + param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrefix));
  587 + param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrefix));
  588 + param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrefix));
  589 + param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrefix));
  590 + param.put("hook.on_record_mp4",String.format("%s/on_record_mp4", hookPrefix));
604 591 param.put("hook.timeoutSec","20");
605 592 // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
606 593 // 置0关闭此特性(推流断开会导致立即断开播放器)
... ... @@ -609,15 +596,14 @@ public class MediaServerServiceImpl implements IMediaServerService {
609 596 param.put("protocol.continue_push_ms", "3000" );
610 597 // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流,
611 598 // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项
612   -// param.put("general.wait_track_ready_ms", "3000" );
613 599 if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) {
614 600 param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
615 601 }
616 602  
617   - if (userSetting.getRecordPath() != null) {
618   - File recordPathFile = new File(userSetting.getRecordPath());
619   - File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
620   - param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
  603 + if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) {
  604 + File recordPathFile = new File(mediaServerItem.getRecordPath());
  605 + param.put("protocol.mp4_save_path", recordPathFile.getParentFile().getPath());
  606 + param.put("protocol.downloadRoot", recordPathFile.getParentFile().getPath());
621 607 param.put("record.appName", recordPathFile.getName());
622 608 }
623 609  
... ... @@ -722,6 +708,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
722 708 ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null);
723 709 String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId();
724 710 redisTemplate.opsForValue().set(key, mediaServerItem);
  711 + resetOnlineServerItem(mediaServerItem);
725 712 clearRTPServer(mediaServerItem);
726 713 }
727 714 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId();
... ... @@ -750,15 +737,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
750 737 }
751 738  
752 739 @Override
753   - public boolean checkRtpServer(MediaServerItem mediaServerItem, String app, String stream) {
754   - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, stream);
755   - if(rtpInfo.getInteger("code") == 0){
756   - return rtpInfo.getBoolean("exist");
757   - }
758   - return false;
759   - }
760   -
761   - @Override
762 740 public MediaServerLoad getLoad(MediaServerItem mediaServerItem) {
763 741 MediaServerLoad result = new MediaServerLoad();
764 742 result.setId(mediaServerItem.getId());
... ... @@ -771,88 +749,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
771 749 }
772 750  
773 751 @Override
774   - public List<RecordFile> getRecords(String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
775   - Assert.notNull(app, "app不存在");
776   - Assert.notNull(stream, "stream不存在");
777   - Assert.notNull(startTime, "startTime不存在");
778   - Assert.notNull(endTime, "endTime不存在");
779   - Assert.notEmpty(mediaServerItems, "流媒体列表为空");
780   -
781   - CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];
782   - for (int i = 0; i < mediaServerItems.size(); i++) {
783   - completableFutures[i] = getRecordFilesForOne(app, stream, startTime, endTime, mediaServerItems.get(i));
784   - }
785   - List<RecordFile> result = new ArrayList<>();
786   - for (int i = 0; i < completableFutures.length; i++) {
787   - try {
788   - List<RecordFile> list = (List<RecordFile>) completableFutures[i].get();
789   - if (!list.isEmpty()) {
790   - for (int g = 0; g < list.size(); g++) {
791   - list.get(g).setMediaServerId(mediaServerItems.get(i).getId());
792   - }
793   - result.addAll(list);
794   - }
795   - } catch (InterruptedException e) {
796   - throw new RuntimeException(e);
797   - } catch (ExecutionException e) {
798   - throw new RuntimeException(e);
799   - }
800   - }
801   - Comparator<RecordFile> comparator = Comparator.comparing(RecordFile::getFileName);
802   - result.sort(comparator);
803   - return result;
804   - }
805   -
806   - @Override
807   - public List<String> getRecordDates(String app, String stream, int year, int month, List<MediaServerItem> mediaServerItems) {
808   - Assert.notNull(app, "app不存在");
809   - Assert.notNull(stream, "stream不存在");
810   - Assert.notEmpty(mediaServerItems, "流媒体列表为空");
811   - CompletableFuture[] completableFutures = new CompletableFuture[mediaServerItems.size()];
812   -
813   - for (int i = 0; i < mediaServerItems.size(); i++) {
814   - completableFutures[i] = getRecordDatesForOne(app, stream, year, month, mediaServerItems.get(i));
815   - }
816   - List<String> result = new ArrayList<>();
817   - CompletableFuture.allOf(completableFutures).join();
818   - for (CompletableFuture completableFuture : completableFutures) {
819   - try {
820   - List<String> list = (List<String>) completableFuture.get();
821   - result.addAll(list);
822   - } catch (InterruptedException e) {
823   - throw new RuntimeException(e);
824   - } catch (ExecutionException e) {
825   - throw new RuntimeException(e);
826   - }
827   - }
828   - Collections.sort(result);
829   - return result;
830   - }
831   -
832   - @Async
833   - public CompletableFuture<List<String>> getRecordDatesForOne(String app, String stream, int year, int month, MediaServerItem mediaServerItem) {
834   - JSONObject fileListJson = assistRESTfulUtils.getDateList(mediaServerItem, app, stream, year, month);
835   - if (fileListJson != null && !fileListJson.isEmpty()) {
836   - if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {
837   - JSONArray data = fileListJson.getJSONArray("data");
838   - return CompletableFuture.completedFuture(data.toJavaList(String.class));
839   - }
840   - }
841   - return CompletableFuture.completedFuture(new ArrayList<>());
842   - }
843   -
844   - @Async
845   - public CompletableFuture<List<RecordFile>> getRecordFilesForOne(String app, String stream, String startTime, String endTime, MediaServerItem mediaServerItem) {
846   - JSONObject fileListJson = assistRESTfulUtils.getFileList(mediaServerItem, 1, 100000000, app, stream, startTime, endTime);
847   - if (fileListJson != null && !fileListJson.isEmpty()) {
848   - if (fileListJson.getString("code") != null && fileListJson.getInteger("code") == 0) {
849   - JSONObject data = fileListJson.getJSONObject("data");
850   - JSONArray list = data.getJSONArray("list");
851   - if (list != null) {
852   - return CompletableFuture.completedFuture(list.toJavaList(RecordFile.class));
853   - }
854   - }
855   - }
856   - return CompletableFuture.completedFuture(new ArrayList<>());
  752 + public List<MediaServerItem> getAllWithAssistPort() {
  753 + return mediaServerMapper.queryAllWithAssistPort();
857 754 }
858 755 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
... ... @@ -64,7 +64,7 @@ public class MediaServiceImpl implements IMediaService {
64 64 if (data == null) {
65 65 return null;
66 66 }
67   - JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class);
  67 + JSONObject mediaJSON = data.getJSONObject(0);
68 68 JSONArray tracks = mediaJSON.getJSONArray("tracks");
69 69 if (authority) {
70 70 streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld, true);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
... ... @@ -169,7 +169,7 @@ public class PlatformServiceImpl implements IPlatformService {
169 169 dynamicTask.stop(registerTaskKey);
170 170 // 注销旧的
171 171 try {
172   - if (parentPlatformOld.isStatus()) {
  172 + if (parentPlatformOld.isStatus() && parentPlatformCatchOld != null) {
173 173 logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId());
174 174 commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
175 175 logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
... ... @@ -286,6 +286,7 @@ public class PlatformServiceImpl implements IPlatformService {
286 286 }
287 287 if (parentPlatform.isAutoPushChannel()) {
288 288 if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) {
  289 + logger.info("[国标级联]:{}, 添加自动通道推送模拟订阅信息", parentPlatform.getServerGBId());
289 290 addSimulatedSubscribeInfo(parentPlatform);
290 291 }
291 292 }else {
... ... @@ -363,9 +364,16 @@ public class PlatformServiceImpl implements IPlatformService {
363 364 // 清除心跳任务
364 365 dynamicTask.stop(keepaliveTaskKey);
365 366 }
366   - // 停止目录订阅回复
367   - logger.info("[平台离线] {}, 停止订阅回复", parentPlatform.getServerGBId());
368   - subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
  367 + // 停止订阅回复
  368 + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
  369 + if (catalogSubscribe != null) {
  370 + if (catalogSubscribe.getExpires() > 0) {
  371 + logger.info("[平台离线] {}, 停止目录订阅回复", parentPlatform.getServerGBId());
  372 + subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId());
  373 + }
  374 + }
  375 + logger.info("[平台离线] {}, 停止移动位置订阅回复", parentPlatform.getServerGBId());
  376 + subscribeHolder.removeMobilePositionSubscribe(parentPlatform.getServerGBId());
369 377 // 发起定时自动重新注册
370 378 if (!stopRegister) {
371 379 // 设置为60秒自动尝试重新注册
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
  3 +import com.alibaba.fastjson2.JSONArray;
3 4 import com.alibaba.fastjson2.JSONObject;
4 5 import com.genersoft.iot.vmp.common.InviteInfo;
5 6 import com.genersoft.iot.vmp.common.InviteSessionStatus;
... ... @@ -19,13 +20,19 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
19 20 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
20 21 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
21 22 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
  23 +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  24 +import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
  25 +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
  26 +import com.genersoft.iot.vmp.media.zlm.dto.*;
22 27 import com.genersoft.iot.vmp.media.zlm.*;
23 28 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
24 29 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
25 30 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
26 31 import com.genersoft.iot.vmp.media.zlm.dto.hook.HookParam;
  32 +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnRecordMp4HookParam;
27 33 import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
28 34 import com.genersoft.iot.vmp.service.*;
  35 +import com.genersoft.iot.vmp.service.bean.*;
29 36 import com.genersoft.iot.vmp.service.bean.ErrorCallback;
30 37 import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
31 38 import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg;
... ... @@ -33,6 +40,8 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
33 40 import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener;
34 41 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
35 42 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  43 +import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
  44 +import com.genersoft.iot.vmp.utils.CloudRecordUtils;
36 45 import com.genersoft.iot.vmp.utils.DateUtil;
37 46 import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
38 47 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
... ... @@ -90,12 +99,18 @@ public class PlayServiceImpl implements IPlayService {
90 99 private IInviteStreamService inviteStreamService;
91 100  
92 101 @Autowired
  102 + private ZlmHttpHookSubscribe subscribe;
  103 +
  104 + @Autowired
93 105 private SendRtpPortManager sendRtpPortManager;
94 106  
95 107 @Autowired
96 108 private ZLMRESTfulUtils zlmresTfulUtils;
97 109  
98 110 @Autowired
  111 + private ZLMServerFactory zlmServerFactory;
  112 +
  113 + @Autowired
99 114 private AssistRESTfulUtils assistRESTfulUtils;
100 115  
101 116 @Autowired
... ... @@ -117,7 +132,7 @@ public class PlayServiceImpl implements IPlayService {
117 132 private DynamicTask dynamicTask;
118 133  
119 134 @Autowired
120   - private ZlmHttpHookSubscribe subscribe;
  135 + private CloudRecordServiceMapper cloudRecordServiceMapper;
121 136  
122 137 @Autowired
123 138 private ISIPCommanderForPlatform commanderForPlatform;
... ... @@ -407,6 +422,15 @@ public class PlayServiceImpl implements IPlayService {
407 422 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
408 423 subscribe.removeSubscribe(hookSubscribe);
409 424 }
  425 + }else {
  426 + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},码流类型:{},端口:{}, SSRC: {}",
  427 + device.getDeviceId(), channelId, device.isSwitchPrimarySubStream() ? "辅码流" : "主码流",
  428 + ssrcInfo.getPort(), ssrcInfo.getSsrc());
  429 +
  430 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  431 +
  432 + mediaServerService.closeRTPServer(mediaServerItem.getId(), ssrcInfo.getStream());
  433 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
410 434 }
411 435 }, userSetting.getPlayTimeout());
412 436  
... ... @@ -437,6 +461,7 @@ public class PlayServiceImpl implements IPlayService {
437 461 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
438 462 timeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAY);
439 463 }, (event) -> {
  464 + logger.info("[点播失败] deviceId: {}, channelId:{}, {}: {}", device.getDeviceId(), channelId, event.statusCode, event.msg);
440 465 dynamicTask.stop(timeOutTaskKey);
441 466 mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
442 467 // 释放ssrc
... ... @@ -478,7 +503,13 @@ public class PlayServiceImpl implements IPlayService {
478 503 if (!device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
479 504 return;
480 505 }
481   - String substring = contentString.substring(0, contentString.indexOf("y="));
  506 +
  507 + String substring;
  508 + if (contentString.indexOf("y=") > 0) {
  509 + substring = contentString.substring(0, contentString.indexOf("y="));
  510 + }else {
  511 + substring = contentString;
  512 + }
482 513 try {
483 514 SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
484 515 int port = -1;
... ... @@ -598,23 +629,6 @@ public class PlayServiceImpl implements IPlayService {
598 629 }
599 630  
600 631 @Override
601   - public MediaServerItem getNewMediaServerItemHasAssist(Device device) {
602   - if (device == null) {
603   - return null;
604   - }
605   - MediaServerItem mediaServerItem;
606   - if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) {
607   - mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true);
608   - } else {
609   - mediaServerItem = mediaServerService.getOne(device.getMediaServerId());
610   - }
611   - if (mediaServerItem == null) {
612   - logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM...");
613   - }
614   - return mediaServerItem;
615   - }
616   -
617   - @Override
618 632 public void playBack(String deviceId, String channelId, String startTime,
619 633 String endTime, ErrorCallback<Object> callback) {
620 634 Device device = storager.queryVideoDevice(deviceId);
... ... @@ -711,7 +725,6 @@ public class PlayServiceImpl implements IPlayService {
711 725 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
712 726 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
713 727 playBackTimeOutTaskKey, callback, inviteInfo, InviteSessionType.PLAYBACK);
714   -
715 728 }, errorEvent);
716 729 } catch (InvalidArgumentException | SipException | ParseException e) {
717 730 logger.error("[命令发送失败] 录像回放: {}", e.getMessage());
... ... @@ -732,6 +745,10 @@ public class PlayServiceImpl implements IPlayService {
732 745 ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
733 746 String contentString = new String(responseEvent.getResponse().getRawContent());
734 747 String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
  748 + // 兼容回复的消息中缺少ssrc(y字段)的情况
  749 + if (ssrcInResponse == null) {
  750 + ssrcInResponse = ssrcInfo.getSsrc();
  751 + }
735 752 if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
736 753 // ssrc 一致
737 754 if (mediaServerItem.isRtpEnable()) {
... ... @@ -809,13 +826,15 @@ public class PlayServiceImpl implements IPlayService {
809 826 }
810 827  
811 828  
  829 +
  830 +
812 831 @Override
813 832 public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, ErrorCallback<Object> callback) {
814 833 Device device = storager.queryVideoDevice(deviceId);
815 834 if (device == null) {
816 835 return;
817 836 }
818   - MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device);
  837 + MediaServerItem newMediaServerItem = this.getNewMediaServerItem(device);
819 838 if (newMediaServerItem == null) {
820 839 callback.run(InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getCode(),
821 840 InviteErrorCode.ERROR_FOR_ASSIST_NOT_READY.getMsg(),
... ... @@ -894,6 +913,28 @@ public class PlayServiceImpl implements IPlayService {
894 913 // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题
895 914 InviteOKHandler(eventResult, ssrcInfo, mediaServerItem, device, channelId,
896 915 downLoadTimeOutTaskKey, callback, inviteInfo, InviteSessionType.DOWNLOAD);
  916 +
  917 + // 注册录像回调事件,录像下载结束后写入下载地址
  918 + ZlmHttpHookSubscribe.Event hookEventForRecord = (mediaServerItemInuse, hookParam) -> {
  919 + logger.info("[录像下载] 收到录像写入磁盘消息: , {}/{}-{}",
  920 + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), ssrcInfo.getStream());
  921 + logger.info("[录像下载] 收到录像写入磁盘消息内容: " + hookParam);
  922 + OnRecordMp4HookParam recordMp4HookParam = (OnRecordMp4HookParam)hookParam;
  923 + String filePath = recordMp4HookParam.getFile_path();
  924 + DownloadFileInfo downloadFileInfo = CloudRecordUtils.getDownloadFilePath(mediaServerItem, filePath);
  925 + InviteInfo inviteInfoForNew = inviteStreamService.getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId()
  926 + , inviteInfo.getChannelId(), inviteInfo.getStream());
  927 + inviteInfoForNew.getStreamInfo().setDownLoadFilePath(downloadFileInfo);
  928 + inviteStreamService.updateInviteInfo(inviteInfoForNew);
  929 + };
  930 + HookSubscribeForRecordMp4 hookSubscribe = HookSubscribeFactory.on_record_mp4(
  931 + mediaServerItem.getId(), "rtp", ssrcInfo.getStream());
  932 +
  933 + // 设置过期时间,下载失败时自动处理订阅数据
  934 +// long difference = DateUtil.getDifference(startTime, endTime)/1000;
  935 +// Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(difference * 2));
  936 +// hookSubscribe.setExpires(expiresInstant);
  937 + subscribe.addSubscribe(hookSubscribe, hookEventForRecord);
897 938 });
898 939 } catch (InvalidArgumentException | SipException | ParseException e) {
899 940 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
... ... @@ -909,47 +950,71 @@ public class PlayServiceImpl implements IPlayService {
909 950 @Override
910 951 public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
911 952 InviteInfo inviteInfo = inviteStreamService.getInviteInfo(InviteSessionType.DOWNLOAD, deviceId, channelId, stream);
  953 + if (inviteInfo == null || inviteInfo.getStreamInfo() == null) {
  954 + logger.warn("[获取下载进度] 未查询到录像下载的信息");
  955 + return null;
  956 + }
912 957  
913   - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) {
914   - if (inviteInfo.getStreamInfo().getProgress() == 1) {
915   - return inviteInfo.getStreamInfo();
916   - }
  958 + if (inviteInfo.getStreamInfo().getProgress() == 1) {
  959 + return inviteInfo.getStreamInfo();
  960 + }
917 961  
918   - // 获取当前已下载时长
919   - String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
920   - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
921   - if (mediaServerItem == null) {
922   - logger.warn("查询录像信息时发现节点已离线");
923   - return null;
924   - }
925   - if (mediaServerItem.getRecordAssistPort() > 0) {
926   - JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream(), null);
927   - if (jsonObject == null) {
928   - throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败");
929   - }
930   - if (jsonObject.getInteger("code") == 0) {
931   - long duration = jsonObject.getLong("data");
  962 + // 获取当前已下载时长
  963 + String mediaServerId = inviteInfo.getStreamInfo().getMediaServerId();
  964 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  965 + if (mediaServerItem == null) {
  966 + logger.warn("[获取下载进度] 查询录像信息时发现节点不存在");
  967 + return null;
  968 + }
  969 + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId, null, stream);
932 970  
933   - if (duration == 0) {
934   - inviteInfo.getStreamInfo().setProgress(0);
935   - } else {
936   - String startTime = inviteInfo.getStreamInfo().getStartTime();
937   - String endTime = inviteInfo.getStreamInfo().getEndTime();
938   - long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
939   - long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
940   -
941   - BigDecimal currentCount = new BigDecimal(duration / 1000);
942   - BigDecimal totalCount = new BigDecimal(end - start);
943   - BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
944   - double process = divide.doubleValue();
945   - inviteInfo.getStreamInfo().setProgress(process);
946   - }
947   - inviteStreamService.updateInviteInfo(inviteInfo);
948   - }
  971 + if (ssrcTransaction == null) {
  972 + logger.warn("[获取下载进度] 下载已结束");
  973 + return null;
  974 + }
  975 +
  976 + JSONObject mediaListJson= zlmresTfulUtils.getMediaList(mediaServerItem, "rtp", stream);
  977 + if (mediaListJson == null) {
  978 + logger.warn("[获取下载进度] 从zlm查询进度失败");
  979 + return null;
  980 + }
  981 + if (mediaListJson.getInteger("code") != 0) {
  982 + logger.warn("[获取下载进度] 从zlm查询进度出现错误: {}", mediaListJson.getString("msg"));
  983 + return null;
  984 + }
  985 + JSONArray data = mediaListJson.getJSONArray("data");
  986 + if (data == null) {
  987 + logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
  988 + return null;
  989 + }
  990 + JSONObject mediaJSON = data.getJSONObject(0);
  991 + JSONArray tracks = mediaJSON.getJSONArray("tracks");
  992 + if (tracks.isEmpty()) {
  993 + logger.warn("[获取下载进度] 从zlm查询进度时未返回数据");
  994 + return null;
  995 + }
  996 + JSONObject jsonObject = tracks.getJSONObject(0);
  997 + long duration = jsonObject.getLongValue("duration");
  998 + if (duration == 0) {
  999 + inviteInfo.getStreamInfo().setProgress(0);
  1000 + } else {
  1001 + String startTime = inviteInfo.getStreamInfo().getStartTime();
  1002 + String endTime = inviteInfo.getStreamInfo().getEndTime();
  1003 + // 此时start和end单位是秒
  1004 + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  1005 + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  1006 +
  1007 + BigDecimal currentCount = new BigDecimal(duration);
  1008 + BigDecimal totalCount = new BigDecimal((end - start) * 1000);
  1009 + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP);
  1010 + double process = divide.doubleValue();
  1011 + if (process > 0.999) {
  1012 + process = 1.0;
949 1013 }
950   - return inviteInfo.getStreamInfo();
  1014 + inviteInfo.getStreamInfo().setProgress(process);
951 1015 }
952   - return null;
  1016 + inviteStreamService.updateInviteInfo(inviteInfo);
  1017 + return inviteInfo.getStreamInfo();
953 1018 }
954 1019  
955 1020 private StreamInfo onPublishHandlerForDownload(MediaServerItem mediaServerItemInuse, HookParam hookParam, String deviceId, String channelId, String startTime, String endTime) {
... ... @@ -1219,7 +1284,12 @@ public class PlayServiceImpl implements IPlayService {
1219 1284 throw new ServiceException("mediaServer不存在");
1220 1285 }
1221 1286 // zlm 暂停RTP超时检查
1222   - JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamId);
  1287 + // 使用zlm中的流ID
  1288 + String streamKey = inviteInfo.getStream();
  1289 + if (!mediaServerItem.isRtpEnable()) {
  1290 + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
  1291 + }
  1292 + JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamKey);
1223 1293 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1224 1294 throw new ServiceException("暂停RTP接收失败");
1225 1295 }
... ... @@ -1242,7 +1312,12 @@ public class PlayServiceImpl implements IPlayService {
1242 1312 throw new ServiceException("mediaServer不存在");
1243 1313 }
1244 1314 // zlm 暂停RTP超时检查
1245   - JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamId);
  1315 + // 使用zlm中的流ID
  1316 + String streamKey = inviteInfo.getStream();
  1317 + if (!mediaServerItem.isRtpEnable()) {
  1318 + streamKey = Long.toHexString(Long.parseLong(inviteInfo.getSsrcInfo().getSsrc())).toUpperCase();
  1319 + }
  1320 + JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamKey);
1246 1321 if (jsonObject == null || jsonObject.getInteger("code") != 0) {
1247 1322 throw new ServiceException("继续RTP接收失败");
1248 1323 }
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
... ... @@ -126,7 +126,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
126 126 }
127 127 JSONArray dataArray = jsonObject.getJSONArray("data");
128 128 JSONObject mediaServerConfig = dataArray.getJSONObject(0);
  129 + if (ObjectUtils.isEmpty(param.getFfmpegCmdKey())) {
  130 + param.setFfmpegCmdKey("ffmpeg.cmd");
  131 + }
129 132 String ffmpegCmd = mediaServerConfig.getString(param.getFfmpegCmdKey());
  133 + if (ffmpegCmd == null) {
  134 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法获取ffmpeg cmd");
  135 + }
130 136 String schema = getSchemaFromFFmpegCmd(ffmpegCmd);
131 137 if (schema == null) {
132 138 throw new ControllerException(ErrorCode.ERROR100.getCode(), "ffmpeg拉流代理无法从ffmpeg cmd中获取到输出格式");
... ... @@ -401,6 +407,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
401 407 logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"),
402 408 streamProxy.getSrcUrl() == null? streamProxy.getUrl():streamProxy.getSrcUrl());
403 409 }
  410 + } else if (streamProxy != null && streamProxy.isEnable()) {
  411 + return true ;
404 412 }
405 413 return result;
406 414 }
... ... @@ -452,7 +460,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
452 460 streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId);
453 461  
454 462 // 移除拉流代理生成的流信息
455   -// syncPullStream(mediaServerId);
  463 + syncPullStream(mediaServerId);
456 464  
457 465 // 恢复流代理, 只查找这个这个流媒体
458 466 List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer(
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
... ... @@ -282,6 +282,8 @@ public class StreamPushServiceImpl implements IStreamPushService {
282 282 redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
283 283 // 移除redis内流的信息
284 284 redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream());
  285 + // 冗余数据,自己系统中自用
  286 + redisCatchStorage.removePushListItem(offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream(), mediaServerItem.getId());
285 287 }
286 288 }
287 289  
... ... @@ -319,6 +321,9 @@ public class StreamPushServiceImpl implements IStreamPushService {
319 321 jsonObject.put("register", false);
320 322 jsonObject.put("mediaServerId", mediaServerId);
321 323 redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
  324 +
  325 + // 冗余数据,自己系统中自用
  326 + redisCatchStorage.removePushListItem(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), mediaServerId);
322 327 }
323 328 }
324 329 }
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
... ... @@ -113,8 +113,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
113 113 while (!taskQueue.isEmpty()) {
114 114 Message msg = taskQueue.poll();
115 115 try {
116   - JSONObject msgJSON = JSON.parseObject(msg.getBody(), JSONObject.class);
117   - WvpRedisMsg wvpRedisMsg = JSON.to(WvpRedisMsg.class, msgJSON);
  116 + WvpRedisMsg wvpRedisMsg = JSON.parseObject(msg.getBody(), WvpRedisMsg.class);
  117 + logger.info("[收到REDIS通知] 消息: {}", JSON.toJSONString(wvpRedisMsg));
118 118 if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) {
119 119 continue;
120 120 }
... ... @@ -123,7 +123,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
123 123  
124 124 switch (wvpRedisMsg.getCmd()){
125 125 case WvpRedisMsgCmd.GET_SEND_ITEM:
126   - RequestSendItemMsg content = JSON.to(RequestSendItemMsg.class, wvpRedisMsg.getContent());
  126 + RequestSendItemMsg content = JSON.parseObject(wvpRedisMsg.getContent(), RequestSendItemMsg.class);
127 127 requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial());
128 128 break;
129 129 case WvpRedisMsgCmd.REQUEST_PUSH_STREAM:
... ... @@ -242,7 +242,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
242 242 result.setData(content);
243 243  
244 244 WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
245   - WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result);
  245 + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, JSON.toJSONString(result));
246 246 JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
247 247 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
248 248 }
... ... @@ -260,7 +260,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
260 260 result.setMsg("流媒体不存在");
261 261  
262 262 WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId,
263   - WvpRedisMsgCmd.GET_SEND_ITEM, serial, result);
  263 + WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result));
264 264  
265 265 JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
266 266 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
... ... @@ -283,7 +283,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
283 283 WVPResult<SendRtpItem> result = new WVPResult<>();
284 284 result.setCode(ERROR_CODE_TIMEOUT);
285 285 WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
286   - userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
  286 + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
287 287 );
288 288 JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
289 289 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
... ... @@ -324,7 +324,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
324 324 result.setData(responseSendItemMsg);
325 325  
326 326 WvpRedisMsg response = WvpRedisMsg.getResponseInstance(
327   - userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result
  327 + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, JSON.toJSONString(result)
328 328 );
329 329 JSONObject jsonObject = (JSONObject)JSON.toJSON(response);
330 330 redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject);
... ... @@ -350,7 +350,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
350 350 requestSendItemMsg.setServerId(serverId);
351 351 String key = UUID.randomUUID().toString();
352 352 WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM,
353   - key, requestSendItemMsg);
  353 + key, JSON.toJSONString(requestSendItemMsg));
354 354  
355 355 JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
356 356 logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject);
... ... @@ -375,7 +375,7 @@ public class RedisGbPlayMsgListener implements MessageListener {
375 375 public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) {
376 376 String key = UUID.randomUUID().toString();
377 377 WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId,
378   - WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param);
  378 + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, JSON.toJSONString(param));
379 379  
380 380 JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg);
381 381 logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject);
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
... ... @@ -50,11 +50,12 @@ public class RedisGpsMsgListener implements MessageListener {
50 50 Message msg = taskQueue.poll();
51 51 try {
52 52 GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class);
  53 + logger.info("[REDIS的位置变化通知], {}", JSON.toJSONString(gpsMsgInfo));
53 54 // 只是放入redis缓存起来
54 55 redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
55 56 }catch (Exception e) {
56   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
57   - logger.error("[REDIS的ALARM通知] 异常内容: ", e);
  57 + logger.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  58 + logger.error("[REDIS的位置变化通知] 异常内容: ", e);
58 59 }
59 60 }
60 61 });
... ...
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
... ... @@ -208,4 +208,8 @@ public interface IRedisCatchStorage {
208 208 void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel);
209 209  
210 210 void sendPlatformStopPlayMsg(MessageForPushChannel messageForPushChannel);
  211 +
  212 + void addPushListItem(String app, String stream, OnStreamChangedHookParam param);
  213 +
  214 + void removePushListItem(String app, String stream, String mediaServerId);
211 215 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/CloudRecordServiceMapper.java 0 → 100644
  1 +package com.genersoft.iot.vmp.storager.dao;
  2 +
  3 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  4 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  5 +import org.apache.ibatis.annotations.*;
  6 +
  7 +import java.util.List;
  8 +
  9 +@Mapper
  10 +public interface CloudRecordServiceMapper {
  11 +
  12 + @Insert(" <script>" +
  13 + "INSERT INTO wvp_cloud_record (" +
  14 + " app," +
  15 + " stream," +
  16 + "<if test=\"callId != null\"> call_id,</if>" +
  17 + " start_time," +
  18 + " end_time," +
  19 + " media_server_id," +
  20 + " file_name," +
  21 + " folder," +
  22 + " file_path," +
  23 + " file_size," +
  24 + " time_len ) " +
  25 + "VALUES (" +
  26 + " #{app}," +
  27 + " #{stream}," +
  28 + " <if test=\"callId != null\"> #{callId},</if>" +
  29 + " #{startTime}," +
  30 + " #{endTime}," +
  31 + " #{mediaServerId}," +
  32 + " #{fileName}," +
  33 + " #{folder}," +
  34 + " #{filePath}," +
  35 + " #{fileSize}," +
  36 + " #{timeLen})" +
  37 + " </script>")
  38 + int add(CloudRecordItem cloudRecordItem);
  39 +
  40 + @Select(" <script>" +
  41 + "select * " +
  42 + " from wvp_cloud_record " +
  43 + " where 0 = 0" +
  44 + " <if test='query != null'> AND (app LIKE concat('%',#{query},'%') OR stream LIKE concat('%',#{query},'%') )</if> " +
  45 + " <if test= 'app != null '> and app=#{app}</if>" +
  46 + " <if test= 'stream != null '> and stream=#{stream}</if>" +
  47 + " <if test= 'startTimeStamp != null '> and end_time &gt;= #{startTimeStamp}</if>" +
  48 + " <if test= 'endTimeStamp != null '> and start_time &lt;= #{endTimeStamp}</if>" +
  49 + " <if test= 'callId != null '> and call_id = #{callId}</if>" +
  50 + " <if test= 'mediaServerItemList != null ' > and media_server_id in " +
  51 + " <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
  52 + " </if>" +
  53 + " order by start_time DESC" +
  54 +
  55 + " </script>")
  56 + List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
  57 + @Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
  58 + @Param("callId")String callId, List<MediaServerItem> mediaServerItemList);
  59 +
  60 +
  61 + @Select(" <script>" +
  62 + "select file_path" +
  63 + " from wvp_cloud_record " +
  64 + " where 0 = 0" +
  65 + " <if test= 'app != null '> and app=#{app}</if>" +
  66 + " <if test= 'stream != null '> and stream=#{stream}</if>" +
  67 + " <if test= 'startTimeStamp != null '> and end_time &gt;= #{startTimeStamp}</if>" +
  68 + " <if test= 'endTimeStamp != null '> and start_time &lt;= #{endTimeStamp}</if>" +
  69 + " <if test= 'callId != null '> and call_id = #{callId}</if>" +
  70 + " <if test= 'mediaServerItemList != null ' > and media_server_id in " +
  71 + " <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
  72 + " </if>" +
  73 + " </script>")
  74 + List<String> queryRecordFilePathList(@Param("app") String app, @Param("stream") String stream,
  75 + @Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
  76 + @Param("callId")String callId, List<MediaServerItem> mediaServerItemList);
  77 +
  78 + @Update(" <script>" +
  79 + "update wvp_cloud_record set collect = #{collect} where file_path in " +
  80 + " <foreach collection='cloudRecordItemList' item='item' open='(' separator=',' close=')' > #{item.filePath}</foreach>" +
  81 + " </script>")
  82 + int updateCollectList(@Param("collect") boolean collect, List<CloudRecordItem> cloudRecordItemList);
  83 +
  84 + @Delete(" <script>" +
  85 + "delete from wvp_cloud_record where media_server_id=#{mediaServerId} and file_path in " +
  86 + " <foreach collection='filePathList' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
  87 + " </script>")
  88 + void deleteByFileList(List<String> filePathList, @Param("mediaServerId") String mediaServerId);
  89 +
  90 +
  91 + @Select(" <script>" +
  92 + "select *" +
  93 + " from wvp_cloud_record " +
  94 + " where collect = false and end_time &lt;= #{endTimeStamp} and media_server_id = #{mediaServerId} " +
  95 + " </script>")
  96 + List<CloudRecordItem> queryRecordListForDelete(@Param("endTimeStamp")Long endTimeStamp, String mediaServerId);
  97 +
  98 + @Update(" <script>" +
  99 + "update wvp_cloud_record set collect = #{collect} where id = #{recordId} " +
  100 + " </script>")
  101 + int changeCollectById(@Param("collect") boolean collect, @Param("recordId") Integer recordId);
  102 +
  103 + @Delete(" <script>" +
  104 + "delete from wvp_cloud_record where id in " +
  105 + " <foreach collection='cloudRecordItemIdList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
  106 + " </script>")
  107 + int deleteList(List<CloudRecordItem> cloudRecordItemIdList);
  108 +
  109 + @Select(" <script>" +
  110 + "select *" +
  111 + " from wvp_cloud_record " +
  112 + "where call_id = #{callId}" +
  113 + " </script>")
  114 + List<CloudRecordItem> getListByCallId(@Param("callId") String callId);
  115 +
  116 + @Select(" <script>" +
  117 + "select *" +
  118 + " from wvp_cloud_record " +
  119 + "where id = #{id}" +
  120 + " </script>")
  121 + CloudRecordItem queryOne(@Param("id") Integer id);
  122 +}
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
... ... @@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
6 6 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
7 7 import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
8 8 import org.apache.ibatis.annotations.*;
9   -import org.apache.ibatis.annotations.Param;
10 9 import org.springframework.stereotype.Repository;
11 10  
12 11 import java.util.List;
... ... @@ -31,7 +30,7 @@ public interface DeviceChannelMapper {
31 30 @Update(value = {" <script>" +
32 31 "UPDATE wvp_device_channel " +
33 32 "SET update_time=#{updateTime}" +
34   - "<if test='name != null'>, name=#{name}</if>" +
  33 + ", custom_name=#{name}" +
35 34 "<if test='manufacture != null'>, manufacture=#{manufacture}</if>" +
36 35 "<if test='model != null'>, model=#{model}</if>" +
37 36 "<if test='owner != null'>, owner=#{owner}</if>" +
... ... @@ -49,12 +48,12 @@ public interface DeviceChannelMapper {
49 48 "<if test='ipAddress != null'>, ip_address=#{ipAddress}</if>" +
50 49 "<if test='port != null'>, port=#{port}</if>" +
51 50 "<if test='password != null'>, password=#{password}</if>" +
52   - "<if test='PTZType != null'>, ptz_type=#{PTZType}</if>" +
  51 + "<if test='PTZType != null'>, custom_ptz_type=#{PTZType}</if>" +
53 52 "<if test='status != null'>, status=#{status}</if>" +
54 53 "<if test='streamId != null'>, stream_id=#{streamId}</if>" +
55 54 "<if test='hasAudio != null'>, has_audio=#{hasAudio}</if>" +
56   - "<if test='longitude != null'>, longitude=#{longitude}</if>" +
57   - "<if test='latitude != null'>, latitude=#{latitude}</if>" +
  55 + ", custom_longitude=#{longitude}" +
  56 + ", custom_latitude=#{latitude}" +
58 57 "<if test='longitudeGcj02 != null'>, longitude_gcj02=#{longitudeGcj02}</if>" +
59 58 "<if test='latitudeGcj02 != null'>, latitude_gcj02=#{latitudeGcj02}</if>" +
60 59 "<if test='longitudeWgs84 != null'>, longitude_wgs84=#{longitudeWgs84}</if>" +
... ... @@ -67,7 +66,43 @@ public interface DeviceChannelMapper {
67 66  
68 67 @Select(value = {" <script>" +
69 68 "SELECT " +
70   - "dc.* " +
  69 + "dc.id, " +
  70 + "dc.channel_id, " +
  71 + "COALESCE(dc.custom_name, dc.name) AS name, " +
  72 + "dc.manufacture, " +
  73 + "dc.model, " +
  74 + "dc.owner, " +
  75 + "dc.civil_code, " +
  76 + "dc.block, " +
  77 + "dc.address, " +
  78 + "dc.parent_id, " +
  79 + "dc.safety_way, " +
  80 + "dc.register_way, " +
  81 + "dc.cert_num, " +
  82 + "dc.certifiable, " +
  83 + "dc.err_code, " +
  84 + "dc.end_time, " +
  85 + "dc.secrecy, " +
  86 + "dc.ip_address, " +
  87 + "dc.port, " +
  88 + "dc.password, " +
  89 + "COALESCE(dc.custom_ptz_type, dc.ptz_type) AS ptz_type, " +
  90 + "dc.status, " +
  91 + "COALESCE(dc.custom_longitude, dc.longitude) AS longitude, " +
  92 + "COALESCE(dc.custom_latitude, dc.latitude) AS latitude, " +
  93 + "dc.stream_id, " +
  94 + "dc.device_id, " +
  95 + "dc.parental, " +
  96 + "dc.has_audio, " +
  97 + "dc.create_time, " +
  98 + "dc.update_time, " +
  99 + "dc.sub_count, " +
  100 + "dc.longitude_gcj02, " +
  101 + "dc.latitude_gcj02, " +
  102 + "dc.longitude_wgs84, " +
  103 + "dc.latitude_wgs84, " +
  104 + "dc.business_group_id, " +
  105 + "dc.gps_time " +
71 106 "from " +
72 107 "wvp_device_channel dc " +
73 108 "WHERE " +
... ... @@ -154,7 +189,7 @@ public interface DeviceChannelMapper {
154 189 " dc.id,\n" +
155 190 " dc.channel_id,\n" +
156 191 " dc.device_id,\n" +
157   - " dc.name,\n" +
  192 + " COALESCE(dc.custom_name, dc.name) AS name,\n" +
158 193 " de.manufacturer,\n" +
159 194 " de.host_address,\n" +
160 195 " dc.sub_count,\n" +
... ... @@ -392,10 +427,10 @@ public interface DeviceChannelMapper {
392 427 @Select("select * from wvp_device_channel where device_id=#{deviceId} and SUBSTRING(channel_id, 11, 3)=#{typeCode}")
393 428 List<DeviceChannel> getBusinessGroups(@Param("deviceId") String deviceId, @Param("typeCode") String typeCode);
394 429  
395   - @Select("select dc.id, dc.channel_id, dc.device_id, dc.name, dc.manufacture,dc.model,dc.owner, pc.civil_code,dc.block, " +
  430 + @Select("select dc.id, dc.channel_id, dc.device_id, COALESCE(dc.custom_name, dc.name) AS name, dc.manufacture,dc.model,dc.owner, pc.civil_code,dc.block, " +
396 431 " dc.address, '0' as parental,'0' as channel_type, pc.id as parent_id, dc.safety_way, dc.register_way,dc.cert_num, dc.certifiable, " +
397   - " dc.err_code,dc.end_time, dc.secrecy, dc.ip_address, dc.port, dc.ptz_type, dc.password, dc.status, " +
398   - " dc.longitude_wgs84 as longitude, dc.latitude_wgs84 as latitude, pc.business_group_id " +
  432 + " dc.err_code,dc.end_time, dc.secrecy, dc.ip_address, dc.port, COALESCE(dc.custom_ptz_type, dc.ptz_type) AS ptz_type, dc.password, dc.status, " +
  433 + " COALESCE(dc.custom_longitude, dc.longitude) AS longitude, COALESCE(dc.custom_latitude, dc.latitude) AS latitude, pc.business_group_id " +
399 434 " from wvp_device_channel dc" +
400 435 " LEFT JOIN wvp_platform_gb_channel pgc on dc.id = pgc.device_channel_id" +
401 436 " LEFT JOIN wvp_platform_catalog pc on pgc.catalog_id = pc.id and pgc.platform_id = pc.platform_id" +
... ... @@ -457,7 +492,44 @@ public interface DeviceChannelMapper {
457 492 void clearPlay(String deviceId);
458 493 // 设备主子码流逻辑END
459 494 @Select(value = {" <script>" +
460   - "select * " +
  495 + "SELECT id,\n" +
  496 + " channel_id,\n" +
  497 + " COALESCE(custom_name, name) AS name,\n" +
  498 + " custom_name,\n" +
  499 + " manufacture,\n" +
  500 + " model,\n" +
  501 + " owner,\n" +
  502 + " civil_code,\n" +
  503 + " block,\n" +
  504 + " address,\n" +
  505 + " parent_id,\n" +
  506 + " safety_way,\n" +
  507 + " register_way,\n" +
  508 + " cert_num,\n" +
  509 + " certifiable,\n" +
  510 + " err_code,\n" +
  511 + " end_time,\n" +
  512 + " secrecy,\n" +
  513 + " ip_address,\n" +
  514 + " port,\n" +
  515 + " password,\n" +
  516 + " COALESCE(custom_ptz_type, ptz_type) AS ptz_type,\n" +
  517 + " status,\n" +
  518 + " COALESCE(custom_longitude, longitude) AS longitude,\n" +
  519 + " COALESCE(custom_latitude, latitude) AS latitude,\n" +
  520 + " stream_id,\n" +
  521 + " device_id,\n" +
  522 + " parental,\n" +
  523 + " has_audio,\n" +
  524 + " create_time,\n" +
  525 + " update_time,\n" +
  526 + " sub_count,\n" +
  527 + " longitude_gcj02,\n" +
  528 + " latitude_gcj02,\n" +
  529 + " longitude_wgs84,\n" +
  530 + " latitude_wgs84,\n" +
  531 + " business_group_id,\n" +
  532 + " gps_time\n" +
461 533 "from wvp_device_channel " +
462 534 "where device_id=#{deviceId}" +
463 535 " <if test='parentId != null and parentId != deviceId'> and parent_id = #{parentId} </if>" +
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
... ... @@ -158,7 +158,7 @@ public interface GbStreamMapper {
158 158 " <foreach collection='list' item='item' index='index' separator=';'>"+
159 159 "UPDATE wvp_gb_stream " +
160 160 " SET name=#{item.name},"+
161   - " gb_id=#{item.gb_id}"+
  161 + " gb_id=#{item.gbId}"+
162 162 " WHERE app=#{item.app} and stream=#{item.stream}"+
163 163 "</foreach>"+
164 164 "</script>")
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
... ... @@ -31,6 +31,8 @@ public interface MediaServerMapper {
31 31 "rtp_port_range,"+
32 32 "send_rtp_port_range,"+
33 33 "record_assist_port,"+
  34 + "record_day,"+
  35 + "record_path,"+
34 36 "default_server,"+
35 37 "create_time,"+
36 38 "update_time,"+
... ... @@ -55,6 +57,8 @@ public interface MediaServerMapper {
55 57 "#{rtpPortRange}, " +
56 58 "#{sendRtpPortRange}, " +
57 59 "#{recordAssistPort}, " +
  60 + "#{recordDay}, " +
  61 + "#{recordPath}, " +
58 62 "#{defaultServer}, " +
59 63 "#{createTime}, " +
60 64 "#{updateTime}, " +
... ... @@ -82,6 +86,8 @@ public interface MediaServerMapper {
82 86 "<if test=\"secret != null\">, secret=#{secret}</if>" +
83 87 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
84 88 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
  89 + "<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
  90 + "<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
85 91 "WHERE id=#{id}"+
86 92 " </script>"})
87 93 int update(MediaServerItem mediaServerItem);
... ... @@ -105,6 +111,8 @@ public interface MediaServerMapper {
105 111 "<if test=\"sendRtpPortRange != null\">, send_rtp_port_range=#{sendRtpPortRange}</if>" +
106 112 "<if test=\"secret != null\">, secret=#{secret}</if>" +
107 113 "<if test=\"recordAssistPort != null\">, record_assist_port=#{recordAssistPort}</if>" +
  114 + "<if test=\"recordDay != null\">, record_day=#{recordDay}</if>" +
  115 + "<if test=\"recordPath != null\">, record_path=#{recordPath}</if>" +
108 116 "<if test=\"hookAliveInterval != null\">, hook_alive_interval=#{hookAliveInterval}</if>" +
109 117 "WHERE ip=#{ip} and http_port=#{httpPort}"+
110 118 " </script>"})
... ... @@ -130,4 +138,8 @@ public interface MediaServerMapper {
130 138  
131 139 @Select("SELECT * FROM wvp_media_server WHERE default_server=true")
132 140 MediaServerItem queryDefault();
  141 +
  142 + @Select("SELECT * FROM wvp_media_server WHERE record_assist_port > 0")
  143 + List<MediaServerItem> queryAllWithAssistPort();
  144 +
133 145 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
... ... @@ -117,8 +117,6 @@ public interface PlatformChannelMapper {
117 117 "where dc.channel_id = #{channelId} and pgc.platform_id=#{platformId}")
118 118 List<Device> queryDeviceInfoByPlatformIdAndChannelId(@Param("platformId") String platformId, @Param("channelId") String channelId);
119 119  
120   - @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id='${channelId}'")
121   - List<String> queryParentPlatformByChannelId(String channelId);
122   -
123   -
  120 + @Select("SELECT pgc.platform_id from wvp_platform_gb_channel pgc left join wvp_device_channel dc on dc.id = pgc.device_channel_id WHERE dc.channel_id=#{channelId}")
  121 + List<String> queryParentPlatformByChannelId(@Param("channelId") String channelId);
124 122 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java
... ... @@ -103,6 +103,9 @@ public interface PlatformGbStreamMapper {
103 103 "</script>")
104 104 void delByAppAndStreamsByPlatformId(@Param("gbStreams") List<GbStream> gbStreams, @Param("platformId") String platformId);
105 105  
106   - @Delete("DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId} and catalog_id=#{catalogId}")
  106 + @Delete("<script> "+
  107 + "DELETE from wvp_platform_gb_stream WHERE platform_id=#{platformId}" +
  108 + " <if test='catalogId != null' > and catalog_id=#{catalogId}</if>" +
  109 + "</script>")
107 110 int delByPlatformAndCatalogId(@Param("platformId") String platformId, @Param("catalogId") String catalogId);
108 111 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
... ... @@ -13,9 +13,9 @@ import java.util.List;
13 13 public interface StreamPushMapper {
14 14  
15 15 @Insert("INSERT INTO wvp_stream_push (app, stream, total_reader_count, origin_type, origin_type_str, " +
16   - "push_time, alive_second, media_server_id, update_time, create_time, push_ing, self) VALUES" +
  16 + "push_time, alive_second, media_server_id, server_id, update_time, create_time, push_ing, self) VALUES" +
17 17 "(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " +
18   - "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{updateTime} , #{createTime}, " +
  18 + "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{serverId} , #{updateTime} , #{createTime}, " +
19 19 "#{pushIng}, #{self} )")
20 20 int add(StreamPushItem streamPushItem);
21 21  
... ... @@ -24,6 +24,7 @@ public interface StreamPushMapper {
24 24 "UPDATE wvp_stream_push " +
25 25 "SET update_time=#{updateTime}" +
26 26 "<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" +
  27 + "<if test=\"serverId != null\">, server_id=#{serverId}</if>" +
27 28 "<if test=\"totalReaderCount != null\">, total_reader_count=#{totalReaderCount}</if>" +
28 29 "<if test=\"originType != null\">, origin_type=#{originType}</if>" +
29 30 "<if test=\"originTypeStr != null\">, origin_type_str=#{originTypeStr}</if>" +
... ... @@ -89,10 +90,10 @@ public interface StreamPushMapper {
89 90  
90 91 @Insert("<script>" +
91 92 "Insert INTO wvp_stream_push (app, stream, total_reader_count, origin_type, origin_type_str, " +
92   - "create_time, alive_second, media_server_id, status, push_ing) " +
  93 + "create_time, alive_second, media_server_id, server_id, status, push_ing) " +
93 94 "VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" +
94 95 "( #{item.app}, #{item.stream}, #{item.totalReaderCount}, #{item.originType}, " +
95   - "#{item.originTypeStr},#{item.createTime}, #{item.aliveSecond}, #{item.mediaServerId}, #{item.status} ," +
  96 + "#{item.originTypeStr},#{item.createTime}, #{item.aliveSecond}, #{item.mediaServerId},#{item.serverId}, #{item.status} ," +
96 97 " #{item.pushIng} )" +
97 98 " </foreach>" +
98 99 "</script>")
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -609,14 +609,13 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
609 609 @Override
610 610 public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
611 611 String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
612   - logger.info("[redis通知] 发送 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online);
613 612 StringBuilder msg = new StringBuilder();
614 613 msg.append(deviceId);
615 614 if (channelId != null) {
616 615 msg.append(":").append(channelId);
617 616 }
618 617 msg.append(" ").append(online? "ON":"OFF");
619   - logger.info("[redis通知] 推送状态-> {} ", msg);
  618 + logger.info("[redis通知] 推送设备/通道状态-> {} ", msg);
620 619 // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
621 620 stringRedisTemplate.convertAndSend(key, msg.toString());
622 621 }
... ... @@ -650,4 +649,20 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
650 649 logger.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
651 650 redisTemplate.convertAndSend(key, JSON.toJSON(msg));
652 651 }
  652 +
  653 + @Override
  654 + public void addPushListItem(String app, String stream, OnStreamChangedHookParam param) {
  655 + String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
  656 + redisTemplate.opsForValue().set(key, param);
  657 + }
  658 +
  659 + @Override
  660 + public void removePushListItem(String app, String stream, String mediaServerId) {
  661 + String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
  662 + OnStreamChangedHookParam param = (OnStreamChangedHookParam)redisTemplate.opsForValue().get(key);
  663 + if (param != null && param.getMediaServerId().equalsIgnoreCase(mediaServerId)) {
  664 + redisTemplate.delete(key);
  665 + }
  666 +
  667 + }
653 668 }
... ...
src/main/java/com/genersoft/iot/vmp/utils/CloudRecordUtils.java 0 → 100644
  1 +package com.genersoft.iot.vmp.utils;
  2 +
  3 +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  4 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
  5 +
  6 +public class CloudRecordUtils {
  7 +
  8 + public static DownloadFileInfo getDownloadFilePath(MediaServerItem mediaServerItem, String filePath) {
  9 + DownloadFileInfo downloadFileInfo = new DownloadFileInfo();
  10 +
  11 + String pathTemplate = "%s://%s:%s/index/api/downloadFile?file_path=" + filePath;
  12 +
  13 + downloadFileInfo.setHttpPath(String.format(pathTemplate, "http", mediaServerItem.getStreamIp(),
  14 + mediaServerItem.getHttpPort()));
  15 +
  16 + if (mediaServerItem.getHttpSSlPort() > 0) {
  17 + downloadFileInfo.setHttpsPath(String.format(pathTemplate, "https", mediaServerItem.getStreamIp(),
  18 + mediaServerItem.getHttpSSlPort()));
  19 + }
  20 + return downloadFileInfo;
  21 + }
  22 +}
... ...
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
... ... @@ -40,11 +40,17 @@ public class DateUtil {
40 40 */
41 41 public static final String URL_PATTERN = "yyyyMMddHHmmss";
42 42  
  43 + /**
  44 + * 日期格式
  45 + */
  46 + public static final String date_PATTERN = "yyyy-MM-dd";
  47 +
43 48 public static final String zoneStr = "Asia/Shanghai";
44 49  
45 50 public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
46 51 public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
47 52 public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
  53 + public static final DateTimeFormatter DateFormatter = DateTimeFormatter.ofPattern(date_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
48 54 public static final DateTimeFormatter urlFormatter = DateTimeFormatter.ofPattern(URL_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr));
49 55  
50 56 public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
... ... @@ -72,6 +78,22 @@ public class DateUtil {
72 78 }
73 79  
74 80 /**
  81 + * 时间戳 转 yyyy_MM_dd_HH_mm_ss
  82 + */
  83 + public static String timestampTo_yyyy_MM_dd_HH_mm_ss(long timestamp) {
  84 + Instant instant = Instant.ofEpochSecond(timestamp);
  85 + return formatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
  86 + }
  87 +
  88 + /**
  89 + * 时间戳 转 yyyy_MM_dd
  90 + */
  91 + public static String timestampTo_yyyy_MM_dd(long timestamp) {
  92 + Instant instant = Instant.ofEpochMilli(timestamp);
  93 + return DateFormatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr)));
  94 + }
  95 +
  96 + /**
75 97 * 获取当前时间
76 98 * @return
77 99 */
... ... @@ -117,4 +139,13 @@ public class DateUtil {
117 139 Instant beforeInstant = Instant.from(formatter.parse(keepaliveTime));
118 140 return ChronoUnit.MILLIS.between(beforeInstant, Instant.now());
119 141 }
  142 +
  143 + public static long getDifference(String startTime, String endTime) {
  144 + if (ObjectUtils.isEmpty(startTime) || ObjectUtils.isEmpty(endTime)) {
  145 + return 0;
  146 + }
  147 + Instant startInstant = Instant.from(formatter.parse(startTime));
  148 + Instant endInstant = Instant.from(formatter.parse(endTime));
  149 + return ChronoUnit.MILLIS.between(endInstant, startInstant);
  150 + }
120 151 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java
1 1 package com.genersoft.iot.vmp.vmanager.bean;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
  4 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
4 5 import io.swagger.v3.oas.annotations.media.Schema;
5 6  
6 7 @Schema(description = "流信息")
... ... @@ -93,6 +94,9 @@ public class StreamContent {
93 94 @Schema(description = "结束时间")
94 95 private String endTime;
95 96  
  97 + @Schema(description = "文件下载地址(录像下载使用)")
  98 + private DownloadFileInfo downLoadFilePath;
  99 +
96 100 private double progress;
97 101  
98 102 public StreamContent(StreamInfo streamInfo) {
... ... @@ -170,6 +174,10 @@ public class StreamContent {
170 174 this.startTime = streamInfo.getStartTime();
171 175 this.endTime = streamInfo.getEndTime();
172 176 this.progress = streamInfo.getProgress();
  177 +
  178 + if (streamInfo.getDownLoadFilePath() != null) {
  179 + this.downLoadFilePath = streamInfo.getDownLoadFilePath();
  180 + }
173 181 }
174 182  
175 183 public String getApp() {
... ... @@ -411,4 +419,12 @@ public class StreamContent {
411 419 public void setProgress(double progress) {
412 420 this.progress = progress;
413 421 }
  422 +
  423 + public DownloadFileInfo getDownLoadFilePath() {
  424 + return downLoadFilePath;
  425 + }
  426 +
  427 + public void setDownLoadFilePath(DownloadFileInfo downLoadFilePath) {
  428 + this.downLoadFilePath = downLoadFilePath;
  429 + }
414 430 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java
1 1 package com.genersoft.iot.vmp.vmanager.cloudRecord;
2 2  
  3 +import com.alibaba.fastjson2.JSONArray;
3 4 import com.genersoft.iot.vmp.conf.DynamicTask;
4 5 import com.genersoft.iot.vmp.conf.UserSetting;
5 6 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  7 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
6 8 import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
7 9 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
8   -import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
9 10 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  11 +import com.genersoft.iot.vmp.service.ICloudRecordService;
10 12 import com.genersoft.iot.vmp.service.IMediaServerService;
  13 +import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
  14 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
11 15 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
12   -import com.genersoft.iot.vmp.vmanager.bean.PageInfo;
13   -import com.genersoft.iot.vmp.vmanager.bean.RecordFile;
  16 +import com.github.pagehelper.PageInfo;
14 17 import io.swagger.v3.oas.annotations.Operation;
15 18 import io.swagger.v3.oas.annotations.Parameter;
  19 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
16 20 import io.swagger.v3.oas.annotations.tags.Tag;
17 21 import org.apache.commons.lang3.ObjectUtils;
18 22 import org.slf4j.Logger;
... ... @@ -32,40 +36,27 @@ import java.util.List;
32 36 @RequestMapping("/api/cloud/record")
33 37 public class CloudRecordController {
34 38  
35   - @Autowired
36   - private ZLMServerFactory zlmServerFactory;
37   -
38   - @Autowired
39   - private SendRtpPortManager sendRtpPortManager;
40 39  
41 40 private final static Logger logger = LoggerFactory.getLogger(CloudRecordController.class);
42 41  
43 42 @Autowired
44   - private ZlmHttpHookSubscribe hookSubscribe;
  43 + private ICloudRecordService cloudRecordService;
45 44  
46 45 @Autowired
47 46 private IMediaServerService mediaServerService;
48 47  
49   - @Autowired
50   - private UserSetting userSetting;
51   -
52   - @Autowired
53   - private DynamicTask dynamicTask;
54   -
55   - @Autowired
56   - private RedisTemplate<Object, Object> redisTemplate;
57 48  
58 49 @ResponseBody
59 50 @GetMapping("/date/list")
60   - @Operation(summary = "查询存在云端录像的日期")
  51 + @Operation(summary = "查询存在云端录像的日期", security = @SecurityRequirement(name = JwtUtils.HEADER))
61 52 @Parameter(name = "app", description = "应用名", required = true)
62 53 @Parameter(name = "stream", description = "流ID", required = true)
63 54 @Parameter(name = "year", description = "年,置空则查询当年", required = false)
64 55 @Parameter(name = "month", description = "月,置空则查询当月", required = false)
65 56 @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部", required = false)
66 57 public List<String> openRtpServer(
67   - @RequestParam String app,
68   - @RequestParam String stream,
  58 + @RequestParam(required = true) String app,
  59 + @RequestParam(required = true) String stream,
69 60 @RequestParam(required = false) int year,
70 61 @RequestParam(required = false) int month,
71 62 @RequestParam(required = false) String mediaServerId
... ... @@ -95,26 +86,28 @@ public class CloudRecordController {
95 86 return new ArrayList<>();
96 87 }
97 88  
98   - return mediaServerService.getRecordDates(app, stream, year, month, mediaServerItems);
  89 + return cloudRecordService.getDateList(app, stream, year, month, mediaServerItems);
99 90 }
100 91  
101 92 @ResponseBody
102 93 @GetMapping("/list")
103   - @Operation(summary = "分页查询云端录像")
104   - @Parameter(name = "app", description = "应用名", required = true)
105   - @Parameter(name = "stream", description = "流ID", required = true)
106   - @Parameter(name = "page", description = "当前页", required = false)
107   - @Parameter(name = "count", description = "每页查询数量", required = false)
108   - @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = true)
109   - @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = true)
  94 + @Operation(summary = "分页查询云端录像", security = @SecurityRequirement(name = JwtUtils.HEADER))
  95 + @Parameter(name = "query", description = "检索内容", required = false)
  96 + @Parameter(name = "app", description = "应用名", required = false)
  97 + @Parameter(name = "stream", description = "流ID", required = false)
  98 + @Parameter(name = "page", description = "当前页", required = true)
  99 + @Parameter(name = "count", description = "每页查询数量", required = true)
  100 + @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
  101 + @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
110 102 @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
111   - public PageInfo<RecordFile> openRtpServer(
112   - @RequestParam String app,
113   - @RequestParam String stream,
  103 + public PageInfo<CloudRecordItem> openRtpServer(
  104 + @RequestParam(required = false) String query,
  105 + @RequestParam(required = false) String app,
  106 + @RequestParam(required = false) String stream,
114 107 @RequestParam int page,
115 108 @RequestParam int count,
116   - @RequestParam String startTime,
117   - @RequestParam String endTime,
  109 + @RequestParam(required = false) String startTime,
  110 + @RequestParam(required = false) String endTime,
118 111 @RequestParam(required = false) String mediaServerId
119 112  
120 113 ) {
... ... @@ -133,13 +126,128 @@ public class CloudRecordController {
133 126 mediaServerItems = mediaServerService.getAll();
134 127 }
135 128 if (mediaServerItems.isEmpty()) {
136   - return new PageInfo<>();
  129 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
  130 + }
  131 + if (query != null && ObjectUtils.isEmpty(query.trim())) {
  132 + query = null;
  133 + }
  134 + if (app != null && ObjectUtils.isEmpty(app.trim())) {
  135 + app = null;
  136 + }
  137 + if (stream != null && ObjectUtils.isEmpty(stream.trim())) {
  138 + stream = null;
  139 + }
  140 + if (startTime != null && ObjectUtils.isEmpty(startTime.trim())) {
  141 + startTime = null;
  142 + }
  143 + if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
  144 + endTime = null;
137 145 }
138   - List<RecordFile> records = mediaServerService.getRecords(app, stream, startTime, endTime, mediaServerItems);
139   - PageInfo<RecordFile> pageInfo = new PageInfo<>(records);
140   - pageInfo.startPage(page, count);
141   - return pageInfo;
  146 + return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServerItems);
142 147 }
143 148  
  149 + @ResponseBody
  150 + @GetMapping("/task/add")
  151 + @Operation(summary = "添加合并任务")
  152 + @Parameter(name = "app", description = "应用名", required = false)
  153 + @Parameter(name = "stream", description = "流ID", required = false)
  154 + @Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
  155 + @Parameter(name = "startTime", description = "鉴权ID", required = false)
  156 + @Parameter(name = "endTime", description = "鉴权ID", required = false)
  157 + @Parameter(name = "callId", description = "鉴权ID", required = false)
  158 + @Parameter(name = "remoteHost", description = "返回地址时的远程地址", required = false)
  159 + public String addTask(
  160 + @RequestParam(required = false) String app,
  161 + @RequestParam(required = false) String stream,
  162 + @RequestParam(required = false) String mediaServerId,
  163 + @RequestParam(required = false) String startTime,
  164 + @RequestParam(required = false) String endTime,
  165 + @RequestParam(required = false) String callId,
  166 + @RequestParam(required = false) String remoteHost
  167 + ){
  168 + return cloudRecordService.addTask(app, stream, mediaServerId, startTime, endTime, callId, remoteHost);
  169 + }
  170 +
  171 + @ResponseBody
  172 + @GetMapping("/task/list")
  173 + @Operation(summary = "查询合并任务")
  174 + @Parameter(name = "taskId", description = "任务Id", required = false)
  175 + @Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
  176 + @Parameter(name = "isEnd", description = "是否结束", required = false)
  177 + public JSONArray queryTaskList(
  178 + @RequestParam(required = false) String app,
  179 + @RequestParam(required = false) String stream,
  180 + @RequestParam(required = false) String callId,
  181 + @RequestParam(required = false) String taskId,
  182 + @RequestParam(required = false) String mediaServerId,
  183 + @RequestParam(required = false) Boolean isEnd
  184 + ){
  185 + return cloudRecordService.queryTask(app, stream, callId, taskId, mediaServerId, isEnd);
  186 + }
144 187  
  188 + @ResponseBody
  189 + @GetMapping("/collect/add")
  190 + @Operation(summary = "添加收藏")
  191 + @Parameter(name = "app", description = "应用名", required = false)
  192 + @Parameter(name = "stream", description = "流ID", required = false)
  193 + @Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
  194 + @Parameter(name = "startTime", description = "鉴权ID", required = false)
  195 + @Parameter(name = "endTime", description = "鉴权ID", required = false)
  196 + @Parameter(name = "callId", description = "鉴权ID", required = false)
  197 + @Parameter(name = "recordId", description = "录像记录的ID,用于精准收藏一个视频文件", required = false)
  198 + public int addCollect(
  199 + @RequestParam(required = false) String app,
  200 + @RequestParam(required = false) String stream,
  201 + @RequestParam(required = false) String mediaServerId,
  202 + @RequestParam(required = false) String startTime,
  203 + @RequestParam(required = false) String endTime,
  204 + @RequestParam(required = false) String callId,
  205 + @RequestParam(required = false) Integer recordId
  206 + ){
  207 + logger.info("[云端录像] 添加收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}",
  208 + app, stream, mediaServerId, startTime, endTime, callId, recordId);
  209 + if (recordId != null) {
  210 + return cloudRecordService.changeCollectById(recordId, true);
  211 + }else {
  212 + return cloudRecordService.changeCollect(true, app, stream, mediaServerId, startTime, endTime, callId);
  213 + }
  214 + }
  215 +
  216 + @ResponseBody
  217 + @GetMapping("/collect/delete")
  218 + @Operation(summary = "移除收藏")
  219 + @Parameter(name = "app", description = "应用名", required = false)
  220 + @Parameter(name = "stream", description = "流ID", required = false)
  221 + @Parameter(name = "mediaServerId", description = "流媒体ID", required = false)
  222 + @Parameter(name = "startTime", description = "鉴权ID", required = false)
  223 + @Parameter(name = "endTime", description = "鉴权ID", required = false)
  224 + @Parameter(name = "callId", description = "鉴权ID", required = false)
  225 + @Parameter(name = "recordId", description = "录像记录的ID,用于精准精准移除一个视频文件的收藏", required = false)
  226 + public int deleteCollect(
  227 + @RequestParam(required = false) String app,
  228 + @RequestParam(required = false) String stream,
  229 + @RequestParam(required = false) String mediaServerId,
  230 + @RequestParam(required = false) String startTime,
  231 + @RequestParam(required = false) String endTime,
  232 + @RequestParam(required = false) String callId,
  233 + @RequestParam(required = false) Integer recordId
  234 + ){
  235 + logger.info("[云端录像] 移除收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}",
  236 + app, stream, mediaServerId, startTime, endTime, callId, recordId);
  237 + if (recordId != null) {
  238 + return cloudRecordService.changeCollectById(recordId, false);
  239 + }else {
  240 + return cloudRecordService.changeCollect(false, app, stream, mediaServerId, startTime, endTime, callId);
  241 + }
  242 + }
  243 +
  244 + @ResponseBody
  245 + @GetMapping("/play/path")
  246 + @Operation(summary = "获取播放地址")
  247 + @Parameter(name = "recordId", description = "录像记录的ID", required = true)
  248 + public DownloadFileInfo getPlayUrlPath(
  249 + @RequestParam(required = true) Integer recordId
  250 + ){
  251 + return cloudRecordService.getPlayUrlPath(recordId);
  252 + }
145 253 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition;
2 2  
3 3 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  4 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
4 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 6 import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
6 7 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
... ... @@ -13,6 +14,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
13 14 import com.github.pagehelper.util.StringUtil;
14 15 import io.swagger.v3.oas.annotations.Operation;
15 16 import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
16 18 import io.swagger.v3.oas.annotations.tags.Tag;
17 19 import org.slf4j.Logger;
18 20 import org.slf4j.LoggerFactory;
... ... @@ -59,7 +61,7 @@ public class MobilePositionController {
59 61 * @param end 结束时间
60 62 * @return
61 63 */
62   - @Operation(summary = "查询历史轨迹")
  64 + @Operation(summary = "查询历史轨迹", security = @SecurityRequirement(name = JwtUtils.HEADER))
63 65 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
64 66 @Parameter(name = "channelId", description = "通道国标编号")
65 67 @Parameter(name = "start", description = "开始时间")
... ... @@ -84,7 +86,7 @@ public class MobilePositionController {
84 86 * @param deviceId 设备ID
85 87 * @return
86 88 */
87   - @Operation(summary = "查询设备最新位置")
  89 + @Operation(summary = "查询设备最新位置", security = @SecurityRequirement(name = JwtUtils.HEADER))
88 90 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
89 91 @GetMapping("/latest/{deviceId}")
90 92 public MobilePosition latestPosition(@PathVariable String deviceId) {
... ... @@ -96,7 +98,7 @@ public class MobilePositionController {
96 98 * @param deviceId 设备ID
97 99 * @return
98 100 */
99   - @Operation(summary = "获取移动位置信息")
  101 + @Operation(summary = "获取移动位置信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
100 102 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
101 103 @GetMapping("/realtime/{deviceId}")
102 104 public DeferredResult<MobilePosition> realTimePosition(@PathVariable String deviceId) {
... ... @@ -136,7 +138,7 @@ public class MobilePositionController {
136 138 * @param interval 上报时间间隔
137 139 * @return true = 命令发送成功
138 140 */
139   - @Operation(summary = "订阅位置信息")
  141 + @Operation(summary = "订阅位置信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
140 142 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
141 143 @Parameter(name = "expires", description = "订阅超时时间", required = true)
142 144 @Parameter(name = "interval", description = "上报时间间隔", required = true)
... ... @@ -162,7 +164,7 @@ public class MobilePositionController {
162 164 * @param deviceId 设备ID
163 165 * @return true = 命令发送成功
164 166 */
165   - @Operation(summary = "数据位置信息格式处理")
  167 + @Operation(summary = "数据位置信息格式处理", security = @SecurityRequirement(name = JwtUtils.HEADER))
166 168 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
167 169 @GetMapping("/transform/{deviceId}")
168 170 public void positionTransform(@PathVariable String deviceId) {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.alarm;
2 2  
3 3 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  4 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
4 5 import com.genersoft.iot.vmp.gb28181.bean.Device;
5 6 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
6 7 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
... ... @@ -13,6 +14,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
13 14 import com.github.pagehelper.PageInfo;
14 15 import io.swagger.v3.oas.annotations.Operation;
15 16 import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
16 18 import io.swagger.v3.oas.annotations.tags.Tag;
17 19 import org.slf4j.Logger;
18 20 import org.slf4j.LoggerFactory;
... ... @@ -56,7 +58,7 @@ public class AlarmController {
56 58 * @return
57 59 */
58 60 @DeleteMapping("/delete")
59   - @Operation(summary = "删除报警")
  61 + @Operation(summary = "删除报警", security = @SecurityRequirement(name = JwtUtils.HEADER))
60 62 @Parameter(name = "id", description = "ID")
61 63 @Parameter(name = "deviceIds", description = "多个设备id,逗号分隔")
62 64 @Parameter(name = "time", description = "结束时间")
... ... @@ -93,7 +95,7 @@ public class AlarmController {
93 95 * @return
94 96 */
95 97 @GetMapping("/test/notify/alarm")
96   - @Operation(summary = "测试向上级/设备发送模拟报警通知")
  98 + @Operation(summary = "测试向上级/设备发送模拟报警通知", security = @SecurityRequirement(name = JwtUtils.HEADER))
97 99 @Parameter(name = "deviceId", description = "设备国标编号")
98 100 public void delete(@RequestParam String deviceId) {
99 101 Device device = storage.queryVideoDevice(deviceId);
... ... @@ -141,7 +143,7 @@ public class AlarmController {
141 143 * @param endTime 结束时间
142 144 * @return
143 145 */
144   - @Operation(summary = "分页查询报警")
  146 + @Operation(summary = "分页查询报警", security = @SecurityRequirement(name = JwtUtils.HEADER))
145 147 @Parameter(name = "page",description = "当前页",required = true)
146 148 @Parameter(name = "count",description = "每页查询数量",required = true)
147 149 @Parameter(name = "deviceId",description = "设备id")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java
... ... @@ -9,6 +9,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device;
9 9  
10 10 import com.alibaba.fastjson2.JSONObject;
11 11 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  12 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
12 13 import com.genersoft.iot.vmp.gb28181.bean.Device;
13 14 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
14 15 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 18 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
18 19 import io.swagger.v3.oas.annotations.Operation;
19 20 import io.swagger.v3.oas.annotations.Parameter;
  21 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
20 22 import io.swagger.v3.oas.annotations.tags.Tag;
21 23 import org.slf4j.Logger;
22 24 import org.slf4j.LoggerFactory;
... ... @@ -57,7 +59,7 @@ public class DeviceConfig {
57 59 * @return
58 60 */
59 61 @GetMapping("/basicParam/{deviceId}")
60   - @Operation(summary = "基本配置设置命令")
  62 + @Operation(summary = "基本配置设置命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
61 63 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
62 64 @Parameter(name = "channelId", description = "通道国标编号", required = true)
63 65 @Parameter(name = "name", description = "名称")
... ... @@ -113,7 +115,7 @@ public class DeviceConfig {
113 115 * @param channelId 通道ID
114 116 * @return
115 117 */
116   - @Operation(summary = "设备配置查询请求")
  118 + @Operation(summary = "设备配置查询请求", security = @SecurityRequirement(name = JwtUtils.HEADER))
117 119 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
118 120 @Parameter(name = "channelId", description = "通道国标编号", required = true)
119 121 @Parameter(name = "configType", description = "配置类型")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
... ... @@ -9,6 +9,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device;
9 9  
10 10 import com.alibaba.fastjson2.JSONObject;
11 11 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  12 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
12 13 import com.genersoft.iot.vmp.gb28181.bean.Device;
13 14 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
14 15 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17 18 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
18 19 import io.swagger.v3.oas.annotations.Operation;
19 20 import io.swagger.v3.oas.annotations.Parameter;
  21 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
20 22 import io.swagger.v3.oas.annotations.tags.Tag;
21 23 import org.slf4j.Logger;
22 24 import org.slf4j.LoggerFactory;
... ... @@ -53,7 +55,7 @@ public class DeviceControl {
53 55 *
54 56 * @param deviceId 设备ID
55 57 */
56   - @Operation(summary = "远程启动控制命令")
  58 + @Operation(summary = "远程启动控制命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
57 59 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
58 60 @GetMapping("/teleboot/{deviceId}")
59 61 public void teleBootApi(@PathVariable String deviceId) {
... ... @@ -76,7 +78,7 @@ public class DeviceControl {
76 78 * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像
77 79 * @param channelId 通道编码(可选)
78 80 */
79   - @Operation(summary = "录像控制")
  81 + @Operation(summary = "录像控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
80 82 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
81 83 @Parameter(name = "channelId", description = "通道国标编号", required = true)
82 84 @Parameter(name = "recordCmdStr", description = "命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", required = true)
... ... @@ -125,7 +127,7 @@ public class DeviceControl {
125 127 * @param deviceId 设备ID
126 128 * @param guardCmdStr SetGuard:布防,ResetGuard:撤防
127 129 */
128   - @Operation(summary = "布防/撤防命令")
  130 + @Operation(summary = "布防/撤防命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
129 131 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
130 132 @Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true)
131 133 @GetMapping("/guard/{deviceId}/{guardCmdStr}")
... ... @@ -170,7 +172,7 @@ public class DeviceControl {
170 172 * @param alarmMethod 报警方式(可选)
171 173 * @param alarmType 报警类型(可选)
172 174 */
173   - @Operation(summary = "报警复位")
  175 + @Operation(summary = "报警复位", security = @SecurityRequirement(name = JwtUtils.HEADER))
174 176 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
175 177 @Parameter(name = "channelId", description = "通道国标编号", required = true)
176 178 @Parameter(name = "alarmMethod", description = "报警方式")
... ... @@ -217,7 +219,7 @@ public class DeviceControl {
217 219 * @param deviceId 设备ID
218 220 * @param channelId 通道ID
219 221 */
220   - @Operation(summary = "强制关键帧")
  222 + @Operation(summary = "强制关键帧", security = @SecurityRequirement(name = JwtUtils.HEADER))
221 223 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
222 224 @Parameter(name = "channelId", description = "通道国标编号")
223 225 @GetMapping("/i_frame/{deviceId}")
... ... @@ -249,7 +251,7 @@ public class DeviceControl {
249 251 * @param presetIndex 调用预置位编号(可选)
250 252 * @param channelId 通道编码(可选)
251 253 */
252   - @Operation(summary = "看守位控制")
  254 + @Operation(summary = "看守位控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
253 255 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
254 256 @Parameter(name = "channelId", description = "通道国标编号", required = true)
255 257 @Parameter(name = "enabled", description = "是否开启看守位 1:开启,0:关闭", required = true)
... ... @@ -309,7 +311,7 @@ public class DeviceControl {
309 311 * @param lengthy 拉框宽度像素值
310 312 * @return
311 313 */
312   - @Operation(summary = "拉框放大")
  314 + @Operation(summary = "拉框放大", security = @SecurityRequirement(name = JwtUtils.HEADER))
313 315 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
314 316 @Parameter(name = "channelId", description = "通道国标编号", required = true)
315 317 @Parameter(name = "length", description = "播放窗口长度像素值", required = true)
... ... @@ -359,7 +361,7 @@ public class DeviceControl {
359 361 * @param lengthy 拉框宽度像素值
360 362 * @return
361 363 */
362   - @Operation(summary = "拉框放大")
  364 + @Operation(summary = "拉框缩小", security = @SecurityRequirement(name = JwtUtils.HEADER))
363 365 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
364 366 @Parameter(name = "channelId", description = "通道国标编号")
365 367 @Parameter(name = "length", description = "播放窗口长度像素值", required = true)
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device;
3 3 import com.alibaba.fastjson2.JSONObject;
4 4 import com.genersoft.iot.vmp.conf.DynamicTask;
5 5 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  6 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
6 7 import com.genersoft.iot.vmp.gb28181.bean.Device;
7 8 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
8 9 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
... ... @@ -23,6 +24,7 @@ import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
23 24 import com.github.pagehelper.PageInfo;
24 25 import io.swagger.v3.oas.annotations.Operation;
25 26 import io.swagger.v3.oas.annotations.Parameter;
  27 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
26 28 import io.swagger.v3.oas.annotations.tags.Tag;
27 29 import org.apache.commons.compress.utils.IOUtils;
28 30 import org.apache.ibatis.annotations.Options;
... ... @@ -85,7 +87,7 @@ public class DeviceQuery {
85 87 * @param deviceId 国标ID
86 88 * @return 国标设备
87 89 */
88   - @Operation(summary = "查询国标设备")
  90 + @Operation(summary = "查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
89 91 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
90 92 @GetMapping("/devices/{deviceId}")
91 93 public Device devices(@PathVariable String deviceId){
... ... @@ -99,7 +101,7 @@ public class DeviceQuery {
99 101 * @param count 每页查询数量
100 102 * @return 分页国标列表
101 103 */
102   - @Operation(summary = "分页查询国标设备")
  104 + @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
103 105 @Parameter(name = "page", description = "当前页", required = true)
104 106 @Parameter(name = "count", description = "每页查询数量", required = true)
105 107 @GetMapping("/devices")
... ... @@ -123,7 +125,7 @@ public class DeviceQuery {
123 125 * @return 通道列表
124 126 */
125 127 @GetMapping("/devices/{deviceId}/channels")
126   - @Operation(summary = "分页查询通道")
  128 + @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
127 129 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
128 130 @Parameter(name = "page", description = "当前页", required = true)
129 131 @Parameter(name = "count", description = "每页查询数量", required = true)
... ... @@ -149,7 +151,7 @@ public class DeviceQuery {
149 151 * @param deviceId 设备id
150 152 * @return
151 153 */
152   - @Operation(summary = "同步设备通道")
  154 + @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
153 155 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
154 156 @GetMapping("/devices/{deviceId}/sync")
155 157 public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId){
... ... @@ -177,7 +179,7 @@ public class DeviceQuery {
177 179 * @param deviceId 设备id
178 180 * @return
179 181 */
180   - @Operation(summary = "移除设备")
  182 + @Operation(summary = "移除设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
181 183 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
182 184 @DeleteMapping("/devices/{deviceId}/delete")
183 185 public String delete(@PathVariable String deviceId){
... ... @@ -222,7 +224,7 @@ public class DeviceQuery {
222 224 * @param channelType 通道类型
223 225 * @return 子通道列表
224 226 */
225   - @Operation(summary = "分页查询子目录通道")
  227 + @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
226 228 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
227 229 @Parameter(name = "channelId", description = "通道国标编号", required = true)
228 230 @Parameter(name = "page", description = "当前页", required = true)
... ... @@ -254,7 +256,7 @@ public class DeviceQuery {
254 256 * @param channel 通道
255 257 * @return
256 258 */
257   - @Operation(summary = "更新通道信息")
  259 + @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
258 260 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
259 261 @Parameter(name = "channel", description = "通道信息", required = true)
260 262 @PostMapping("/channel/update/{deviceId}")
... ... @@ -268,7 +270,7 @@ public class DeviceQuery {
268 270 * @param streamMode 数据流传输模式
269 271 * @return
270 272 */
271   - @Operation(summary = "修改数据流传输模式")
  273 + @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER))
272 274 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
273 275 @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" +
274 276 "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true)
... ... @@ -284,7 +286,7 @@ public class DeviceQuery {
284 286 * @param device 设备信息
285 287 * @return
286 288 */
287   - @Operation(summary = "添加设备信息")
  289 + @Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
288 290 @Parameter(name = "device", description = "设备", required = true)
289 291 @PostMapping("/device/add/")
290 292 public void addDevice(Device device){
... ... @@ -306,7 +308,7 @@ public class DeviceQuery {
306 308 * @param device 设备信息
307 309 * @return
308 310 */
309   - @Operation(summary = "更新设备信息")
  311 + @Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
310 312 @Parameter(name = "device", description = "设备", required = true)
311 313 @PostMapping("/device/update/")
312 314 public void updateDevice(Device device){
... ... @@ -321,7 +323,7 @@ public class DeviceQuery {
321 323 *
322 324 * @param deviceId 设备id
323 325 */
324   - @Operation(summary = "设备状态查询")
  326 + @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
325 327 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
326 328 @GetMapping("/devices/{deviceId}/status")
327 329 public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {
... ... @@ -372,7 +374,7 @@ public class DeviceQuery {
372 374 * @param endTime 报警发生终止时间(可选)
373 375 * @return true = 命令发送成功
374 376 */
375   - @Operation(summary = "设备状态查询")
  377 + @Operation(summary = "设备报警查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
376 378 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
377 379 @Parameter(name = "startPriority", description = "报警起始级别")
378 380 @Parameter(name = "endPriority", description = "报警终止级别")
... ... @@ -422,7 +424,7 @@ public class DeviceQuery {
422 424  
423 425  
424 426 @GetMapping("/{deviceId}/sync_status")
425   - @Operation(summary = "获取通道同步进度")
  427 + @Operation(summary = "获取通道同步进度", security = @SecurityRequirement(name = JwtUtils.HEADER))
426 428 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
427 429 public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) {
428 430 SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
... ... @@ -442,7 +444,7 @@ public class DeviceQuery {
442 444 }
443 445  
444 446 @GetMapping("/{deviceId}/subscribe_info")
445   - @Operation(summary = "获取设备的订阅状态")
  447 + @Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER))
446 448 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
447 449 public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) {
448 450 Set<String> allKeys = dynamicTask.getAllKeys();
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.gbStream;
2 2  
3 3 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  4 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
4 5 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
5 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
6 7 import com.genersoft.iot.vmp.service.IGbStreamService;
... ... @@ -11,6 +12,7 @@ import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam;
11 12 import com.github.pagehelper.PageInfo;
12 13 import io.swagger.v3.oas.annotations.Operation;
13 14 import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
14 16 import io.swagger.v3.oas.annotations.tags.Tag;
15 17 import org.slf4j.Logger;
16 18 import org.slf4j.LoggerFactory;
... ... @@ -43,7 +45,7 @@ public class GbStreamController {
43 45 * @param platformId 平台ID
44 46 * @return
45 47 */
46   - @Operation(summary = "查询国标通道")
  48 + @Operation(summary = "查询国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
47 49 @Parameter(name = "page", description = "当前页", required = true)
48 50 @Parameter(name = "count", description = "每页条数", required = true)
49 51 @Parameter(name = "platformId", description = "平台ID", required = true)
... ... @@ -79,7 +81,7 @@ public class GbStreamController {
79 81 * @param gbStreamParam
80 82 * @return
81 83 */
82   - @Operation(summary = "移除国标关联")
  84 + @Operation(summary = "移除国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER))
83 85 @DeleteMapping(value = "/del")
84 86 @ResponseBody
85 87 public void del(@RequestBody GbStreamParam gbStreamParam){
... ... @@ -99,7 +101,7 @@ public class GbStreamController {
99 101 * @param gbStreamParam
100 102 * @return
101 103 */
102   - @Operation(summary = "保存国标关联")
  104 + @Operation(summary = "保存国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER))
103 105 @PostMapping(value = "/add")
104 106 @ResponseBody
105 107 public void add(@RequestBody GbStreamParam gbStreamParam){
... ... @@ -118,7 +120,7 @@ public class GbStreamController {
118 120 * @param gbId
119 121 * @return
120 122 */
121   - @Operation(summary = "保存国标关联")
  123 + @Operation(summary = "保存国标关联", security = @SecurityRequirement(name = JwtUtils.HEADER))
122 124 @GetMapping(value = "/addWithGbid")
123 125 @ResponseBody
124 126 public void add(String gbId, String platformGbId, @RequestParam(required = false) String catalogGbId){
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.media;
2 2  
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
4 4 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  5 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
5 6 import com.genersoft.iot.vmp.conf.security.SecurityUtils;
6 7 import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
7 8 import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
... ... @@ -12,6 +13,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
12 13 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
13 14 import io.swagger.v3.oas.annotations.Operation;
14 15 import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
15 17 import io.swagger.v3.oas.annotations.tags.Tag;
16 18 import org.slf4j.Logger;
17 19 import org.slf4j.LoggerFactory;
... ... @@ -45,7 +47,7 @@ public class MediaController {
45 47 * @param stream 流id
46 48 * @return
47 49 */
48   - @Operation(summary = "根据应用名和流id获取播放地址")
  50 + @Operation(summary = "根据应用名和流id获取播放地址", security = @SecurityRequirement(name = JwtUtils.HEADER))
49 51 @Parameter(name = "app", description = "应用名", required = true)
50 52 @Parameter(name = "stream", description = "流id", required = true)
51 53 @Parameter(name = "mediaServerId", description = "媒体服务器id")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
... ... @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
6 6 import com.genersoft.iot.vmp.conf.DynamicTask;
7 7 import com.genersoft.iot.vmp.conf.UserSetting;
8 8 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  9 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
9 10 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
10 11 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
11 12 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
... ... @@ -21,6 +22,7 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.UpdateChannelParam;
21 22 import com.github.pagehelper.PageInfo;
22 23 import io.swagger.v3.oas.annotations.Operation;
23 24 import io.swagger.v3.oas.annotations.Parameter;
  25 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
24 26 import io.swagger.v3.oas.annotations.tags.Tag;
25 27 import org.slf4j.Logger;
26 28 import org.slf4j.LoggerFactory;
... ... @@ -83,7 +85,7 @@ public class PlatformController {
83 85 *
84 86 * @return
85 87 */
86   - @Operation(summary = "获取国标服务的配置")
  88 + @Operation(summary = "获取国标服务的配置", security = @SecurityRequirement(name = JwtUtils.HEADER))
87 89 @GetMapping("/server_config")
88 90 public JSONObject serverConfig() {
89 91 JSONObject result = new JSONObject();
... ... @@ -99,7 +101,7 @@ public class PlatformController {
99 101 *
100 102 * @return
101 103 */
102   - @Operation(summary = "获取级联服务器信息")
  104 + @Operation(summary = "获取级联服务器信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
103 105 @Parameter(name = "id", description = "平台国标编号", required = true)
104 106 @GetMapping("/info/{id}")
105 107 public ParentPlatform getPlatform(@PathVariable String id) {
... ... @@ -119,7 +121,7 @@ public class PlatformController {
119 121 * @return
120 122 */
121 123 @GetMapping("/query/{count}/{page}")
122   - @Operation(summary = "分页查询级联平台")
  124 + @Operation(summary = "分页查询级联平台", security = @SecurityRequirement(name = JwtUtils.HEADER))
123 125 @Parameter(name = "page", description = "当前页", required = true)
124 126 @Parameter(name = "count", description = "每页条数", required = true)
125 127 public PageInfo<ParentPlatform> platforms(@PathVariable int page, @PathVariable int count) {
... ... @@ -140,7 +142,7 @@ public class PlatformController {
140 142 * @param parentPlatform
141 143 * @return
142 144 */
143   - @Operation(summary = "添加上级平台信息")
  145 + @Operation(summary = "添加上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
144 146 @PostMapping("/add")
145 147 @ResponseBody
146 148 public void addPlatform(@RequestBody ParentPlatform parentPlatform) {
... ... @@ -185,7 +187,7 @@ public class PlatformController {
185 187 * @param parentPlatform
186 188 * @return
187 189 */
188   - @Operation(summary = "保存上级平台信息")
  190 + @Operation(summary = "保存上级平台信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
189 191 @PostMapping("/save")
190 192 @ResponseBody
191 193 public void savePlatform(@RequestBody ParentPlatform parentPlatform) {
... ... @@ -216,7 +218,7 @@ public class PlatformController {
216 218 * @param serverGBId 上级平台国标ID
217 219 * @return
218 220 */
219   - @Operation(summary = "删除上级平台")
  221 + @Operation(summary = "删除上级平台", security = @SecurityRequirement(name = JwtUtils.HEADER))
220 222 @Parameter(name = "serverGBId", description = "上级平台的国标编号")
221 223 @DeleteMapping("/delete/{serverGBId}")
222 224 @ResponseBody
... ... @@ -273,7 +275,7 @@ public class PlatformController {
273 275 * @param serverGBId 上级平台国标ID
274 276 * @return
275 277 */
276   - @Operation(summary = "查询上级平台是否存在")
  278 + @Operation(summary = "查询上级平台是否存在", security = @SecurityRequirement(name = JwtUtils.HEADER))
277 279 @Parameter(name = "serverGBId", description = "上级平台的国标编号")
278 280 @GetMapping("/exit/{serverGBId}")
279 281 @ResponseBody
... ... @@ -294,7 +296,7 @@ public class PlatformController {
294 296 * @param channelType 通道类型
295 297 * @return
296 298 */
297   - @Operation(summary = "查询上级平台是否存在")
  299 + @Operation(summary = "查询上级平台是否存在", security = @SecurityRequirement(name = JwtUtils.HEADER))
298 300 @Parameter(name = "page", description = "当前页", required = true)
299 301 @Parameter(name = "count", description = "每页条数", required = true)
300 302 @Parameter(name = "platformId", description = "上级平台的国标编号")
... ... @@ -331,7 +333,7 @@ public class PlatformController {
331 333 * @param param 通道关联参数
332 334 * @return
333 335 */
334   - @Operation(summary = "向上级平台添加国标通道")
  336 + @Operation(summary = "向上级平台添加国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
335 337 @PostMapping("/update_channel_for_gb")
336 338 @ResponseBody
337 339 public void updateChannelForGB(@RequestBody UpdateChannelParam param) {
... ... @@ -360,7 +362,7 @@ public class PlatformController {
360 362 * @param param 通道关联参数
361 363 * @return
362 364 */
363   - @Operation(summary = "从上级平台移除国标通道")
  365 + @Operation(summary = "从上级平台移除国标通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
364 366 @DeleteMapping("/del_channel_for_gb")
365 367 @ResponseBody
366 368 public void delChannelForGB(@RequestBody UpdateChannelParam param) {
... ... @@ -389,7 +391,7 @@ public class PlatformController {
389 391 * @param parentId 目录父ID
390 392 * @return
391 393 */
392   - @Operation(summary = "获取目录")
  394 + @Operation(summary = "获取目录", security = @SecurityRequirement(name = JwtUtils.HEADER))
393 395 @Parameter(name = "platformId", description = "上级平台的国标编号", required = true)
394 396 @Parameter(name = "parentId", description = "父级目录的国标编号", required = true)
395 397 @GetMapping("/catalog")
... ... @@ -420,7 +422,7 @@ public class PlatformController {
420 422 * @param platformCatalog 目录
421 423 * @return
422 424 */
423   - @Operation(summary = "添加目录")
  425 + @Operation(summary = "添加目录", security = @SecurityRequirement(name = JwtUtils.HEADER))
424 426 @PostMapping("/catalog/add")
425 427 @ResponseBody
426 428 public void addCatalog(@RequestBody PlatformCatalog platformCatalog) {
... ... @@ -445,7 +447,7 @@ public class PlatformController {
445 447 * @param platformCatalog 目录
446 448 * @return
447 449 */
448   - @Operation(summary = "编辑目录")
  450 + @Operation(summary = "编辑目录", security = @SecurityRequirement(name = JwtUtils.HEADER))
449 451 @PostMapping("/catalog/edit")
450 452 @ResponseBody
451 453 public void editCatalog(@RequestBody PlatformCatalog platformCatalog) {
... ... @@ -471,7 +473,7 @@ public class PlatformController {
471 473 * @param platformId 平台Id
472 474 * @return
473 475 */
474   - @Operation(summary = "删除目录")
  476 + @Operation(summary = "删除目录", security = @SecurityRequirement(name = JwtUtils.HEADER))
475 477 @Parameter(name = "id", description = "目录Id", required = true)
476 478 @Parameter(name = "platformId", description = "平台Id", required = true)
477 479 @DeleteMapping("/catalog/del")
... ... @@ -506,7 +508,7 @@ public class PlatformController {
506 508 * @param platformCatalog 关联的信息
507 509 * @return
508 510 */
509   - @Operation(summary = "删除关联")
  511 + @Operation(summary = "删除关联", security = @SecurityRequirement(name = JwtUtils.HEADER))
510 512 @DeleteMapping("/catalog/relation/del")
511 513 @ResponseBody
512 514 public void delRelation(@RequestBody PlatformCatalog platformCatalog) {
... ... @@ -529,7 +531,7 @@ public class PlatformController {
529 531 * @param catalogId 目录Id
530 532 * @return
531 533 */
532   - @Operation(summary = "修改默认目录")
  534 + @Operation(summary = "修改默认目录", security = @SecurityRequirement(name = JwtUtils.HEADER))
533 535 @Parameter(name = "catalogId", description = "目录Id", required = true)
534 536 @Parameter(name = "platformId", description = "平台Id", required = true)
535 537 @PostMapping("/catalog/default/update")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.common.StreamInfo;
9 9 import com.genersoft.iot.vmp.conf.UserSetting;
10 10 import com.genersoft.iot.vmp.conf.exception.ControllerException;
11 11 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  12 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
12 13 import com.genersoft.iot.vmp.gb28181.bean.Device;
13 14 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
14 15 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
... ... @@ -31,6 +32,7 @@ import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
31 32 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
32 33 import io.swagger.v3.oas.annotations.Operation;
33 34 import io.swagger.v3.oas.annotations.Parameter;
  35 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
34 36 import io.swagger.v3.oas.annotations.tags.Tag;
35 37 import org.slf4j.Logger;
36 38 import org.slf4j.LoggerFactory;
... ... @@ -92,7 +94,7 @@ public class PlayController {
92 94 @Autowired
93 95 private UserSetting userSetting;
94 96  
95   - @Operation(summary = "开始点播")
  97 + @Operation(summary = "开始点播", security = @SecurityRequirement(name = JwtUtils.HEADER))
96 98 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
97 99 @Parameter(name = "channelId", description = "通道国标编号", required = true)
98 100 @GetMapping("/start/{deviceId}/{channelId}")
... ... @@ -157,7 +159,7 @@ public class PlayController {
157 159 return result;
158 160 }
159 161  
160   - @Operation(summary = "停止点播")
  162 + @Operation(summary = "停止点播", security = @SecurityRequirement(name = JwtUtils.HEADER))
161 163 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
162 164 @Parameter(name = "channelId", description = "通道国标编号", required = true)
163 165 @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true)
... ... @@ -202,7 +204,7 @@ public class PlayController {
202 204 * 将不是h264的视频通过ffmpeg 转码为h264 + aac
203 205 * @param streamId 流ID
204 206 */
205   - @Operation(summary = "将不是h264的视频通过ffmpeg 转码为h264 + aac")
  207 + @Operation(summary = "将不是h264的视频通过ffmpeg 转码为h264 + aac", security = @SecurityRequirement(name = JwtUtils.HEADER))
206 208 @Parameter(name = "streamId", description = "视频流ID", required = true)
207 209 @PostMapping("/convert/{streamId}")
208 210 public JSONObject playConvert(@PathVariable String streamId) {
... ... @@ -244,7 +246,7 @@ public class PlayController {
244 246 /**
245 247 * 结束转码
246 248 */
247   - @Operation(summary = "结束转码")
  249 + @Operation(summary = "结束转码", security = @SecurityRequirement(name = JwtUtils.HEADER))
248 250 @Parameter(name = "key", description = "视频流key", required = true)
249 251 @Parameter(name = "mediaServerId", description = "流媒体服务ID", required = true)
250 252 @PostMapping("/convertStop/{key}")
... ... @@ -269,7 +271,7 @@ public class PlayController {
269 271 }
270 272 }
271 273  
272   - @Operation(summary = "语音广播命令")
  274 + @Operation(summary = "语音广播命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
273 275 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
274 276 @Parameter(name = "deviceId", description = "通道国标编号", required = true)
275 277 @Parameter(name = "timeout", description = "推流超时时间(秒)", required = true)
... ... @@ -309,7 +311,7 @@ public class PlayController {
309 311 playService.stopAudioBroadcast(deviceId, channelId);
310 312 }
311 313  
312   - @Operation(summary = "获取所有的ssrc")
  314 + @Operation(summary = "获取所有的ssrc", security = @SecurityRequirement(name = JwtUtils.HEADER))
313 315 @GetMapping("/ssrc")
314 316 public JSONObject getSSRC() {
315 317 if (logger.isDebugEnabled()) {
... ... @@ -332,7 +334,7 @@ public class PlayController {
332 334 return jsonObject;
333 335 }
334 336  
335   - @Operation(summary = "获取截图")
  337 + @Operation(summary = "获取截图", security = @SecurityRequirement(name = JwtUtils.HEADER))
336 338 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
337 339 @Parameter(name = "channelId", description = "通道国标编号", required = true)
338 340 @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true)
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
... ... @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
7 7 import com.genersoft.iot.vmp.conf.exception.ControllerException;
8 8 import com.genersoft.iot.vmp.conf.exception.ServiceException;
9 9 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  10 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
10 11 import com.genersoft.iot.vmp.gb28181.bean.Device;
11 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
12 13 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -20,6 +21,7 @@ import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
20 21 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
21 22 import io.swagger.v3.oas.annotations.Operation;
22 23 import io.swagger.v3.oas.annotations.Parameter;
  24 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
23 25 import io.swagger.v3.oas.annotations.tags.Tag;
24 26 import org.slf4j.Logger;
25 27 import org.slf4j.LoggerFactory;
... ... @@ -68,7 +70,7 @@ public class PlaybackController {
68 70 @Autowired
69 71 private UserSetting userSetting;
70 72  
71   - @Operation(summary = "开始视频回放")
  73 + @Operation(summary = "开始视频回放", security = @SecurityRequirement(name = JwtUtils.HEADER))
72 74 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
73 75 @Parameter(name = "channelId", description = "通道国标编号", required = true)
74 76 @Parameter(name = "startTime", description = "开始时间", required = true)
... ... @@ -125,7 +127,7 @@ public class PlaybackController {
125 127 }
126 128  
127 129  
128   - @Operation(summary = "停止视频回放")
  130 + @Operation(summary = "停止视频回放", security = @SecurityRequirement(name = JwtUtils.HEADER))
129 131 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
130 132 @Parameter(name = "channelId", description = "通道国标编号", required = true)
131 133 @Parameter(name = "stream", description = "流ID", required = true)
... ... @@ -149,7 +151,7 @@ public class PlaybackController {
149 151 }
150 152  
151 153  
152   - @Operation(summary = "回放暂停")
  154 + @Operation(summary = "回放暂停", security = @SecurityRequirement(name = JwtUtils.HEADER))
153 155 @Parameter(name = "streamId", description = "回放流ID", required = true)
154 156 @GetMapping("/pause/{streamId}")
155 157 public void playPause(@PathVariable String streamId) {
... ... @@ -165,7 +167,7 @@ public class PlaybackController {
165 167 }
166 168  
167 169  
168   - @Operation(summary = "回放恢复")
  170 + @Operation(summary = "回放恢复", security = @SecurityRequirement(name = JwtUtils.HEADER))
169 171 @Parameter(name = "streamId", description = "回放流ID", required = true)
170 172 @GetMapping("/resume/{streamId}")
171 173 public void playResume(@PathVariable String streamId) {
... ... @@ -180,7 +182,7 @@ public class PlaybackController {
180 182 }
181 183  
182 184  
183   - @Operation(summary = "回放拖动播放")
  185 + @Operation(summary = "回放拖动播放", security = @SecurityRequirement(name = JwtUtils.HEADER))
184 186 @Parameter(name = "streamId", description = "回放流ID", required = true)
185 187 @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true)
186 188 @GetMapping("/seek/{streamId}/{seekTime}")
... ... @@ -200,7 +202,7 @@ public class PlaybackController {
200 202 }
201 203 }
202 204  
203   - @Operation(summary = "回放倍速播放")
  205 + @Operation(summary = "回放倍速播放", security = @SecurityRequirement(name = JwtUtils.HEADER))
204 206 @Parameter(name = "streamId", description = "回放流ID", required = true)
205 207 @Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true)
206 208 @GetMapping("/speed/{streamId}/{speed}")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
2 2  
3 3  
4 4 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  5 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
5 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 7 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
7 8 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
... ... @@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
10 11 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
11 12 import io.swagger.v3.oas.annotations.Operation;
12 13 import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
13 15 import io.swagger.v3.oas.annotations.tags.Tag;
14 16 import org.slf4j.Logger;
15 17 import org.slf4j.LoggerFactory;
... ... @@ -50,7 +52,7 @@ public class PtzController {
50 52 * @param zoomSpeed 缩放速度
51 53 */
52 54  
53   - @Operation(summary = "云台控制")
  55 + @Operation(summary = "云台控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
54 56 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
55 57 @Parameter(name = "channelId", description = "通道国标编号", required = true)
56 58 @Parameter(name = "command", description = "控制指令,允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop", required = true)
... ... @@ -113,7 +115,7 @@ public class PtzController {
113 115 }
114 116  
115 117  
116   - @Operation(summary = "通用前端控制命令")
  118 + @Operation(summary = "通用前端控制命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
117 119 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
118 120 @Parameter(name = "channelId", description = "通道国标编号", required = true)
119 121 @Parameter(name = "cmdCode", description = "指令码", required = true)
... ... @@ -137,7 +139,7 @@ public class PtzController {
137 139 }
138 140  
139 141  
140   - @Operation(summary = "预置位查询")
  142 + @Operation(summary = "预置位查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
141 143 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
142 144 @Parameter(name = "channelId", description = "通道国标编号", required = true)
143 145 @GetMapping("/preset/query/{deviceId}/{channelId}")
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.record;
2 2  
  3 +import com.genersoft.iot.vmp.common.InviteInfo;
  4 +import com.genersoft.iot.vmp.common.InviteSessionType;
3 5 import com.genersoft.iot.vmp.common.StreamInfo;
4 6 import com.genersoft.iot.vmp.conf.UserSetting;
5 7 import com.genersoft.iot.vmp.conf.exception.ControllerException;
6 8 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  9 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
7 10 import com.genersoft.iot.vmp.gb28181.bean.Device;
8 11 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
9 12 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
10 13 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
11 14 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
12 15 import com.genersoft.iot.vmp.service.IDeviceService;
  16 +import com.genersoft.iot.vmp.service.IInviteStreamService;
13 17 import com.genersoft.iot.vmp.service.IPlayService;
  18 +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
14 19 import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
15 20 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
16 21 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -19,10 +24,12 @@ import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
19 24 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
20 25 import io.swagger.v3.oas.annotations.Operation;
21 26 import io.swagger.v3.oas.annotations.Parameter;
  27 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
22 28 import io.swagger.v3.oas.annotations.tags.Tag;
23 29 import org.slf4j.Logger;
24 30 import org.slf4j.LoggerFactory;
25 31 import org.springframework.beans.factory.annotation.Autowired;
  32 +import org.springframework.util.ObjectUtils;
26 33 import org.springframework.web.bind.annotation.GetMapping;
27 34 import org.springframework.web.bind.annotation.PathVariable;
28 35 import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -56,12 +63,15 @@ public class GBRecordController {
56 63 private IPlayService playService;
57 64  
58 65 @Autowired
  66 + private IInviteStreamService inviteStreamService;
  67 +
  68 + @Autowired
59 69 private IDeviceService deviceService;
60 70  
61 71 @Autowired
62 72 private UserSetting userSetting;
63 73  
64   - @Operation(summary = "录像查询")
  74 + @Operation(summary = "录像查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
65 75 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
66 76 @Parameter(name = "channelId", description = "通道国标编号", required = true)
67 77 @Parameter(name = "startTime", description = "开始时间", required = true)
... ... @@ -115,7 +125,7 @@ public class GBRecordController {
115 125 }
116 126  
117 127  
118   - @Operation(summary = "开始历史媒体下载")
  128 + @Operation(summary = "开始历史媒体下载", security = @SecurityRequirement(name = JwtUtils.HEADER))
119 129 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
120 130 @Parameter(name = "channelId", description = "通道国标编号", required = true)
121 131 @Parameter(name = "startTime", description = "开始时间", required = true)
... ... @@ -164,7 +174,7 @@ public class GBRecordController {
164 174 return result;
165 175 }
166 176  
167   - @Operation(summary = "停止历史媒体下载")
  177 + @Operation(summary = "停止历史媒体下载", security = @SecurityRequirement(name = JwtUtils.HEADER))
168 178 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
169 179 @Parameter(name = "channelId", description = "通道国标编号", required = true)
170 180 @Parameter(name = "stream", description = "流ID", required = true)
... ... @@ -192,7 +202,7 @@ public class GBRecordController {
192 202 }
193 203 }
194 204  
195   - @Operation(summary = "获取历史媒体下载进度")
  205 + @Operation(summary = "获取历史媒体下载进度", security = @SecurityRequirement(name = JwtUtils.HEADER))
196 206 @Parameter(name = "deviceId", description = "设备国标编号", required = true)
197 207 @Parameter(name = "channelId", description = "通道国标编号", required = true)
198 208 @Parameter(name = "stream", description = "流ID", required = true)
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.vmanager.log;
2 2  
3 3 import com.genersoft.iot.vmp.conf.UserSetting;
4 4 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  5 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
5 6 import com.genersoft.iot.vmp.service.ILogService;
6 7 import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
7 8 import com.genersoft.iot.vmp.utils.DateUtil;
... ... @@ -9,6 +10,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
9 10 import com.github.pagehelper.PageInfo;
10 11 import io.swagger.v3.oas.annotations.Operation;
11 12 import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
12 14 import io.swagger.v3.oas.annotations.tags.Tag;
13 15 import org.slf4j.Logger;
14 16 import org.slf4j.LoggerFactory;
... ... @@ -42,7 +44,7 @@ public class LogController {
42 44 * @return
43 45 */
44 46 @GetMapping("/all")
45   - @Operation(summary = "分页查询日志")
  47 + @Operation(summary = "分页查询日志", security = @SecurityRequirement(name = JwtUtils.HEADER))
46 48 @Parameter(name = "query", description = "查询内容", required = true)
47 49 @Parameter(name = "page", description = "当前页", required = true)
48 50 @Parameter(name = "count", description = "每页查询数量", required = true)
... ... @@ -84,7 +86,7 @@ public class LogController {
84 86 * 清空日志
85 87 *
86 88 */
87   - @Operation(summary = "清空日志")
  89 + @Operation(summary = "清空日志", security = @SecurityRequirement(name = JwtUtils.HEADER))
88 90 @DeleteMapping("/clear")
89 91 public void clear() {
90 92 logService.clear();
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants;
5 5 import com.genersoft.iot.vmp.conf.DynamicTask;
6 6 import com.genersoft.iot.vmp.conf.UserSetting;
7 7 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  8 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
8 9 import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager;
9 10 import com.genersoft.iot.vmp.media.zlm.ZLMServerFactory;
10 11 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
... ... @@ -19,6 +20,7 @@ import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
19 20 import com.genersoft.iot.vmp.vmanager.bean.OtherPsSendInfo;
20 21 import io.swagger.v3.oas.annotations.Operation;
21 22 import io.swagger.v3.oas.annotations.Parameter;
  23 +import io.swagger.v3.oas.annotations.security.SecurityRequirement;
22 24 import io.swagger.v3.oas.annotations.tags.Tag;
23 25 import okhttp3.OkHttpClient;
24 26 import okhttp3.Request;
... ... @@ -69,7 +71,7 @@ public class PsController {
69 71  
70 72 @GetMapping(value = "/receive/open")
71 73 @ResponseBody
72   - @Operation(summary = "开启收流和获取发流信息")
  74 + @Operation(summary = "开启收流和获取发流信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
73 75 @Parameter(name = "isSend", description = "是否发送,false时只开启收流, true同时返回推流信息", required = true)
74 76 @Parameter(name = "callId", description = "整个过程的唯一标识,为了与后续接口关联", required = true)
75 77 @Parameter(name = "ssrc", description = "来源流的SSRC,不传则不校验来源ssrc", required = false)
... ... @@ -152,7 +154,7 @@ public class PsController {
152 154  
153 155 @GetMapping(value = "/receive/close")
154 156 @ResponseBody
155   - @Operation(summary = "关闭收流")
  157 + @Operation(summary = "关闭收流", security = @SecurityRequirement(name = JwtUtils.HEADER))
156 158 @Parameter(name = "stream", description = "流的ID", required = true)
157 159 public void closeRtpServer(String stream) {
158 160 logger.info("[第三方PS服务对接->关闭收流] stream->{}", stream);
... ... @@ -170,7 +172,7 @@ public class PsController {
170 172  
171 173 @GetMapping(value = "/send/start")
172 174 @ResponseBody
173   - @Operation(summary = "发送流")
  175 + @Operation(summary = "发送流", security = @SecurityRequirement(name = JwtUtils.HEADER))
174 176 @Parameter(name = "ssrc", description = "发送流的SSRC", required = true)
175 177 @Parameter(name = "dstIp", description = "目标收流IP", required = true)
176 178 @Parameter(name = "dstPort", description = "目标收流端口", required = true)
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java deleted 100755 → 0
1   -//package com.genersoft.iot.vmp.vmanager.record;
2   -//
3   -//import com.alibaba.fastjson2.JSONObject;
4   -//import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
5   -//import com.genersoft.iot.vmp.service.IRecordInfoServer;
6   -//import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
7   -//import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
8   -//import com.github.pagehelper.PageInfo;
9   -//import io.swagger.annotations.Api;
10   -//import io.swagger.annotations.ApiImplicitParam;
11   -//import io.swagger.annotations.ApiImplicitParams;
12   -//import io.swagger.annotations.ApiOperation;
13   -//import org.springframework.beans.factory.annotation.Autowired;
14   -//import org.springframework.web.bind.annotation.*;
15   -//
16   -//@Tag(name = "云端录像")
17   -//
18   -//@RestController
19   -//@RequestMapping("/api/record")
20   -//public class RecordController {
21   -//
22   -// @Autowired
23   -// private IRecordInfoServer recordInfoServer;
24   -//
25   -// //@ApiOperation("录像列表查询")
26   -// @ApiImplicitParams({
27   -// @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
28   -// @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class),
29   -// @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class),
30   -// })
31   -// @GetMapping(value = "/app/list")
32   -// @ResponseBody
33   -// public Object list(@RequestParam(required = false)Integer page,
34   -// @RequestParam(required = false)Integer count ){
35   -//
36   -// PageInfo<RecordInfo> recordList = recordInfoServer.getRecordList(page - 1, page - 1 + count);
37   -// return recordList;
38   -// }
39   -//
40   -// //@ApiOperation("获取录像详情")
41   -// @ApiImplicitParams({
42   -// @ApiImplicitParam(name="recordInfo", value = "录像记录", required = true, dataTypeClass = RecordInfo.class)
43   -// })
44   -// @GetMapping(value = "/detail")
45   -// @ResponseBody
46   -// public JSONObject list(RecordInfo recordInfo, String time ){
47   -//
48   -//
49   -// return null;
50   -// }
51   -//}