From 36004d471ce31685fccf210f0334366445c90c4b Mon Sep 17 00:00:00 2001 From: guo <260206558@qq.com> Date: Wed, 8 Jan 2025 16:31:35 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=E5=9C=A8SpringBoot=202.4=E5=8F=8A?= =?UTF-8?q?=E4=BB=A5=E4=B8=8A=E7=89=88=E6=9C=AC=E5=A4=84=E7=90=86=E8=B7=A8?= =?UTF-8?q?=E5=9F=9F=E6=97=B6=EF=BC=8C=E9=81=87=E5=88=B0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=EF=BC=9A=E5=BD=93allowCredentials=E4=B8=BAtr?= =?UTF-8?q?ue=E6=97=B6=EF=BC=8CallowedOrigins=E4=B8=8D=E8=83=BD=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E7=89=B9=E6=AE=8A=E5=80=BC"*"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../genersoft/iot/vmp/conf/security/WebSecurityConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index 2bdad1ff3..7c4680146 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -148,8 +148,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { corsConfiguration.setAllowCredentials(true); corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins()); }else { - corsConfiguration.setAllowCredentials(false); - corsConfiguration.setAllowedOrigins(Collections.singletonList(CorsConfiguration.ALL)); + // 在SpringBoot 2.4及以上版本处理跨域时,遇到错误提示:当allowCredentials为true时,allowedOrigins不能包含特殊值"*"。 + // 解决方法是明确指定allowedOrigins或使用allowedOriginPatterns。 + corsConfiguration.setAllowCredentials(true); + corsConfiguration.addAllowedOriginPattern(CorsConfiguration.ALL); // 默认全部允许所有跨域 } corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); From cf3204de715cd81c8224f60c011afbf280d2c126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E5=AE=AB=E8=8C=9C?= Date: Tue, 21 Jan 2025 16:57:44 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E8=A7=A6=E5=8F=91=20NPE=20=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ZLMediaKit 回应中可能没有 tracks 字段,导致报 NPE,使得资源没有被正确释放 --- src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java index c4a0615b5..7f2f36124 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java @@ -10,6 +10,8 @@ import lombok.Data; import java.util.List; import java.util.Map; +import org.springframework.util.ObjectUtils; + /** * 视频信息 */ @@ -106,7 +108,7 @@ public class MediaInfo { } } JSONArray jsonArray = jsonObject.getJSONArray("tracks"); - if (!jsonArray.isEmpty()) { + if (!ObjectUtils.isEmpty(jsonArray)) { for (int i = 0; i < jsonArray.size(); i++) { JSONObject trackJson = jsonArray.getJSONObject(i); Integer channels = trackJson.getInteger("channels"); From 3553c2911cac6d40e0c91d7576eb2931beabdd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E5=AE=AB=E8=8C=9C?= Date: Tue, 21 Jan 2025 17:11:28 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=94=B6=E5=88=B0=20Bye?= =?UTF-8?q?=20=E6=97=B6=E8=A7=A6=E5=8F=91=E7=9A=84=E4=B8=80=E4=B8=AA=20NPE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java index 7f2f36124..63064f86d 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/media/bean/MediaInfo.java @@ -84,6 +84,8 @@ public class MediaInfo { Long bytesSpeed = jsonObject.getLong("bytesSpeed"); if (totalReaderCount != null) { mediaInfo.setReaderCount(totalReaderCount); + } else { + mediaInfo.setReaderCount(0); } if (online != null) { mediaInfo.setOnline(online); From 2f53a4d01e87ad41b5813358f9750cba8da69eea Mon Sep 17 00:00:00 2001 From: liyexin Date: Thu, 23 Jan 2025 16:05:12 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Djwk.json=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=AF=BB=E5=8F=96=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=A2=9E=E5=8A=A0jwkFile=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/genersoft/iot/vmp/conf/UserSetting.java | 5 +++++ .../genersoft/iot/vmp/conf/security/JwtUtils.java | 12 ++++++++---- src/main/resources/配置详情.yml | 2 ++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index c64b4ba88..194bfd3a1 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -175,4 +175,9 @@ public class UserSetting { */ private long loginTimeout = 30; + /** + * jwk文件路径,若不指定则使用resources目录下的jwk.json + */ + private String jwkFile = "classpath:jwk.json"; + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java index 61e019c60..d9da77677 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.User; import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.JsonWebKeySet; import org.jose4j.jwk.RsaJsonWebKey; @@ -22,10 +23,10 @@ import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; +import org.springframework.util.ResourceUtils; import javax.annotation.Resource; -import java.io.BufferedReader; -import java.io.InputStreamReader; +import java.io.File; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -92,8 +93,10 @@ public class JwtUtils implements InitializingBean { */ private RsaJsonWebKey generateRsaJsonWebKey() throws JoseException { RsaJsonWebKey rsaJsonWebKey = null; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("/jwk.json"), StandardCharsets.UTF_8))) { - String jwkJson = reader.readLine(); + try { + String jwkFile = userSetting.getJwkFile(); + File file = ResourceUtils.getFile(jwkFile); + String jwkJson = FileUtils.readFileToString(file, StandardCharsets.UTF_8); JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson); List jsonWebKeys = jsonWebKeySet.getJsonWebKeys(); if (!jsonWebKeys.isEmpty()) { @@ -106,6 +109,7 @@ public class JwtUtils implements InitializingBean { // ignored } if (rsaJsonWebKey == null) { + log.warn("[API AUTH] 读取jwk.json失败,将使用新生成的随机RSA密钥对"); // 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中 rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); // 给JWK一个密钥ID diff --git a/src/main/resources/配置详情.yml b/src/main/resources/配置详情.yml index e35f9771a..aa3c54c94 100644 --- a/src/main/resources/配置详情.yml +++ b/src/main/resources/配置详情.yml @@ -253,6 +253,8 @@ user-settings: gb-device-online: 0 # 登录超时时间(分钟), login-timeout: 30 + # jwk文件路径,若不指定则使用resources目录下的jwk.json + jwk-file: classpath:jwk.json # 关闭在线文档(生产环境建议关闭) springdoc: From 547432e194018aa589603aac58ea25b11a862d22 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Fri, 7 Feb 2025 17:48:06 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E4=BC=98=E5=8C=96jwk.json=E8=AF=BB?= =?UTF-8?q?=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/vmp/conf/security/JwtUtils.java | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java index d9da77677..37a3307e5 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -7,7 +7,6 @@ import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.User; import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.JsonWebKeySet; import org.jose4j.jwk.RsaJsonWebKey; @@ -22,12 +21,16 @@ import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; -import org.springframework.util.ResourceUtils; import javax.annotation.Resource; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; @@ -95,8 +98,44 @@ public class JwtUtils implements InitializingBean { RsaJsonWebKey rsaJsonWebKey = null; try { String jwkFile = userSetting.getJwkFile(); - File file = ResourceUtils.getFile(jwkFile); - String jwkJson = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + InputStream inputStream = null; + if (jwkFile.startsWith("classpath:")){ + String filePath = jwkFile.substring("classpath:".length()); + ClassPathResource civilCodeFile = new ClassPathResource(filePath); + if (civilCodeFile.exists()) { + inputStream = civilCodeFile.getInputStream(); + } + }else { + File civilCodeFile = new File(userSetting.getCivilCodeFile()); + if (civilCodeFile.exists()) { + inputStream = Files.newInputStream(civilCodeFile.toPath()); + } + + } + if (inputStream == null ) { + log.warn("[API AUTH] 读取jwk.json失败,文件不存在,将使用新生成的随机RSA密钥对"); + // 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中 + rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); + // 给JWK一个密钥ID + rsaJsonWebKey.setKeyId(keyId); + return rsaJsonWebKey; + } + BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + int index = -1; + String line; + StringBuilder content = new StringBuilder(); + while ((line = inputStreamReader.readLine()) != null) { + content.append(line); + index ++; + if (index == 0) { + continue; + } + } + inputStreamReader.close(); + inputStream.close(); + + + String jwkJson = content.toString(); JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson); List jsonWebKeys = jsonWebKeySet.getJsonWebKeys(); if (!jsonWebKeys.isEmpty()) { @@ -105,15 +144,15 @@ public class JwtUtils implements InitializingBean { rsaJsonWebKey = (RsaJsonWebKey) jsonWebKey; } } - } catch (Exception e) { - // ignored - } + } catch (Exception ignore) {} if (rsaJsonWebKey == null) { - log.warn("[API AUTH] 读取jwk.json失败,将使用新生成的随机RSA密钥对"); + log.warn("[API AUTH] 读取jwk.json失败,获取内容失败,将使用新生成的随机RSA密钥对"); // 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中 rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); // 给JWK一个密钥ID rsaJsonWebKey.setKeyId(keyId); + }else { + log.info("[API AUTH] 读取jwk.json成功"); } return rsaJsonWebKey; } From 3fe131eed3071c2f189b53829cb663028cca8161 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Tue, 11 Feb 2025 11:08:52 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E4=BB=A5=E6=9D=A5=E5=85=A8=E5=B1=80=E5=BD=95?= =?UTF-8?q?=E5=83=8F=E9=85=8D=E7=BD=AE=EF=BC=8C=20=E7=9B=AE=E5=89=8D?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BE=9D=E8=B5=96=EF=BC=8C=E7=9B=AE=E5=89=8D?= =?UTF-8?q?=E5=BD=95=E5=88=B6=E8=AE=A1=E5=88=92=E5=BC=80=E5=90=AF=E5=BD=95?= =?UTF-8?q?=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../genersoft/iot/vmp/common/InviteInfo.java | 5 +++- .../controller/CommonChannelController.java | 8 +++--- .../service/IGbChannelPlayService.java | 6 ++--- .../iot/vmp/gb28181/service/IPlayService.java | 2 +- .../impl/GbChannelPlayServiceImpl.java | 21 +++++++++------- .../service/impl/PlatformServiceImpl.java | 2 +- .../gb28181/service/impl/PlayServiceImpl.java | 25 ++++++++++++------- .../vmp/service/impl/MediaServiceImpl.java | 18 ++++++++----- .../service/impl/RecordPlanServiceImpl.java | 4 +-- .../controller/StreamProxyController.java | 2 +- .../service/IStreamProxyPlayService.java | 2 +- .../impl/StreamProxyPlayServiceImpl.java | 13 +++++----- 12 files changed, 65 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java index adf9643fd..41b363e4e 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -35,10 +35,12 @@ public class InviteInfo { private Long createTime; + private Boolean record; + public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId, String receiveIp, Integer receivePort, String streamMode, - InviteSessionType type, InviteSessionStatus status) { + InviteSessionType type, InviteSessionStatus status, Boolean record) { InviteInfo inviteInfo = new InviteInfo(); inviteInfo.setDeviceId(deviceId); inviteInfo.setChannelId(channelId); @@ -50,6 +52,7 @@ public class InviteInfo { inviteInfo.setType(type); inviteInfo.setStatus(status); inviteInfo.setMediaServerId(mediaServerId); + inviteInfo.setRecord(record); return inviteInfo; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java index 3db52a437..3590fc2a2 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java @@ -3,7 +3,10 @@ package com.genersoft.iot.vmp.gb28181.controller; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.security.JwtUtils; -import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; +import com.genersoft.iot.vmp.gb28181.bean.DeviceType; +import com.genersoft.iot.vmp.gb28181.bean.IndustryCodeType; +import com.genersoft.iot.vmp.gb28181.bean.NetworkIdentificationType; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupByGbDeviceParam; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToGroupParam; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelToRegionByGbDeviceParam; @@ -29,7 +32,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; import javax.servlet.http.HttpServletRequest; -import javax.sip.message.Response; import java.net.MalformedURLException; import java.net.URL; import java.util.List; @@ -261,7 +263,7 @@ public class CommonChannelController { result.setResult(WVPResult.fail(code, msg)); } }; - channelPlayService.play(channel, null, callback); + channelPlayService.play(channel, null, userSetting.getRecordSip(), callback); return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java index 4f5779396..ac58d28b9 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelPlayService.java @@ -10,11 +10,11 @@ public interface IGbChannelPlayService { void start(CommonGBChannel channel, InviteInfo inviteInfo, Platform platform, ErrorCallback callback); - void play(CommonGBChannel channel, Platform platform, ErrorCallback callback); + void play(CommonGBChannel channel, Platform platform, Boolean record, ErrorCallback callback); - void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback callback); + void playGbDeviceChannel(CommonGBChannel channel, Boolean record, ErrorCallback callback); - void playProxy(CommonGBChannel channel, ErrorCallback callback); + void playProxy(CommonGBChannel channel, Boolean record, ErrorCallback callback); void playPush(CommonGBChannel channel, String platformDeviceId, String platformName, ErrorCallback callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java index aa2ca3ab6..e208d3159 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java @@ -64,7 +64,7 @@ public interface IPlayService { void stop(InviteInfo inviteInfo); - void play(CommonGBChannel channel, ErrorCallback callback); + void play(CommonGBChannel channel, Boolean record, ErrorCallback callback); void playBack(CommonGBChannel channel, Long startTime, Long stopTime, ErrorCallback callback); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java index cfa63fd2f..4b828a2f1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelPlayServiceImpl.java @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.service.impl; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.enums.ChannelDataType; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.InviteInfo; import com.genersoft.iot.vmp.gb28181.bean.Platform; @@ -9,7 +10,6 @@ import com.genersoft.iot.vmp.gb28181.bean.PlayException; import com.genersoft.iot.vmp.gb28181.service.IGbChannelPlayService; import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.service.bean.ErrorCallback; -import com.genersoft.iot.vmp.service.bean.InviteErrorCode; import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; import com.genersoft.iot.vmp.streamPush.service.IStreamPushPlayService; import lombok.extern.slf4j.Slf4j; @@ -31,6 +31,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { @Autowired private IStreamPushPlayService streamPushPlayService; + @Autowired + private UserSetting userSetting; + @Override public void start(CommonGBChannel channel, InviteInfo inviteInfo, Platform platform, ErrorCallback callback) { @@ -40,7 +43,7 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { } log.info("[点播通用通道] 类型:{}, 通道: {}({})", inviteInfo.getSessionName(), channel.getGbName(), channel.getGbDeviceId()); if ("Play".equalsIgnoreCase(inviteInfo.getSessionName())) { - play(channel, platform, callback); + play(channel, platform, userSetting.getRecordSip(), callback); }else if ("Playback".equals(inviteInfo.getSessionName())) { if (channel.getDataType() == ChannelDataType.GB28181.value) { // 国标通道 @@ -90,13 +93,13 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { } @Override - public void play(CommonGBChannel channel, Platform platform, ErrorCallback callback) { + public void play(CommonGBChannel channel, Platform platform, Boolean record, ErrorCallback callback) { if (channel.getDataType() == ChannelDataType.GB28181.value) { // 国标通道 - playGbDeviceChannel(channel, callback); + playGbDeviceChannel(channel, record, callback); } else if (channel.getDataType() == ChannelDataType.STREAM_PROXY.value) { // 拉流代理 - playProxy(channel, callback); + playProxy(channel, record, callback); } else if (channel.getDataType() == ChannelDataType.STREAM_PUSH.value) { if (platform != null) { // 推流 @@ -113,10 +116,10 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { } @Override - public void playGbDeviceChannel(CommonGBChannel channel, ErrorCallback callback){ + public void playGbDeviceChannel(CommonGBChannel channel, Boolean record, ErrorCallback callback){ // 国标通道 try { - deviceChannelPlayService.play(channel, callback); + deviceChannelPlayService.play(channel, record, callback); } catch (PlayException e) { callback.run(e.getCode(), e.getMsg(), null); } catch (Exception e) { @@ -126,10 +129,10 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService { } @Override - public void playProxy(CommonGBChannel channel, ErrorCallback callback){ + public void playProxy(CommonGBChannel channel, Boolean record, ErrorCallback callback){ // 拉流代理通道 try { - streamProxyPlayService.start(channel.getDataDeviceId(), callback); + streamProxyPlayService.start(channel.getDataDeviceId(), record, callback); }catch (Exception e) { callback.run(Response.BUSY_HERE, "busy here", null); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java index 7d9f7e168..5a7abffa2 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java @@ -543,7 +543,7 @@ public class PlatformServiceImpl implements IPlatformService { // 初始化redis中的invite消息状态 InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channel.getGbId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), InviteSessionType.BROADCAST, - InviteSessionStatus.ready); + InviteSessionStatus.ready, userSetting.getRecordSip()); inviteStreamService.updateInviteInfo(inviteInfo); String timeOutTaskKey = UUID.randomUUID().toString(); dynamicTask.startDelay(timeOutTaskKey, () -> { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java index d6dbb4f18..ae8a86453 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -305,11 +305,11 @@ public class PlayServiceImpl implements IPlayService { throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道"); } - return play(mediaServerItem, device, channel, ssrc, callback); + return play(mediaServerItem, device, channel, ssrc, userSetting.getRecordSip(), callback); } - private SSRCInfo play(MediaServer mediaServerItem, Device device, DeviceChannel channel, String ssrc, - ErrorCallback callback) { + private SSRCInfo play(MediaServer mediaServerItem, Device device, DeviceChannel channel, String ssrc, Boolean record, + ErrorCallback callback) { if (mediaServerItem == null ) { if (callback != null) { callback.run(InviteErrorCode.ERROR_FOR_PARAMETER_ERROR.getCode(), @@ -322,7 +322,8 @@ public class PlayServiceImpl implements IPlayService { InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); if (inviteInfoInCatch != null ) { if (inviteInfoInCatch.getStreamInfo() == null) { - // 释放生成的ssrc,使用上一次申请的 + // 释放生成的ssrc,使用上一次申请的322 + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); // 点播发起了但是尚未成功, 仅注册回调等待结果即可 inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); @@ -432,7 +433,13 @@ public class PlayServiceImpl implements IPlayService { // 初始化redis中的invite消息状态 InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, - InviteSessionStatus.ready); + InviteSessionStatus.ready, userSetting.getRecordSip()); + if (record != null) { + inviteInfo.setRecord(record); + }else { + inviteInfo.setRecord(userSetting.getRecordSip()); + } + inviteStreamService.updateInviteInfo(inviteInfo); try { @@ -812,7 +819,7 @@ public class PlayServiceImpl implements IPlayService { // 初始化redis中的invite消息状态 InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK, - InviteSessionStatus.ready); + InviteSessionStatus.ready, userSetting.getRecordSip()); inviteStreamService.updateInviteInfo(inviteInfo); try { @@ -1018,7 +1025,7 @@ public class PlayServiceImpl implements IPlayService { // 初始化redis中的invite消息状态 InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, - InviteSessionStatus.ready); + InviteSessionStatus.ready, true); inviteStreamService.updateInviteInfo(inviteInfo); try { @@ -1647,7 +1654,7 @@ public class PlayServiceImpl implements IPlayService { } @Override - public void play(CommonGBChannel channel, ErrorCallback callback) { + public void play(CommonGBChannel channel, Boolean record, ErrorCallback callback) { Device device = deviceService.getDevice(channel.getDataDeviceId()); if (device == null) { log.warn("[点播] 未找到通道{}的设备信息", channel); @@ -1659,7 +1666,7 @@ public class PlayServiceImpl implements IPlayService { throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); } DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); - play(mediaServer, device, deviceChannel, null, callback); + play(mediaServer, device, deviceChannel, null, record, callback); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java index fcb7570fb..b0b707910 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java @@ -129,17 +129,19 @@ public class MediaServiceImpl implements IMediaService { ResultForOnPublish result = new ResultForOnPublish(); result.setEnable_audio(true); - // 是否录像 - if ("rtp".equals(app)) { - result.setEnable_mp4(userSetting.getRecordSip()); - } else { - result.setEnable_mp4(userSetting.getRecordPushLive()); - } // 国标流 if ("rtp".equals(app)) { InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(null, stream); + if (inviteInfo != null) { + result.setEnable_mp4(inviteInfo.getRecord()); + }else { + result.setEnable_mp4(userSetting.getRecordSip()); + } + + result.setEnable_mp4(inviteInfo.getRecord()); + // 单端口模式下修改流 ID if (!mediaServer.isRtpEnable() && inviteInfo == null) { String ssrc = String.format("%010d", Long.parseLong(stream, 16)); @@ -190,8 +192,12 @@ public class MediaServiceImpl implements IMediaService { } } else if (app.equals("broadcast")) { result.setEnable_audio(true); + result.setEnable_mp4(userSetting.getRecordSip()); } else if (app.equals("talk")) { result.setEnable_audio(true); + result.setEnable_mp4(userSetting.getRecordSip()); + }else { + result.setEnable_mp4(userSetting.getRecordPushLive()); } if (app.equalsIgnoreCase("rtp")) { String receiveKey = VideoManagerConstants.WVP_OTHER_RECEIVE_RTP_INFO + userSetting.getServerId() + "_" + stream; diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java index 1b5317a0c..2e97d7759 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RecordPlanServiceImpl.java @@ -66,7 +66,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService { return; } // 开启点播, - channelPlayService.play(channel, null, ((code, msg, streamInfo) -> { + channelPlayService.play(channel, null, true, ((code, msg, streamInfo) -> { if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) { log.info("[录像] 流离开时拉起需要录像的流, 开启成功, 通道ID: {}", channel.getGbId()); recordStreamMap.put(channel.getGbId(), streamInfo); @@ -110,7 +110,7 @@ public class RecordPlanServiceImpl implements IRecordPlanService { // 查找是否已经开启录像, 如果没有则开启录像 for (CommonGBChannel channel : channelList) { // 开启点播, - channelPlayService.play(channel, null, ((code, msg, streamInfo) -> { + channelPlayService.play(channel, null, true, ((code, msg, streamInfo) -> { if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) { log.info("[录像] 开启成功, 通道ID: {}", channel.getGbId()); recordStreamMap.put(channel.getGbId(), streamInfo); diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java index 4cf966a52..d7f8d62b5 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/controller/StreamProxyController.java @@ -187,7 +187,7 @@ public class StreamProxyController { @Parameter(name = "id", description = "代理Id", required = true) public StreamContent start(int id){ log.info("播放代理: {}", id); - StreamInfo streamInfo = streamProxyPlayService.start(id); + StreamInfo streamInfo = streamProxyPlayService.start(id, null, null); if (streamInfo == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), ErrorCode.ERROR100.getMsg()); }else { diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java index 18b1499a4..284c90bf2 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyPlayService.java @@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; public interface IStreamProxyPlayService { - StreamInfo start(int id); + StreamInfo start(int id, Boolean record, ErrorCallback callback); void start(int id, ErrorCallback callback); diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java index 9e5fa140e..3d4e75c69 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyPlayServiceImpl.java @@ -14,11 +14,6 @@ import com.genersoft.iot.vmp.streamProxy.dao.StreamProxyMapper; import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyPlayService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.ConcurrentHashMap; - -import javax.sip.message.Response; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; @@ -27,6 +22,9 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import javax.sip.message.Response; +import java.util.concurrent.ConcurrentHashMap; + /** * 视频代理业务 */ @@ -90,11 +88,14 @@ public class StreamProxyPlayServiceImpl implements IStreamProxyPlayService { } @Override - public StreamInfo start(int id) { + public StreamInfo start(int id, Boolean record, ErrorCallback callback) { StreamProxy streamProxy = streamProxyMapper.select(id); if (streamProxy == null) { throw new ControllerException(ErrorCode.ERROR404.getCode(), "代理信息未找到"); } + if (record != null) { + streamProxy.setEnableMp4(record); + } return startProxy(streamProxy); } From ea12e3465f20b2204e7875fd2d8550733d2e9631 Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Tue, 11 Feb 2025 16:06:18 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=E5=AF=B9=E8=AE=B2=E5=90=8E=E5=81=9C=E6=AD=A2=E7=82=B9=E6=92=AD?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/vmp/gb28181/bean/SsrcTransaction.java | 11 ++- .../controller/GBRecordController.java | 2 +- .../vmp/gb28181/service/IPlatformService.java | 2 +- .../service/impl/PlatformServiceImpl.java | 21 +++--- .../gb28181/service/impl/PlayServiceImpl.java | 70 ++++++++++--------- .../session/SipInviteSessionManager.java | 10 +-- .../gb28181/transmit/cmd/ISIPCommander.java | 5 +- .../cmd/ISIPCommanderForPlatform.java | 2 +- .../transmit/cmd/impl/SIPCommander.java | 31 ++++---- .../cmd/impl/SIPCommanderForPlatform.java | 12 ++-- .../request/impl/ByeRequestProcessor.java | 4 +- .../request/impl/InviteRequestProcessor.java | 3 +- .../cmd/BroadcastNotifyMessageHandler.java | 2 +- .../service/impl/MediaServerServiceImpl.java | 2 +- .../iot/vmp/service/bean/SSRCInfo.java | 4 +- .../vmp/service/impl/MediaServiceImpl.java | 2 +- .../service/impl/RtpServerServiceImpl.java | 4 +- .../vmp/web/gb28181/ApiStreamController.java | 2 +- 18 files changed, 98 insertions(+), 91 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java index 893db116e..2a051a771 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java @@ -27,6 +27,11 @@ public class SsrcTransaction { */ private String callId; + /** + * 关联的流应用名 + */ + private String app; + /** * 关联的流ID */ @@ -52,12 +57,13 @@ public class SsrcTransaction { */ private InviteSessionType type; - public static SsrcTransaction buildForDevice(String deviceId, Integer channelId, String callId, String stream, + public static SsrcTransaction buildForDevice(String deviceId, Integer channelId, String callId, String app, String stream, String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type) { SsrcTransaction ssrcTransaction = new SsrcTransaction(); ssrcTransaction.setDeviceId(deviceId); ssrcTransaction.setChannelId(channelId); ssrcTransaction.setCallId(callId); + ssrcTransaction.setApp(app); ssrcTransaction.setStream(stream); ssrcTransaction.setMediaServerId(mediaServerId); ssrcTransaction.setSsrc(ssrc); @@ -65,13 +71,14 @@ public class SsrcTransaction { ssrcTransaction.setType(type); return ssrcTransaction; } - public static SsrcTransaction buildForPlatform(String platformId, Integer channelId, String callId, String stream, + public static SsrcTransaction buildForPlatform(String platformId, Integer channelId, String callId, String app,String stream, String ssrc, String mediaServerId, SIPResponse response, InviteSessionType type) { SsrcTransaction ssrcTransaction = new SsrcTransaction(); ssrcTransaction.setPlatformId(platformId); ssrcTransaction.setChannelId(channelId); ssrcTransaction.setCallId(callId); ssrcTransaction.setStream(stream); + ssrcTransaction.setApp(app); ssrcTransaction.setMediaServerId(mediaServerId); ssrcTransaction.setSsrc(ssrc); ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java index e48ae616e..102b7ee91 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GBRecordController.java @@ -195,7 +195,7 @@ public class GBRecordController { } try { - cmder.streamByeCmd(device, channelId, stream, null); + cmder.streamByeCmd(device, channelId, "rtp", stream, null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.warn("[停止历史媒体下载]停止历史媒体下载,发送BYE失败 {}", e.getMessage()); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java index 7858269cf..36b6b63ae 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformService.java @@ -76,7 +76,7 @@ public interface IPlatformService { /** * 语音喊话回复BYE */ - void stopBroadcast(Platform platform, CommonGBChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem); + void stopBroadcast(Platform platform, CommonGBChannel channel, String app, String stream, boolean sendBye, MediaServer mediaServerItem); void addSimulatedSubscribeInfo(Platform parentPlatform); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java index 5a7abffa2..3a1668d68 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java @@ -553,14 +553,14 @@ public class PlatformServiceImpl implements IPlatformService { log.info("[国标级联] 发起语音喊话 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", platform.getServerGBId(), channel.getGbDeviceId(), ssrcInfo.getPort(), ssrcInfo.getSsrc()); // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 try { - commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getStream(), null, null); + commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getApp(), ssrcInfo.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); } finally { timeoutCallback.run(1, "收流超时"); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); } } @@ -638,7 +638,7 @@ public class PlatformServiceImpl implements IPlatformService { if (!result) { try { log.warn("[Invite 200OK] 更新ssrc失败,停止喊话 {}/{}", platform.getServerGBId(), channel.getGbDeviceId()); - commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getStream(), null, null); + commanderForPlatform.streamByeCmd(platform, channel, ssrcInfo.getApp(), ssrcInfo.getStream(), null, null); } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { log.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); } @@ -647,7 +647,7 @@ public class PlatformServiceImpl implements IPlatformService { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), "下级自定义了ssrc,重新设置收流信息失败", null); @@ -687,12 +687,13 @@ public class PlatformServiceImpl implements IPlatformService { if (ssrcInResponse != null) { // 单端口 // 重新订阅流上线 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(inviteInfo.getStream()); - sessionManager.removeByStream(inviteInfo.getStream()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(ssrcInfo.getApp(), inviteInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), inviteInfo.getStream()); inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); ssrcTransaction.setPlatformId(platform.getServerGBId()); ssrcTransaction.setChannelId(channel.getGbId()); + ssrcTransaction.setApp(ssrcInfo.getApp()); ssrcTransaction.setStream(inviteInfo.getStream()); ssrcTransaction.setSsrc(ssrcInResponse); ssrcTransaction.setMediaServerId(mediaServerItem.getId()); @@ -744,7 +745,7 @@ public class PlatformServiceImpl implements IPlatformService { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); @@ -755,11 +756,11 @@ public class PlatformServiceImpl implements IPlatformService { } @Override - public void stopBroadcast(Platform platform, CommonGBChannel channel, String stream, boolean sendBye, MediaServer mediaServerItem) { + public void stopBroadcast(Platform platform, CommonGBChannel channel, String app, String stream, boolean sendBye, MediaServer mediaServerItem) { try { if (sendBye) { - commanderForPlatform.streamByeCmd(platform, channel, stream, null, null); + commanderForPlatform.streamByeCmd(platform, channel, app, stream, null, null); } } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { log.warn("[消息发送失败] 停止语音对讲, 平台:{},通道:{}", platform.getId(), channel.getGbDeviceId() ); @@ -771,7 +772,7 @@ public class PlatformServiceImpl implements IPlatformService { mediaServerService.releaseSsrc(mediaServerItem.getId(), inviteInfo.getSsrcInfo().getSsrc()); inviteStreamService.removeInviteInfo(inviteInfo); } - sessionManager.removeByStream(stream); + sessionManager.removeByStream(app, stream); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java index ae8a86453..2ef0851f4 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -188,7 +188,7 @@ public class PlayServiceImpl implements IPlayService { DeviceChannel channel = deviceChannelService.getOneById(sendRtpInfo.getChannelId()); try { if (device != null && channel != null) { - cmder.streamByeCmd(device, channel.getDeviceId(), event.getStream(), sendRtpInfo.getCallId()); + cmder.streamByeCmd(device, channel.getDeviceId(), event.getApp(), event.getStream(), sendRtpInfo.getCallId(), null); if (sendRtpInfo.getPlayType().equals(InviteStreamType.BROADCAST) || sendRtpInfo.getPlayType().equals(InviteStreamType.TALK)) { AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(channel.getId()); @@ -405,14 +405,14 @@ public class PlayServiceImpl implements IPlayService { } inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, code, msg, null); inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(streamId); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream("rtp", streamId); if (ssrcTransaction != null) { try { - cmder.streamByeCmd(device, channel.getDeviceId(), streamId, null); + cmder.streamByeCmd(device, channel.getDeviceId(),"rtp", streamId, null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); } finally { - sessionManager.removeByStream(streamId); + sessionManager.removeByStream("rtp", streamId); } } } @@ -450,7 +450,7 @@ public class PlayServiceImpl implements IPlayService { log.info("[点播失败]{}:{} deviceId: {}, channelId:{}",event.statusCode, event.msg, device.getDeviceId(), channel.getDeviceId()); receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); if (callback != null) { callback.run(event.statusCode, event.msg, null); } @@ -462,7 +462,7 @@ public class PlayServiceImpl implements IPlayService { } catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 点播消息: {}", e.getMessage()); receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); if (callback != null) { callback.run(InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getCode(), InviteErrorCode.ERROR_FOR_SIP_SENDING_FAILED.getMsg(), null); @@ -513,13 +513,13 @@ public class PlayServiceImpl implements IPlayService { timeoutCallback.run(); // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 try { - cmder.streamByeCmd(device, channel.getDeviceId(), stream, null); + cmder.streamByeCmd(device, channel.getDeviceId(), null, null, callId, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[语音对讲]超时, 发送BYE失败 {}", e.getMessage()); } finally { timeoutCallback.run(); mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); - sessionManager.removeByStream(sendRtpInfo.getStream()); + sessionManager.removeByStream(sendRtpInfo.getApp(), sendRtpInfo.getStream()); } }, userSetting.getPlayTimeout()); @@ -528,7 +528,7 @@ public class PlayServiceImpl implements IPlayService { if (localPort == null || localPort <= 0) { timeoutCallback.run(); mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); - sessionManager.removeByStream(sendRtpInfo.getStream()); + sessionManager.removeByStream(sendRtpInfo.getApp(), sendRtpInfo.getStream()); return; } sendRtpInfo.setPort(localPort); @@ -563,7 +563,7 @@ public class PlayServiceImpl implements IPlayService { sendRtpInfo.setCallId(response.getCallIdHeader().getCallId()); sendRtpServerService.update(sendRtpInfo); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpInfo.getChannelId(), "talk", + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpInfo.getChannelId(), "talk", sendRtpInfo.getApp(), sendRtpInfo.getStream(), sendRtpInfo.getSsrc(), sendRtpInfo.getMediaServerId(), response, InviteSessionType.TALK); @@ -580,7 +580,7 @@ public class PlayServiceImpl implements IPlayService { mediaServerService.closeRTPServer(mediaServerItem, sendRtpInfo.getStream()); // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); - sessionManager.removeByStream(sendRtpInfo.getStream()); + sessionManager.removeByStream(sendRtpInfo.getApp(), sendRtpInfo.getStream()); errorEvent.response(event); }, userSetting.getPlayTimeout().longValue()); } catch (InvalidArgumentException | SipException | ParseException e) { @@ -591,7 +591,7 @@ public class PlayServiceImpl implements IPlayService { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpInfo.getSsrc()); - sessionManager.removeByStream(sendRtpInfo.getStream()); + sessionManager.removeByStream(sendRtpInfo.getApp(), sendRtpInfo.getStream()); SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; eventResult.statusCode = -1; @@ -634,7 +634,7 @@ public class PlayServiceImpl implements IPlayService { if (!result) { // 主动连接失败,结束流程, 清理数据 receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); inviteStreamService.call(InviteSessionType.BROADCAST, channel.getId(), null, @@ -645,7 +645,7 @@ public class PlayServiceImpl implements IPlayService { log.error("[TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channel.getDeviceId(), e); receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); @@ -789,14 +789,14 @@ public class PlayServiceImpl implements IPlayService { } inviteStreamService.call(InviteSessionType.PLAYBACK, channel.getId(), null, code, msg, null); inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAYBACK, channel.getId()); - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream("rtp", stream); if (ssrcTransaction != null) { try { - cmder.streamByeCmd(device, channel.getDeviceId(), stream, null); + cmder.streamByeCmd(device, channel.getDeviceId(),"rtp", stream, null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[录像回放] 发送BYE失败 {}", e.getMessage()); } finally { - sessionManager.removeByStream(stream); + sessionManager.removeByStream("rtp", stream); } } } @@ -835,7 +835,7 @@ public class PlayServiceImpl implements IPlayService { } receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); inviteStreamService.removeInviteInfo(inviteInfo); }, userSetting.getPlayTimeout().longValue()); } catch (InvalidArgumentException | SipException | ParseException e) { @@ -844,7 +844,7 @@ public class PlayServiceImpl implements IPlayService { callback.run(InviteErrorCode.FAIL.getCode(), e.getMessage(), null); } receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); inviteStreamService.removeInviteInfo(inviteInfo); } } @@ -890,7 +890,7 @@ public class PlayServiceImpl implements IPlayService { if (!result) { try { log.warn("[Invite 200OK] 更新ssrc失败,停止点播 {}/{}", device.getDeviceId(), channel.getDeviceId()); - cmder.streamByeCmd(device, channel.getDeviceId(), ssrcInfo.getStream(), null, null); + cmder.streamByeCmd(device, channel.getDeviceId(), ssrcInfo.getApp(), ssrcInfo.getStream(), null, null); } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { log.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); } @@ -898,7 +898,7 @@ public class PlayServiceImpl implements IPlayService { // 释放ssrc mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), "下级自定义了ssrc,重新设置收流信息失败", null); @@ -924,13 +924,15 @@ public class PlayServiceImpl implements IPlayService { if (ssrcInResponse != null) { // 单端口 // 重新订阅流上线 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(inviteInfo.getStream()); - sessionManager.removeByStream(inviteInfo.getStream()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream("rtp", inviteInfo.getStream()); + sessionManager.removeByStream("rtp", inviteInfo.getStream()); inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); ssrcTransaction.setDeviceId(device.getDeviceId()); ssrcTransaction.setChannelId(ssrcTransaction.getChannelId()); ssrcTransaction.setCallId(ssrcTransaction.getCallId()); ssrcTransaction.setSsrc(ssrcInResponse); + ssrcTransaction.setApp("rtp"); + ssrcTransaction.setStream(inviteInfo.getStream()); ssrcTransaction.setMediaServerId(mediaServerItem.getId()); ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo((SIPResponse) responseEvent.getResponse())); ssrcTransaction.setType(inviteSessionType); @@ -993,14 +995,14 @@ public class PlayServiceImpl implements IPlayService { inviteStreamService.call(InviteSessionType.DOWNLOAD, channel.getId(), null, code, msg, null); inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.DOWNLOAD, channel.getId()); if (result != null && result.getSsrcInfo() != null) { - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(result.getSsrcInfo().getStream()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(result.getSsrcInfo().getApp(), result.getSsrcInfo().getStream()); if (ssrcTransaction != null) { try { - cmder.streamByeCmd(device, channel.getDeviceId(), ssrcTransaction.getStream(), null); + cmder.streamByeCmd(device, channel.getDeviceId(), ssrcTransaction.getApp(), ssrcTransaction.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[录像下载] 发送BYE失败 {}", e.getMessage()); } finally { - sessionManager.removeByStream(ssrcTransaction.getStream()); + sessionManager.removeByStream(ssrcTransaction.getApp(), ssrcTransaction.getStream()); } } } @@ -1034,7 +1036,7 @@ public class PlayServiceImpl implements IPlayService { // 对方返回错误 callback.run(InviteErrorCode.FAIL.getCode(), String.format("录像下载失败, 错误码: %s, %s", eventResult.statusCode, eventResult.msg), null); receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); inviteStreamService.removeInviteInfo(inviteInfo); }, eventResult ->{ // 处理收到200ok后的TCP主动连接以及SSRC不一致的问题 @@ -1066,7 +1068,7 @@ public class PlayServiceImpl implements IPlayService { log.error("[命令发送失败] 录像下载: {}", e.getMessage()); callback.run(InviteErrorCode.FAIL.getCode(),e.getMessage(), null); receiveRtpServerService.closeRTPServer(mediaServerItem, ssrcInfo); - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); inviteStreamService.removeInviteInfo(inviteInfo); } } @@ -1202,8 +1204,8 @@ public class PlayServiceImpl implements IPlayService { continue; } try { - cmder.streamByeCmd(device, deviceChannel.getDeviceId(), - ssrcTransaction.getStream(), null); + cmder.streamByeCmd(device, deviceChannel.getDeviceId(), ssrcTransaction.getApp(), + ssrcTransaction.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage()); @@ -1537,10 +1539,10 @@ public class PlayServiceImpl implements IPlayService { ssrcFactory.releaseSsrc(mediaServerId, sendRtpInfo.getSsrc()); - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(sendRtpInfo.getStream()); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(sendRtpInfo.getApp(), sendRtpInfo.getStream()); if (ssrcTransaction != null) { try { - cmder.streamByeCmd(device, channel.getDeviceId(), sendRtpInfo.getStream(), null); + cmder.streamByeCmd(device, channel.getDeviceId(), sendRtpInfo.getApp(), sendRtpInfo.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.info("[语音对讲] 停止消息发送失败,可能已经停止"); } @@ -1607,7 +1609,7 @@ public class PlayServiceImpl implements IPlayService { if (InviteSessionStatus.ok == inviteInfo.getStatus()) { try { log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); - cmder.streamByeCmd(device, channel.getDeviceId(), inviteInfo.getStream(), null, null); + cmder.streamByeCmd(device, channel.getDeviceId(), "rtp", inviteInfo.getStream(), null, null); } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { log.error("[命令发送失败] 停止点播/回放/下载, 发送BYE: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); @@ -1639,7 +1641,7 @@ public class PlayServiceImpl implements IPlayService { if (InviteSessionStatus.ok == inviteInfo.getStatus()) { try { log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); - cmder.streamByeCmd(device, channel.getDeviceId(), inviteInfo.getStream(), null, null); + cmder.streamByeCmd(device, channel.getDeviceId(), "rtp", inviteInfo.getStream(), null, null); } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { log.warn("[命令发送失败] 停止点播/回放/下载, 发送BYE: {}", e.getMessage()); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java index 3a3bdaef3..69dd332e2 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java @@ -27,15 +27,15 @@ public class SipInviteSessionManager { */ public void put(SsrcTransaction ssrcTransaction){ redisTemplate.opsForHash().put(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() - , ssrcTransaction.getStream(), ssrcTransaction); + , ssrcTransaction.getApp() + ssrcTransaction.getStream(), ssrcTransaction); redisTemplate.opsForHash().put(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() , ssrcTransaction.getCallId(), ssrcTransaction); } - public SsrcTransaction getSsrcTransactionByStream(String stream){ + public SsrcTransaction getSsrcTransactionByStream(String app, String stream){ String key = VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(); - return (SsrcTransaction)redisTemplate.opsForHash().get(key, stream); + return (SsrcTransaction)redisTemplate.opsForHash().get(key, app + stream); } public SsrcTransaction getSsrcTransactionByCallId(String callId){ @@ -56,8 +56,8 @@ public class SipInviteSessionManager { return result; } - public void removeByStream(String stream) { - SsrcTransaction ssrcTransaction = getSsrcTransactionByStream(stream); + public void removeByStream(String app, String stream) { + SsrcTransaction ssrcTransaction = getSsrcTransactionByStream(app, stream); if (ssrcTransaction == null ) { return; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java index dca16ba84..b21dba9cb 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java @@ -129,13 +129,10 @@ public interface ISIPCommander { /** * 视频流停止 */ - void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; + void streamByeCmd(Device device, String channelId, String app, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; void talkStreamCmd(MediaServer mediaServerItem, SendRtpInfo sendRtpItem, Device device, DeviceChannel channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, Long timeout) throws InvalidArgumentException, SipException, ParseException; - - void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException; - void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; /** diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java index 6a80dd3ac..c94a072f0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java @@ -144,7 +144,7 @@ public interface ISIPCommanderForPlatform { void streamByeCmd(Platform platform, SendRtpInfo sendRtpItem, CommonGBChannel channel) throws SipException, InvalidArgumentException, ParseException; - void streamByeCmd(Platform platform, CommonGBChannel channel, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; + void streamByeCmd(Platform platform, CommonGBChannel channel, String app, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; void broadcastInviteCmd(Platform platform, CommonGBChannel channel, String sourceId, MediaServer mediaServerItem, SSRCInfo ssrcInfo, HookSubscribe.Event event, SipSubscribe.Event okEvent, diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 9e5fb182b..eed06646b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -332,14 +332,15 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); errorEvent.response(e); }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); String callId = response.getCallIdHeader().getCallId(); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), callId, stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), + callId,ssrcInfo.getApp(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAY); sessionManager.put(ssrcTransaction); okEvent.response(e); @@ -435,7 +436,9 @@ public class SIPCommander implements ISIPCommander { ResponseEvent responseEvent = (ResponseEvent) event.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), - channel.getId(), sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.PLAYBACK); + channel.getId(), sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), + device.getTransport()).getCallId(), ssrcInfo.getApp(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), + mediaServerItem.getId(), response, InviteSessionType.PLAYBACK); sessionManager.put(ssrcTransaction); okEvent.response(event); }, timeout); @@ -526,7 +529,9 @@ public class SIPCommander implements ISIPCommander { SIPResponse response = (SIPResponse) responseEvent.getResponse(); String contentString =new String(response.getRawContent()); String ssrc = SipUtils.getSsrcFromSdp(contentString); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), + response.getCallIdHeader().getCallId(), ssrcInfo.getApp(), ssrcInfo.getStream(), ssrc, + mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); sessionManager.put(ssrcTransaction); okEvent.response(event); }, timeout); @@ -586,32 +591,24 @@ public class SIPCommander implements ISIPCommander { Request request = headerProvider.createInviteRequest(device, channel.getDeviceId(), content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sendRtpItem.getSsrc(), callIdHeader); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { - sessionManager.removeByStream(sendRtpItem.getStream()); + sessionManager.removeByStream(sendRtpItem.getApp(), sendRtpItem.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); errorEvent.response(e); }), e -> { // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), "talk", stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), channel.getId(), "talk",sendRtpItem.getApp(), stream, sendRtpItem.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.TALK); sessionManager.put(ssrcTransaction); okEvent.response(e); }, timeout); } - /** - * 视频流停止, 不使用回调 - */ - @Override - public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException { - streamByeCmd(device, channelId, stream, callId, null); - } - /** * 视频流停止 */ @Override - public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { + public void streamByeCmd(Device device, String channelId, String app, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { if (device == null) { log.warn("[发送BYE] device为null"); return; @@ -620,7 +617,7 @@ public class SIPCommander implements ISIPCommander { if (callId != null) { ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callId); }else if (stream != null) { - ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + ssrcTransaction = sessionManager.getSsrcTransactionByStream(app, stream); } if (ssrcTransaction == null) { @@ -1362,7 +1359,7 @@ public class SIPCommander implements ISIPCommander { @Override public void playbackControlCmd(Device device, DeviceChannel channel, String stream, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream("rtp", stream); if (ssrcTransaction == null) { log.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), stream); return; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java index 2dbfdf192..5e5a207b6 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java @@ -7,7 +7,6 @@ import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; @@ -644,13 +643,13 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { } @Override - public void streamByeCmd(Platform platform, CommonGBChannel channel, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { + public void streamByeCmd(Platform platform, CommonGBChannel channel, String app, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { SsrcTransaction ssrcTransaction = null; if (callId != null) { ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callId); }else if (stream != null) { - ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + ssrcTransaction = sessionManager.getSsrcTransactionByStream(app, stream); } if (ssrcTransaction == null) { throw new SsrcTransactionNotFoundException(platform.getServerGBId(), channel.getGbDeviceId(), callId, stream); @@ -658,7 +657,7 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); - sessionManager.removeByStream(ssrcTransaction.getStream()); + sessionManager.removeByStream(ssrcTransaction.getApp(), ssrcTransaction.getStream()); Request byteRequest = headerProviderPlatformProvider.createByteRequest(platform, channel.getGbDeviceId(), ssrcTransaction.getSipTransactionInfo()); sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), byteRequest, null, okEvent); @@ -743,14 +742,15 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), ssrcInfo.getSsrc(), callIdHeader); sipSender.transmitRequest(sipLayer.getLocalIp(platform.getDeviceIp()), request, (e -> { - sessionManager.removeByStream(ssrcInfo.getStream()); + sessionManager.removeByStream(ssrcInfo.getApp(), ssrcInfo.getStream()); mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); subscribe.removeSubscribe(hook); errorEvent.response(e); }), e -> { ResponseEvent responseEvent = (ResponseEvent) e.event; SIPResponse response = (SIPResponse) responseEvent.getResponse(); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForPlatform(platform.getServerGBId(), channel.getGbId(), callIdHeader.getCallId(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForPlatform(platform.getServerGBId(), channel.getGbId(), + callIdHeader.getCallId(), ssrcInfo.getApp(), stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, InviteSessionType.BROADCAST); sessionManager.put(ssrcTransaction); okEvent.response(e); }); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 5fbe6525b..26a886b78 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -169,7 +169,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In } try { log.info("[停止点播] {}/{}", sendRtpItem.getTargetId(), sendRtpItem.getChannelId()); - cmder.streamByeCmd(device, deviceChannel.getDeviceId(), streamId, null); + cmder.streamByeCmd(device, deviceChannel.getDeviceId(), sendRtpItem.getApp(), sendRtpItem.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { log.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); @@ -196,7 +196,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In return; } String mediaServerId = ssrcTransaction.getMediaServerId(); - platformService.stopBroadcast(platform, channel, ssrcTransaction.getStream(), false, + platformService.stopBroadcast(platform, channel, ssrcTransaction.getApp(), ssrcTransaction.getStream(), false, mediaServerService.getOne(mediaServerId)); DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); Device device = deviceService.getDevice(channel.getDataDeviceId()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index 75a8dcd43..34545b7cc 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -624,7 +624,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.Ok); audioBroadcastCatch.setSipTransactionInfoByRequest(sipResponse); audioBroadcastManager.update(audioBroadcastCatch); - SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpItem.getChannelId(), request.getCallIdHeader().getCallId(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST); + SsrcTransaction ssrcTransaction = SsrcTransaction.buildForDevice(device.getDeviceId(), sendRtpItem.getChannelId(), + request.getCallIdHeader().getCallId(), sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST); sessionManager.put(ssrcTransaction); // 开启发流,大华在收到200OK后就会开始建立连接 if (!device.isBroadcastPushAfterAck()) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java index c16fc7e0b..71e08fe57 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java @@ -151,7 +151,7 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp log.info("[国标级联] 语音喊话 设备正在使用中 platform: {}, channel: {}", platform.getServerGBId(), channel.getGbDeviceId()); // 查看语音通道已经建立且已经占用 回复BYE - platformService.stopBroadcast(platform, channel, hookData.getStream(), true, hookData.getMediaServer()); + platformService.stopBroadcast(platform, channel, hookData.getApp(), hookData.getStream(), true, hookData.getMediaServer()); }else { // 查看语音通道已经建立但是未占用 broadcastCatch.setApp(hookData.getApp()); diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java index c6c597b07..a088366a7 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java @@ -197,7 +197,7 @@ public class MediaServerServiceImpl implements IMediaServerService { } else { rtpServerPort = mediaServer.getRtpProxyPort(); } - return new SSRCInfo(rtpServerPort, ssrc, streamId, null); + return new SSRCInfo(rtpServerPort, ssrc, "rtp", streamId, null); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java index ac0e2b3b6..c35ceb550 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java @@ -7,12 +7,14 @@ public class SSRCInfo { private int port; private String ssrc; + private String app; private String Stream; private String timeOutTaskKey; - public SSRCInfo(int port, String ssrc, String stream, String timeOutTaskKey) { + public SSRCInfo(int port, String ssrc, String app, String stream, String timeOutTaskKey) { this.port = port; this.ssrc = ssrc; + this.app = app; this.Stream = stream; this.timeOutTaskKey = timeOutTaskKey; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java index b0b707910..2044e7623 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java @@ -154,7 +154,7 @@ public class MediaServiceImpl implements IMediaService { } // 设置音频信息及录制信息 - SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(stream); + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByStream(app, stream); if (ssrcTransaction != null ) { // 为录制国标模拟一个鉴权信息, 方便后续写入录像文件时使用 diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java index ccd7780ec..86099262d 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/RtpServerServiceImpl.java @@ -117,11 +117,11 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService { // 设置流超时的定时任务 String timeOutTaskKey = UUID.randomUUID().toString(); - SSRCInfo ssrcInfo = new SSRCInfo(rtpServerPort, ssrc, streamId, timeOutTaskKey); + SSRCInfo ssrcInfo = new SSRCInfo(rtpServerPort, ssrc, "rtp", streamId, timeOutTaskKey); OpenRTPServerResult openRTPServerResult = new OpenRTPServerResult(); openRTPServerResult.setSsrcInfo(ssrcInfo); - Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", streamId, rtpServerParam.getMediaServerItem().getId()); + Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, ssrcInfo.getApp(), streamId, rtpServerParam.getMediaServerItem().getId()); dynamicTask.startDelay(timeOutTaskKey, () -> { // 收流超时 // 释放ssrc diff --git a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java index a4cca7934..931076561 100644 --- a/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java +++ b/src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java @@ -244,7 +244,7 @@ public class ApiStreamController { } try { - cmder.streamByeCmd(device, code, inviteInfo.getStream(), null); + cmder.streamByeCmd(device, code, "rtp", inviteInfo.getStream(), null, null); } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { JSONObject result = new JSONObject(); result.put("error","发送BYE失败:" + e.getMessage()); From d08248aae9e4ae85fd11573a4a32f936fe1a210c Mon Sep 17 00:00:00 2001 From: lin <648540858@qq.com> Date: Tue, 11 Feb 2025 18:16:05 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BF=83=E8=B7=B3?= =?UTF-8?q?=E9=97=B4=E9=9A=94=E7=9A=84=E8=AE=BE=E7=BD=AE=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E7=8A=B6=E6=80=81=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/vmp/gb28181/bean/Device.java | 16 ++++++- .../vmp/gb28181/controller/DeviceConfig.java | 4 +- .../iot/vmp/gb28181/dao/DeviceMapper.java | 12 +++-- .../vmp/gb28181/service/IDeviceService.java | 1 + .../service/impl/DeviceServiceImpl.java | 47 ++++++++++++------- .../cmd/KeepaliveNotifyMessageHandler.java | 27 +++++------ .../ConfigDownloadResponseMessageHandler.java | 23 ++++++++- web_src/src/components/DeviceList.vue | 31 ++++++++++++ 数据库/2.7.3/初始化-mysql-2.7.3.sql | 4 +- .../2.7.3/初始化-postgresql-kingbase-2.7.3.sql | 4 +- 数据库/2.7.3/更新-mysql-2.7.3.sql | 9 +++- .../2.7.3/更新-postgresql-kingbase-2.7.3.sql | 9 +++- 12 files changed, 144 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java index 1e783e867..7e1286c5a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java @@ -104,7 +104,21 @@ public class Device { * 心跳间隔 */ @Schema(description = "心跳间隔") - private int keepaliveIntervalTime; + private Integer heartBeatInterval; + + + /** + * 心跳超时次数 + */ + @Schema(description = "心跳超时次数") + private Integer heartBeatCount; + + + /** + * 定位功能支持情况 + */ + @Schema(description = "定位功能支持情况。取值:0-不支持;1-支持 GPS定位;2-支持北斗定位(可选,默认取值为0") + private Integer positionCapability; /** * 通道个数 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java index f6e7ddcb8..d3b40e81b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceConfig.java @@ -65,7 +65,7 @@ public class DeviceConfig { @Parameter(name = "heartBeatInterval", description = "心跳间隔") @Parameter(name = "heartBeatCount", description = "心跳计数") public DeferredResult homePositionApi(@PathVariable String deviceId, - String channelId, + @RequestParam(required = false) String channelId, @RequestParam(required = false) String name, @RequestParam(required = false) String expiration, @RequestParam(required = false) String heartBeatInterval, @@ -124,7 +124,7 @@ public class DeviceConfig { if (log.isDebugEnabled()) { log.debug("设备状态查询API调用"); } - String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : deviceId + channelId); String uuid = UUID.randomUUID().toString(); Device device = deviceService.getDeviceByDeviceId(deviceId); try { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java index 3f50e4358..145469b76 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java @@ -66,7 +66,9 @@ public interface DeviceMapper { "expires," + "register_time," + "keepalive_time," + - "keepalive_interval_time," + + "heart_beat_interval," + + "heart_beat_count," + + "position_capability," + "create_time," + "update_time," + "charset," + @@ -96,7 +98,9 @@ public interface DeviceMapper { "#{expires}," + "#{registerTime}," + "#{keepaliveTime}," + - "#{keepaliveIntervalTime}," + + "#{heartBeatInterval}," + + "#{heartBeatCount}," + + "#{positionCapability}," + "#{createTime}," + "#{updateTime}," + "#{charset}," + @@ -128,7 +132,9 @@ public interface DeviceMapper { ", on_line=#{onLine}" + ", register_time=#{registerTime}" + ", keepalive_time=#{keepaliveTime}" + - ", keepalive_interval_time=#{keepaliveIntervalTime}" + + ", heart_beat_interval=#{heartBeatInterval}" + + ", position_capability=#{positionCapability}" + + ", heart_beat_count=#{heartBeatCount}" + ", expires=#{expires}" + "WHERE device_id=#{deviceId}"+ " "}) diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java index 09a861790..33f5e524d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceService.java @@ -166,4 +166,5 @@ public interface IDeviceService { void subscribeMobilePosition(int id, int cycle, int interval); + void updateDeviceHeartInfo(Device device); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java index 17dadef10..43087b545 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java @@ -41,6 +41,7 @@ import javax.sip.SipException; import java.text.ParseException; import java.time.Instant; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -110,9 +111,12 @@ public class DeviceServiceImpl implements IDeviceService { } device.setUpdateTime(now); device.setKeepaliveTime(now); - if (device.getKeepaliveIntervalTime() == 0) { - // 默认心跳间隔60 - device.setKeepaliveIntervalTime(60); + if (device.getHeartBeatCount() == null) { + // 读取设备配置, 获取心跳间隔和心跳超时次数, 在次之前暂时设置为默认值 + device.setHeartBeatCount(3); + device.setHeartBeatInterval(60); + device.setPositionCapability(0); + } if (sipTransactionInfo != null) { device.setSipTransactionInfo(sipTransactionInfo); @@ -132,6 +136,7 @@ public class DeviceServiceImpl implements IDeviceService { redisCatchStorage.updateDevice(device); try { commander.deviceInfoQuery(device); + commander.deviceConfigQuery(device, null, "BasicParam", null); } catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); } @@ -178,18 +183,8 @@ public class DeviceServiceImpl implements IDeviceService { // 刷新过期任务 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线 - dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3); - -// -// try { -// cmder.alarmSubscribe(device, 600, "0", "4", "0", "2023-7-27T00:00:00", "2023-7-28T00:00:00"); -// } catch (InvalidArgumentException e) { -// throw new RuntimeException(e); -// } catch (SipException e) { -// throw new RuntimeException(e); -// } catch (ParseException e) { -// throw new RuntimeException(e); -// } + dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "三次心跳超时"), + device.getHeartBeatInterval() * 1000 * device.getHeartBeatCount()); } @@ -202,7 +197,7 @@ public class DeviceServiceImpl implements IDeviceService { return; } log.info("[设备离线] device:{}, 当前心跳间隔: {}, 上次心跳时间:{}, 上次注册时间: {}", deviceId, - device.getKeepaliveIntervalTime(), device.getKeepaliveTime(), device.getRegisterTime()); + device.getHeartBeatInterval(), device.getKeepaliveTime(), device.getRegisterTime()); String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId; dynamicTask.stop(registerExpireTaskKey); if (device.isOnLine()) { @@ -588,4 +583,24 @@ public class DeviceServiceImpl implements IDeviceService { redisCatchStorage.updateDevice(device); } } + + @Override + public void updateDeviceHeartInfo(Device device) { + Device deviceInDb = deviceMapper.query(device.getId()); + if (deviceInDb == null) { + return; + } + if (!Objects.equals(deviceInDb.getHeartBeatCount(), device.getHeartBeatCount()) + || !Objects.equals(deviceInDb.getHeartBeatInterval(), device.getHeartBeatInterval())) { + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "三次心跳超时"), + device.getHeartBeatInterval() * 1000 * device.getHeartBeatCount()); + deviceInDb.setHeartBeatCount(device.getHeartBeatCount()); + deviceInDb.setHeartBeatInterval(device.getHeartBeatInterval()); + deviceInDb.setPositionCapability(device.getPositionCapability()); + updateDevice(deviceInDb); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java index 3232cae2e..04d6a0ca4 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -3,16 +3,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; +import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.ObjectUtils; import org.dom4j.Element; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; @@ -95,10 +97,10 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp } Device device = sipMsgInfo.getDevice(); SIPRequest request = (SIPRequest) evt.getRequest(); - if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) { - log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); - return; - } +// if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) { +// log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); +// return; +// } RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { @@ -114,14 +116,6 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp }); } } - if (device.getKeepaliveTime() == null) { - device.setKeepaliveIntervalTime(60); - } else { - long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); - if (System.currentTimeMillis() / 1000 - lastTime > 10) { - device.setKeepaliveIntervalTime(Long.valueOf(System.currentTimeMillis() / 1000 - lastTime).intValue()); - } - } device.setKeepaliveTime(DateUtil.getNow()); @@ -138,7 +132,8 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp // 刷新过期任务 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); // 如果三次心跳失败,则设置设备离线 - dynamicTask.startDelay(registerExpireTaskKey, () -> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime() * 1000 * 3); + dynamicTask.startDelay(registerExpireTaskKey, () -> deviceService.offline(device.getDeviceId(), "三次心跳超时"), + device.getHeartBeatInterval() * 1000 * device.getHeartBeatCount()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java index ed2c8b0ab..d124f2369 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Platform; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; @@ -36,6 +37,9 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar @Autowired private DeferredResultHolder deferredResultHolder; + @Autowired + private IDeviceService deviceService; + @Override public void afterPropertiesSet() throws Exception { responseMessageHandler.addHandler(cmdType, this); @@ -45,7 +49,12 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar @Override public void handForDevice(RequestEvent evt, Device device, Element element) { String channelId = getText(element, "DeviceID"); - String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId; + String key; + if (device.getDeviceId().equals(channelId)) { + key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId(); + }else { + key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId; + } try { // 回复200 OK responseAck((SIPRequest) evt.getRequest(), Response.OK); @@ -58,6 +67,18 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar if (log.isDebugEnabled()) { log.debug(json.toJSONString()); } + JSONObject basicParam = json.getJSONObject("BasicParam"); + Integer heartBeatInterval = basicParam.getInteger("HeartBeatInterval"); + Integer heartBeatCount = basicParam.getInteger("HeartBeatCount"); + Integer positionCapability = basicParam.getInteger("PositionCapability"); + device.setHeartBeatInterval(heartBeatInterval); + device.setHeartBeatCount(heartBeatCount); + device.setPositionCapability(positionCapability); + + deviceService.updateDeviceHeartInfo(device); + + + RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setData(json); diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue index f538b1ea3..d3664d8b1 100755 --- a/web_src/src/components/DeviceList.vue +++ b/web_src/src/components/DeviceList.vue @@ -95,6 +95,8 @@ 布防 撤防 + + 基础配置同步 @@ -355,6 +357,8 @@ export default { this.resetGuard(itemData) }else if (command === "delete") { this.deleteDevice(itemData) + }else if (command === "syncBasicParam") { + this.syncBasicParam(itemData) } }, setGuard: function (itemData) { @@ -462,6 +466,33 @@ export default { message: error.message }) }); + }, + syncBasicParam: function (data) { + console.log(data) + this.$axios({ + method: 'get', + url: `/api/device/config/query/${data.deviceId}/BasicParam`, + params: { + // channelId: data.deviceId + } + }).then( (res)=> { + if (res.data.code === 0) { + this.$message.success({ + showClose: true, + message: `配置已同步,当前心跳间隔: ${res.data.data.BasicParam.HeartBeatInterval} 心跳间隔:${res.data.data.BasicParam.HeartBeatCount}` + }) + }else { + this.$message.error({ + showClose: true, + message: res.data.msg + }) + } + }).catch( (error)=> { + this.$message.error({ + showClose: true, + message: error.message + }) + }); }, diff --git a/数据库/2.7.3/初始化-mysql-2.7.3.sql b/数据库/2.7.3/初始化-mysql-2.7.3.sql index c147fd9c8..66b1006b6 100644 --- a/数据库/2.7.3/初始化-mysql-2.7.3.sql +++ b/数据库/2.7.3/初始化-mysql-2.7.3.sql @@ -31,7 +31,9 @@ create table wvp_device local_ip character varying(50), password character varying(255), as_message_channel bool default false, - keepalive_interval_time integer, + heart_beat_interval integer, + heart_beat_count integer, + position_capability integer, broadcast_push_after_ack bool default false, constraint uk_device_device unique (device_id) ); diff --git a/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql b/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql index 0695e4101..4bfcb4f0c 100644 --- a/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql +++ b/数据库/2.7.3/初始化-postgresql-kingbase-2.7.3.sql @@ -31,7 +31,9 @@ create table wvp_device local_ip character varying(50), password character varying(255), as_message_channel bool default false, - keepalive_interval_time integer, + heart_beat_interval integer, + heart_beat_count integer, + position_capability integer, broadcast_push_after_ack bool default false, constraint uk_device_device unique (device_id) ); diff --git a/数据库/2.7.3/更新-mysql-2.7.3.sql b/数据库/2.7.3/更新-mysql-2.7.3.sql index 01f42da41..47bf4ca3a 100644 --- a/数据库/2.7.3/更新-mysql-2.7.3.sql +++ b/数据库/2.7.3/更新-mysql-2.7.3.sql @@ -40,4 +40,11 @@ alter table wvp_stream_proxy add relates_media_server_id character varying(50); */ drop index uk_stream_push_app_stream_path on wvp_cloud_record; alter table wvp_cloud_record change folder folder varchar(500) null; -alter table wvp_cloud_record change file_path file_path varchar(500) null; \ No newline at end of file +alter table wvp_cloud_record change file_path file_path varchar(500) null; + +/* +* 20250211 +*/ +alter table wvp_device change keepalive_interval_time heart_beat_interval integer; +alter table wvp_device add heart_beat_count integer; +alter table wvp_device add position_capability integer; \ No newline at end of file diff --git a/数据库/2.7.3/更新-postgresql-kingbase-2.7.3.sql b/数据库/2.7.3/更新-postgresql-kingbase-2.7.3.sql index 8317bce72..0acf261b0 100644 --- a/数据库/2.7.3/更新-postgresql-kingbase-2.7.3.sql +++ b/数据库/2.7.3/更新-postgresql-kingbase-2.7.3.sql @@ -39,4 +39,11 @@ alter table wvp_stream_proxy add relates_media_server_id character varying(50); */ drop index uk_stream_push_app_stream_path on wvp_cloud_record; alter table wvp_cloud_record change folder folder varchar(500) null; -alter table wvp_cloud_record change file_path file_path varchar(500) null; \ No newline at end of file +alter table wvp_cloud_record change file_path file_path varchar(500) null; + +/* +* 20250211 +*/ +alter table wvp_device change keepalive_interval_time heart_beat_interval integer; +alter table wvp_device add heart_beat_count integer; +alter table wvp_device add position_capability integer; \ No newline at end of file