Commit 5b0b17d7410785aef2bedb22447bd458a3713300
1 parent
52656bb8
添加第三方服务参与的推流直接转发到国标功能
Showing
23 changed files
with
321 additions
and
74 deletions
sql/mysql.sql
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
| ... | ... | @@ -55,5 +55,8 @@ public class VideoManagerConstants { |
| 55 | 55 | public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; |
| 56 | 56 | |
| 57 | 57 | //************************** redis 消息********************************* |
| 58 | - public static final String WVP_MSG_STREAM_CHANGE__PREFIX = "WVP_MSG_STREAM_CHANGE_"; | |
| 58 | + public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_"; | |
| 59 | + | |
| 60 | + //************************** 第三方 **************************************** | |
| 61 | + public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; | |
| 59 | 62 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
| ... | ... | @@ -29,6 +29,8 @@ public class UserSetup { |
| 29 | 29 | |
| 30 | 30 | private String serverId = "000000"; |
| 31 | 31 | |
| 32 | + private String thirdPartyGBIdReg = "[\\s\\S]*"; | |
| 33 | + | |
| 32 | 34 | private List<String> interfaceAuthenticationExcludes = new ArrayList<>(); |
| 33 | 35 | |
| 34 | 36 | public Boolean getSavePositionHistory() { |
| ... | ... | @@ -114,4 +116,12 @@ public class UserSetup { |
| 114 | 116 | public void setServerId(String serverId) { |
| 115 | 117 | this.serverId = serverId; |
| 116 | 118 | } |
| 119 | + | |
| 120 | + public String getThirdPartyGBIdReg() { | |
| 121 | + return thirdPartyGBIdReg; | |
| 122 | + } | |
| 123 | + | |
| 124 | + public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) { | |
| 125 | + this.thirdPartyGBIdReg = thirdPartyGBIdReg; | |
| 126 | + } | |
| 117 | 127 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
| ... | ... | @@ -104,6 +104,11 @@ public class ParentPlatform { |
| 104 | 104 | */ |
| 105 | 105 | private int channelCount; |
| 106 | 106 | |
| 107 | + /** | |
| 108 | + * 共享所有的直播流 | |
| 109 | + */ | |
| 110 | + private boolean shareAllLiveStream; | |
| 111 | + | |
| 107 | 112 | public Integer getId() { |
| 108 | 113 | return id; |
| 109 | 114 | } |
| ... | ... | @@ -264,4 +269,12 @@ public class ParentPlatform { |
| 264 | 269 | this.channelCount = channelCount; |
| 265 | 270 | } |
| 266 | 271 | |
| 272 | + | |
| 273 | + public boolean isShareAllLiveStream() { | |
| 274 | + return shareAllLiveStream; | |
| 275 | + } | |
| 276 | + | |
| 277 | + public void setShareAllLiveStream(boolean shareAllLiveStream) { | |
| 278 | + this.shareAllLiveStream = shareAllLiveStream; | |
| 279 | + } | |
| 267 | 280 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -31,6 +31,7 @@ import javax.sip.header.FromHeader; |
| 31 | 31 | import javax.sip.message.Request; |
| 32 | 32 | import javax.sip.message.Response; |
| 33 | 33 | import java.text.ParseException; |
| 34 | +import java.util.List; | |
| 34 | 35 | import java.util.Vector; |
| 35 | 36 | |
| 36 | 37 | /** |
| ... | ... | @@ -105,7 +106,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 105 | 106 | if (platform != null) { |
| 106 | 107 | // 查询平台下是否有该通道 |
| 107 | 108 | DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); |
| 108 | - GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); | |
| 109 | + List<GbStream> gbStreams = storager.queryStreamInParentPlatform(requesterId, channelId); | |
| 110 | + GbStream gbStream = gbStreams.size() > 0? gbStreams.get(0):null; | |
| 109 | 111 | MediaServerItem mediaServerItem = null; |
| 110 | 112 | // 不是通道可能是直播流 |
| 111 | 113 | if (channel != null && gbStream == null ) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
| 1 | 1 | package com.genersoft.iot.vmp.media.zlm; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.conf.UserSetup; | |
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.GbStream; | |
| 4 | 6 | import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; |
| 5 | 7 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 6 | 8 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 7 | 9 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| 8 | 10 | import com.genersoft.iot.vmp.service.IStreamPushService; |
| 11 | +import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; | |
| 9 | 12 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 10 | 13 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 11 | 14 | import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; |
| 12 | 15 | import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; |
| 16 | +import com.genersoft.iot.vmp.storager.dao.StreamPushMapper; | |
| 13 | 17 | import org.slf4j.Logger; |
| 14 | 18 | import org.slf4j.LoggerFactory; |
| 15 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
| 16 | 20 | import org.springframework.stereotype.Component; |
| 21 | +import org.springframework.util.StringUtils; | |
| 17 | 22 | |
| 18 | 23 | import java.util.*; |
| 24 | +import java.util.regex.Matcher; | |
| 25 | +import java.util.regex.Pattern; | |
| 19 | 26 | |
| 20 | 27 | @Component |
| 21 | 28 | public class ZLMMediaListManager { |
| ... | ... | @@ -41,8 +48,14 @@ public class ZLMMediaListManager { |
| 41 | 48 | private IStreamPushService streamPushService; |
| 42 | 49 | |
| 43 | 50 | @Autowired |
| 51 | + private StreamPushMapper streamPushMapper; | |
| 52 | + | |
| 53 | + @Autowired | |
| 44 | 54 | private ZLMHttpHookSubscribe subscribe; |
| 45 | 55 | |
| 56 | + @Autowired | |
| 57 | + private UserSetup userSetup; | |
| 58 | + | |
| 46 | 59 | |
| 47 | 60 | public void updateMediaList(MediaServerItem mediaServerItem) { |
| 48 | 61 | storager.clearMediaList(); |
| ... | ... | @@ -89,7 +102,43 @@ public class ZLMMediaListManager { |
| 89 | 102 | } |
| 90 | 103 | |
| 91 | 104 | public void addMedia(MediaItem mediaItem) { |
| 92 | - storager.updateMedia(streamPushService.transform(mediaItem)); | |
| 105 | + // 查找此直播流是否存在redis预设gbId | |
| 106 | + StreamPushItem transform = streamPushService.transform(mediaItem); | |
| 107 | + // 从streamId取出查询关键值 | |
| 108 | + Pattern pattern = Pattern.compile(userSetup.getThirdPartyGBIdReg()); | |
| 109 | + Matcher matcher = pattern.matcher(mediaItem.getStream());// 指定要匹配的字符串 | |
| 110 | + String queryKey = null; | |
| 111 | + if (matcher.find()) { //此处find()每次被调用后,会偏移到下一个匹配 | |
| 112 | + queryKey = matcher.group(); | |
| 113 | + } | |
| 114 | + if (queryKey != null) { | |
| 115 | + ThirdPartyGB thirdPartyGB = redisCatchStorage.queryMemberNoGBId(queryKey); | |
| 116 | + if (thirdPartyGB != null && !StringUtils.isEmpty(thirdPartyGB.getNationalStandardNo())) { | |
| 117 | + transform.setGbId(thirdPartyGB.getNationalStandardNo()); | |
| 118 | + transform.setName(thirdPartyGB.getName()); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + storager.updateMedia(transform); | |
| 122 | + if (!StringUtils.isEmpty(transform.getGbId())) { | |
| 123 | + // 如果这个国标ID已经给了其他推流且流已离线,则移除其他推流 | |
| 124 | + List<GbStream> gbStreams = gbStreamMapper.selectByGBId(transform.getGbId()); | |
| 125 | + if (gbStreams.size() > 0) { | |
| 126 | + for (GbStream gbStream : gbStreams) { | |
| 127 | + // 出现使用相同国标Id的视频流时,使用新流替换旧流, | |
| 128 | + gbStreamMapper.del(gbStream.getApp(), gbStream.getStream()); | |
| 129 | + platformGbStreamMapper.delByAppAndStream(gbStream.getApp(), gbStream.getStream()); | |
| 130 | + if (!gbStream.isStatus()) { | |
| 131 | + streamPushMapper.del(gbStream.getApp(), gbStream.getStream()); | |
| 132 | + } | |
| 133 | + } | |
| 134 | + } | |
| 135 | + if (gbStreamMapper.selectOne(transform.getApp(), transform.getStream()) != null) { | |
| 136 | + gbStreamMapper.update(transform); | |
| 137 | + }else { | |
| 138 | + gbStreamMapper.add(transform); | |
| 139 | + } | |
| 140 | + | |
| 141 | + } | |
| 93 | 142 | } |
| 94 | 143 | |
| 95 | 144 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
| ... | ... | @@ -37,4 +37,13 @@ public interface IStreamPushService { |
| 37 | 37 | StreamPushItem transform(MediaItem item); |
| 38 | 38 | |
| 39 | 39 | StreamPushItem getPush(String app, String streamId); |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 停止一路推流 | |
| 43 | + * @param app 应用名 | |
| 44 | + * @param streamId 流ID | |
| 45 | + * @return | |
| 46 | + */ | |
| 47 | + boolean stop(String app, String streamId); | |
| 48 | + | |
| 40 | 49 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/bean/ThirdPartyGB.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.service.bean; | |
| 2 | + | |
| 3 | +public class ThirdPartyGB { | |
| 4 | + | |
| 5 | + private String name; | |
| 6 | + private String nationalStandardNo; | |
| 7 | + | |
| 8 | + public String getName() { | |
| 9 | + return name; | |
| 10 | + } | |
| 11 | + | |
| 12 | + public void setName(String name) { | |
| 13 | + this.name = name; | |
| 14 | + } | |
| 15 | + | |
| 16 | + public String getNationalStandardNo() { | |
| 17 | + return nationalStandardNo; | |
| 18 | + } | |
| 19 | + | |
| 20 | + public void setNationalStandardNo(String nationalStandardNo) { | |
| 21 | + this.nationalStandardNo = nationalStandardNo; | |
| 22 | + } | |
| 23 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
| ... | ... | @@ -79,44 +79,38 @@ public class StreamProxyServiceImpl implements IStreamProxyService { |
| 79 | 79 | StringBuffer result = new StringBuffer(); |
| 80 | 80 | boolean streamLive = false; |
| 81 | 81 | param.setMediaServerId(mediaInfo.getId()); |
| 82 | + boolean saveResult; | |
| 82 | 83 | // 更新 |
| 83 | 84 | if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { |
| 84 | - if (videoManagerStorager.updateStreamProxy(param)) { | |
| 85 | - result.append("保存成功"); | |
| 86 | - if (param.isEnable()){ | |
| 87 | - JSONObject jsonObject = addStreamProxyToZlm(param); | |
| 88 | - if (jsonObject == null) { | |
| 89 | - result.append(", 但是启用失败,请检查流地址是否可用"); | |
| 90 | - param.setEnable(false); | |
| 91 | - videoManagerStorager.updateStreamProxy(param); | |
| 92 | - }else { | |
| 85 | + saveResult = videoManagerStorager.updateStreamProxy(param); | |
| 86 | + }else { // 新增 | |
| 87 | + saveResult = videoManagerStorager.addStreamProxy(param); | |
| 88 | + } | |
| 89 | + if (saveResult) { | |
| 90 | + result.append("保存成功"); | |
| 91 | + if (param.isEnable()) { | |
| 92 | + JSONObject jsonObject = addStreamProxyToZlm(param); | |
| 93 | + if (jsonObject == null) { | |
| 94 | + streamLive = false; | |
| 95 | + result.append(", 但是启用失败,请检查流地址是否可用"); | |
| 96 | + param.setEnable(false); | |
| 97 | + videoManagerStorager.updateStreamProxy(param); | |
| 98 | + }else { | |
| 99 | + Integer code = jsonObject.getInteger("code"); | |
| 100 | + if (code == 0) { | |
| 93 | 101 | StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( |
| 94 | 102 | mediaInfo, param.getApp(), param.getStream(), null); |
| 95 | 103 | wvpResult.setData(streamInfo); |
| 96 | - } | |
| 97 | - } | |
| 98 | - } | |
| 99 | - }else { // 新增 | |
| 100 | - if (videoManagerStorager.addStreamProxy(param)){ | |
| 101 | - result.append("保存成功"); | |
| 102 | - streamLive = true; | |
| 103 | - if (param.isEnable()) { | |
| 104 | - JSONObject jsonObject = addStreamProxyToZlm(param); | |
| 105 | - if (jsonObject == null) { | |
| 106 | - streamLive = false; | |
| 104 | + }else { | |
| 107 | 105 | result.append(", 但是启用失败,请检查流地址是否可用"); |
| 108 | 106 | param.setEnable(false); |
| 109 | 107 | videoManagerStorager.updateStreamProxy(param); |
| 110 | - }else { | |
| 111 | - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( | |
| 112 | - mediaInfo, param.getApp(), param.getStream(), null); | |
| 113 | - wvpResult.setData(streamInfo); | |
| 114 | 108 | } |
| 109 | + | |
| 115 | 110 | } |
| 116 | - }else { | |
| 117 | - result.append("保存失败"); | |
| 118 | 111 | } |
| 119 | - | |
| 112 | + }else { | |
| 113 | + result.append("保存失败"); | |
| 120 | 114 | } |
| 121 | 115 | if (param.getPlatformGbId() != null && streamLive) { |
| 122 | 116 | List<GbStream> gbStreams = new ArrayList<>(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
| ... | ... | @@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService; |
| 12 | 12 | import com.genersoft.iot.vmp.service.IStreamPushService; |
| 13 | 13 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 14 | 14 | import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; |
| 15 | +import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; | |
| 15 | 16 | import com.genersoft.iot.vmp.storager.dao.StreamPushMapper; |
| 16 | 17 | import com.github.pagehelper.PageHelper; |
| 17 | 18 | import com.github.pagehelper.PageInfo; |
| ... | ... | @@ -33,6 +34,9 @@ public class StreamPushServiceImpl implements IStreamPushService { |
| 33 | 34 | private StreamPushMapper streamPushMapper; |
| 34 | 35 | |
| 35 | 36 | @Autowired |
| 37 | + private PlatformGbStreamMapper platformGbStreamMapper; | |
| 38 | + | |
| 39 | + @Autowired | |
| 36 | 40 | private ZLMRESTfulUtils zlmresTfulUtils; |
| 37 | 41 | |
| 38 | 42 | @Autowired |
| ... | ... | @@ -116,4 +120,18 @@ public class StreamPushServiceImpl implements IStreamPushService { |
| 116 | 120 | |
| 117 | 121 | return streamPushMapper.selectOne(app, streamId); |
| 118 | 122 | } |
| 123 | + | |
| 124 | + @Override | |
| 125 | + public boolean stop(String app, String streamId) { | |
| 126 | + StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId); | |
| 127 | + int delStream = streamPushMapper.del(app, streamId); | |
| 128 | + gbStreamMapper.del(app, streamId); | |
| 129 | + platformGbStreamMapper.delByAppAndStream(app, streamId); | |
| 130 | + if (delStream > 0) { | |
| 131 | + MediaServerItem mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); | |
| 132 | + zlmresTfulUtils.closeStreams(mediaServerItem,app, streamId); | |
| 133 | + } | |
| 134 | + return true; | |
| 135 | + } | |
| 136 | + | |
| 119 | 137 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
| ... | ... | @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 6 | 6 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; |
| 7 | 7 | import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; |
| 8 | 8 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 9 | +import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; | |
| 9 | 10 | |
| 10 | 11 | import java.util.List; |
| 11 | 12 | import java.util.Map; |
| ... | ... | @@ -152,4 +153,11 @@ public interface IRedisCatchStorage { |
| 152 | 153 | boolean startDownload(StreamInfo streamInfo); |
| 153 | 154 | |
| 154 | 155 | StreamInfo queryDownloadByStreamId(String streamId); |
| 156 | + | |
| 157 | + /** | |
| 158 | + * 查找第三方系统留下的国标预设值 | |
| 159 | + * @param queryKey | |
| 160 | + * @return | |
| 161 | + */ | |
| 162 | + ThirdPartyGB queryMemberNoGBId(String queryKey); | |
| 155 | 163 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
| ... | ... | @@ -327,7 +327,7 @@ public interface IVideoManagerStorager { |
| 327 | 327 | * @param channelId |
| 328 | 328 | * @return |
| 329 | 329 | */ |
| 330 | - GbStream queryStreamInParentPlatform(String platformId, String channelId); | |
| 330 | + List<GbStream> queryStreamInParentPlatform(String platformId, String channelId); | |
| 331 | 331 | |
| 332 | 332 | /** |
| 333 | 333 | * 获取平台关联的直播流 | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java
| ... | ... | @@ -40,10 +40,13 @@ public interface GbStreamMapper { |
| 40 | 40 | @Select("SELECT * FROM gb_stream WHERE app=#{app} AND stream=#{stream}") |
| 41 | 41 | StreamProxyItem selectOne(String app, String stream); |
| 42 | 42 | |
| 43 | + @Select("SELECT * FROM gb_stream WHERE gbId=#{gbId}") | |
| 44 | + List<GbStream> selectByGBId(String gbId); | |
| 45 | + | |
| 43 | 46 | @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs " + |
| 44 | 47 | "LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream " + |
| 45 | 48 | "WHERE gs.gbId = '${gbId}' AND pgs.platformId = '${platformId}'") |
| 46 | - GbStream queryStreamInPlatform(String platformId, String gbId); | |
| 49 | + List<GbStream> queryStreamInPlatform(String platformId, String gbId); | |
| 47 | 50 | |
| 48 | 51 | @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs " + |
| 49 | 52 | "LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream " + | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
| ... | ... | @@ -15,10 +15,10 @@ public interface ParentPlatformMapper { |
| 15 | 15 | |
| 16 | 16 | @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " + |
| 17 | 17 | " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " + |
| 18 | - " status) " + | |
| 18 | + " status, shareAllLiveStream) " + | |
| 19 | 19 | " VALUES (${enable}, '${name}', '${serverGBId}', '${serverGBDomain}', '${serverIP}', ${serverPort}, '${deviceGBId}', '${deviceIp}', " + |
| 20 | 20 | " '${devicePort}', '${username}', '${password}', '${expires}', '${keepTimeout}', '${transport}', '${characterSet}', ${ptz}, ${rtcp}, " + |
| 21 | - " ${status})") | |
| 21 | + " ${status}, ${shareAllLiveStream})") | |
| 22 | 22 | int addParentPlatform(ParentPlatform parentPlatform); |
| 23 | 23 | |
| 24 | 24 | @Update("UPDATE parent_platform " + |
| ... | ... | @@ -39,7 +39,8 @@ public interface ParentPlatformMapper { |
| 39 | 39 | "characterSet=#{characterSet}, " + |
| 40 | 40 | "ptz=#{ptz}, " + |
| 41 | 41 | "rtcp=#{rtcp}, " + |
| 42 | - "status=#{status} " + | |
| 42 | + "status=#{status}, " + | |
| 43 | + "shareAllLiveStream=#{shareAllLiveStream} " + | |
| 43 | 44 | "WHERE id=#{id}") |
| 44 | 45 | int updateParentPlatform(ParentPlatform parentPlatform); |
| 45 | 46 | |
| ... | ... | @@ -70,4 +71,7 @@ public interface ParentPlatformMapper { |
| 70 | 71 | |
| 71 | 72 | @Update("UPDATE parent_platform SET status=#{online} WHERE serverGBId=#{platformGbID}" ) |
| 72 | 73 | int updateParentPlatformStatus(String platformGbID, boolean online); |
| 74 | + | |
| 75 | + @Select("SELECT * FROM parent_platform WHERE shareAllLiveStream=true") | |
| 76 | + List<ParentPlatform> selectAllAhareAllLiveStream(); | |
| 73 | 77 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java
| ... | ... | @@ -24,4 +24,7 @@ public interface PlatformGbStreamMapper { |
| 24 | 24 | |
| 25 | 25 | @Select("SELECT * FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream}") |
| 26 | 26 | List<StreamProxyItem> selectByAppAndStream(String app, String stream); |
| 27 | + | |
| 28 | + @Select("SELECT * FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream} AND platformId=#{serverGBId}") | |
| 29 | + StreamProxyItem selectOne(String app, String stream, String serverGBId); | |
| 27 | 30 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
| ... | ... | @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; |
| 6 | 6 | import com.genersoft.iot.vmp.conf.UserSetup; |
| 7 | 7 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 8 | 8 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 9 | +import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; | |
| 9 | 10 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 10 | 11 | import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; |
| 11 | 12 | import com.genersoft.iot.vmp.utils.redis.RedisUtil; |
| ... | ... | @@ -13,6 +14,7 @@ import org.slf4j.Logger; |
| 13 | 14 | import org.slf4j.LoggerFactory; |
| 14 | 15 | import org.springframework.beans.factory.annotation.Autowired; |
| 15 | 16 | import org.springframework.stereotype.Component; |
| 17 | +import org.springframework.util.StringUtils; | |
| 16 | 18 | |
| 17 | 19 | import java.text.SimpleDateFormat; |
| 18 | 20 | import java.util.*; |
| ... | ... | @@ -324,7 +326,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { |
| 324 | 326 | |
| 325 | 327 | @Override |
| 326 | 328 | public void sendStreamChangeMsg(String type, JSONObject jsonObject) { |
| 327 | - String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE__PREFIX + type; | |
| 329 | + String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; | |
| 328 | 330 | logger.debug("[redis 流变化事件] {}: {}", key, jsonObject.toString()); |
| 329 | 331 | redis.convertAndSend(key, jsonObject); |
| 330 | 332 | } |
| ... | ... | @@ -350,4 +352,11 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { |
| 350 | 352 | if (playLeys == null || playLeys.size() == 0) return null; |
| 351 | 353 | return (StreamInfo)redis.get(playLeys.get(0).toString()); |
| 352 | 354 | } |
| 355 | + | |
| 356 | + @Override | |
| 357 | + public ThirdPartyGB queryMemberNoGBId(String queryKey) { | |
| 358 | + String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey; | |
| 359 | + JSONObject jsonObject = (JSONObject)redis.get(key); | |
| 360 | + return JSONObject.toJavaObject(jsonObject, ThirdPartyGB.class); | |
| 361 | + } | |
| 353 | 362 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
| ... | ... | @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 5 | 5 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 6 | 6 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 7 | 7 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| 8 | +import com.genersoft.iot.vmp.service.IGbStreamService; | |
| 8 | 9 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 9 | 10 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 10 | 11 | import com.genersoft.iot.vmp.storager.dao.*; |
| ... | ... | @@ -19,6 +20,7 @@ import org.springframework.stereotype.Component; |
| 19 | 20 | import org.springframework.transaction.TransactionDefinition; |
| 20 | 21 | import org.springframework.transaction.TransactionStatus; |
| 21 | 22 | import org.springframework.transaction.annotation.Transactional; |
| 23 | +import org.springframework.util.StringUtils; | |
| 22 | 24 | |
| 23 | 25 | import java.text.SimpleDateFormat; |
| 24 | 26 | import java.util.ArrayList; |
| ... | ... | @@ -69,6 +71,16 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { |
| 69 | 71 | |
| 70 | 72 | @Autowired |
| 71 | 73 | private GbStreamMapper gbStreamMapper; |
| 74 | +; | |
| 75 | + | |
| 76 | + @Autowired | |
| 77 | + private PlatformGbStreamMapper platformGbStreamMapper; | |
| 78 | + | |
| 79 | + @Autowired | |
| 80 | + private IGbStreamService gbStreamService; | |
| 81 | + | |
| 82 | + @Autowired | |
| 83 | + private ParentPlatformMapper parentPlatformMapper; | |
| 72 | 84 | |
| 73 | 85 | @Autowired |
| 74 | 86 | private VideoStreamSessionManager streamSession; |
| ... | ... | @@ -356,6 +368,15 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { |
| 356 | 368 | // 更新缓存 |
| 357 | 369 | parentPlatformCatch.setParentPlatform(parentPlatform); |
| 358 | 370 | redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); |
| 371 | + // 共享所有视频流,需要将现有视频流添加到此平台 | |
| 372 | + List<GbStream> gbStreams = gbStreamMapper.selectAll(); | |
| 373 | + if (gbStreams.size() > 0) { | |
| 374 | + if (parentPlatform.isShareAllLiveStream()) { | |
| 375 | + gbStreamService.addPlatformInfo(gbStreams, parentPlatform.getServerGBId()); | |
| 376 | + }else { | |
| 377 | + gbStreamService.delPlatformInfo(gbStreams); | |
| 378 | + } | |
| 379 | + } | |
| 359 | 380 | return result > 0; |
| 360 | 381 | } |
| 361 | 382 | |
| ... | ... | @@ -561,7 +582,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { |
| 561 | 582 | * @return |
| 562 | 583 | */ |
| 563 | 584 | @Override |
| 564 | - public GbStream queryStreamInParentPlatform(String platformId, String gbId) { | |
| 585 | + public List<GbStream> queryStreamInParentPlatform(String platformId, String gbId) { | |
| 565 | 586 | return gbStreamMapper.queryStreamInPlatform(platformId, gbId); |
| 566 | 587 | } |
| 567 | 588 | |
| ... | ... | @@ -602,6 +623,22 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { |
| 602 | 623 | streamPushMapper.del(streamPushItem.getApp(), streamPushItem.getStream()); |
| 603 | 624 | streamPushMapper.add(streamPushItem); |
| 604 | 625 | gbStreamMapper.setStatus(streamPushItem.getApp(), streamPushItem.getStream(), true); |
| 626 | + if(!StringUtils.isEmpty(streamPushItem.getGbId() )){ | |
| 627 | + // 查找开启了全部直播流共享的上级平台 | |
| 628 | + List<ParentPlatform> parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); | |
| 629 | + if (parentPlatforms.size() > 0) { | |
| 630 | + for (ParentPlatform parentPlatform : parentPlatforms) { | |
| 631 | + streamPushItem.setPlatformId(parentPlatform.getServerGBId()); | |
| 632 | + String stream = streamPushItem.getStream(); | |
| 633 | + StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(streamPushItem.getApp(), stream, parentPlatform.getServerGBId()); | |
| 634 | + if (streamProxyItems == null) { | |
| 635 | + platformGbStreamMapper.add(streamPushItem); | |
| 636 | + } | |
| 637 | + | |
| 638 | + } | |
| 639 | + } | |
| 640 | + } | |
| 641 | + | |
| 605 | 642 | } |
| 606 | 643 | |
| 607 | 644 | @Override | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
| ... | ... | @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.vmanager.streamPush; |
| 3 | 3 | import com.genersoft.iot.vmp.gb28181.bean.GbStream; |
| 4 | 4 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| 5 | 5 | import com.genersoft.iot.vmp.service.IStreamPushService; |
| 6 | +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | |
| 6 | 7 | import com.github.pagehelper.PageInfo; |
| 7 | 8 | import io.swagger.annotations.Api; |
| 8 | 9 | import io.swagger.annotations.ApiImplicitParam; |
| ... | ... | @@ -71,4 +72,21 @@ public class StreamPushController { |
| 71 | 72 | return "fail"; |
| 72 | 73 | } |
| 73 | 74 | } |
| 75 | + | |
| 76 | + | |
| 77 | + @ApiOperation("中止一个推流") | |
| 78 | + @ApiImplicitParams({ | |
| 79 | + @ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class), | |
| 80 | + @ApiImplicitParam(name = "streamId", value = "流ID", required = true, dataTypeClass = String.class), | |
| 81 | + }) | |
| 82 | + @PostMapping(value = "/stop") | |
| 83 | + @ResponseBody | |
| 84 | + public Object removeFormGB(@RequestParam(required = true)String app, @RequestParam(required = true)String streamId){ | |
| 85 | + if (streamPushService.stop(app, streamId)){ | |
| 86 | + return "success"; | |
| 87 | + }else { | |
| 88 | + return "fail"; | |
| 89 | + } | |
| 90 | + } | |
| 91 | + | |
| 74 | 92 | } | ... | ... |
src/main/resources/all-application.yml
src/main/resources/wvp.sqlite
No preview for this file type
web_src/src/components/PushVideoList.vue
| ... | ... | @@ -36,7 +36,7 @@ |
| 36 | 36 | <template slot-scope="scope"> |
| 37 | 37 | <el-button-group> |
| 38 | 38 | <el-button size="mini" icon="el-icon-video-play" @click="playPuhsh(scope.row)">播放</el-button> |
| 39 | - <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopPuhsh(scope.row)">停止</el-button> | |
| 39 | + <el-button size="mini" icon="el-icon-switch-button" type="danger" @click="stopPuhsh(scope.row)">移除</el-button> | |
| 40 | 40 | <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!!scope.row.gbId" @click="addToGB(scope.row)">加入国标</el-button> |
| 41 | 41 | <el-button size="mini" icon="el-icon-position" type="primary" v-if="!!scope.row.gbId" @click="removeFromGB(scope.row)">移出国标</el-button> |
| 42 | 42 | </el-button-group> |
| ... | ... | @@ -151,7 +151,21 @@ |
| 151 | 151 | }); |
| 152 | 152 | }, |
| 153 | 153 | stopPuhsh: function(row){ |
| 154 | - console.log(row) | |
| 154 | + var that = this; | |
| 155 | + that.$axios({ | |
| 156 | + method:"post", | |
| 157 | + url:"/api/push/stop", | |
| 158 | + params: { | |
| 159 | + app: row.app, | |
| 160 | + streamId: row.stream | |
| 161 | + } | |
| 162 | + }).then((res)=>{ | |
| 163 | + if (res.data == "success") { | |
| 164 | + that.initData() | |
| 165 | + } | |
| 166 | + }).catch(function (error) { | |
| 167 | + console.log(error); | |
| 168 | + }); | |
| 155 | 169 | }, |
| 156 | 170 | addToGB: function(row){ |
| 157 | 171 | this.$refs.addStreamTOGB.openDialog({app: row.app, stream: row.stream, mediaServerId: row.mediaServerId}, this.initData); |
| ... | ... | @@ -159,16 +173,16 @@ |
| 159 | 173 | removeFromGB: function(row){ |
| 160 | 174 | var that = this; |
| 161 | 175 | that.$axios({ |
| 162 | - method:"delete", | |
| 163 | - url:"/api/push/remove_form_gb", | |
| 164 | - data:row | |
| 165 | - }).then((res)=>{ | |
| 166 | - if (res.data == "success") { | |
| 176 | + method:"delete", | |
| 177 | + url:"/api/push/remove_form_gb", | |
| 178 | + data:row | |
| 179 | + }).then((res)=>{ | |
| 180 | + if (res.data == "success") { | |
| 167 | 181 | that.initData() |
| 168 | 182 | } |
| 169 | - }).catch(function (error) { | |
| 170 | - console.log(error); | |
| 171 | - }); | |
| 183 | + }).catch(function (error) { | |
| 184 | + console.log(error); | |
| 185 | + }); | |
| 172 | 186 | }, |
| 173 | 187 | dateFormat: function(/** timestamp=0 **/) { |
| 174 | 188 | var ts = arguments[0] || 0; | ... | ... |
web_src/src/components/StreamProxyList.vue
| ... | ... | @@ -50,6 +50,7 @@ |
| 50 | 50 | </div> |
| 51 | 51 | </template> |
| 52 | 52 | </el-table-column> |
| 53 | + <el-table-column prop="createTime" label="创建时间" align="center" width="150" show-overflow-tooltip/> | |
| 53 | 54 | <el-table-column label="转HLS" width="120" align="center"> |
| 54 | 55 | <template slot-scope="scope"> |
| 55 | 56 | <div slot="reference" class="name-wrapper"> | ... | ... |
web_src/src/components/dialog/platformEdit.vue
| ... | ... | @@ -17,7 +17,7 @@ |
| 17 | 17 | <el-input v-model="platform.name"></el-input> |
| 18 | 18 | </el-form-item> |
| 19 | 19 | <el-form-item label="SIP服务国标编码" prop="serverGBId"> |
| 20 | - <el-input v-model="platform.serverGBId" clearable></el-input> | |
| 20 | + <el-input v-model="platform.serverGBId" clearable @input="serverGBIdChange"></el-input> | |
| 21 | 21 | </el-form-item> |
| 22 | 22 | <el-form-item label="SIP服务国标域" prop="serverGBDomain"> |
| 23 | 23 | <el-input v-model="platform.serverGBDomain" clearable></el-input> |
| ... | ... | @@ -29,7 +29,7 @@ |
| 29 | 29 | <el-input v-model="platform.serverPort" clearable type="number"></el-input> |
| 30 | 30 | </el-form-item> |
| 31 | 31 | <el-form-item label="设备国标编号" prop="deviceGBId"> |
| 32 | - <el-input v-model="platform.deviceGBId" clearable></el-input> | |
| 32 | + <el-input v-model="platform.deviceGBId" clearable @input="deviceGBIdChange"></el-input> | |
| 33 | 33 | </el-form-item> |
| 34 | 34 | <el-form-item label="本地IP" prop="deviceIp"> |
| 35 | 35 | <el-input v-model="platform.deviceIp" :disabled="true"></el-input> |
| ... | ... | @@ -76,7 +76,7 @@ |
| 76 | 76 | <el-form-item label="其他选项"> |
| 77 | 77 | <el-checkbox label="启用" v-model="platform.enable" @change="checkExpires"></el-checkbox> |
| 78 | 78 | <el-checkbox label="云台控制" v-model="platform.ptz"></el-checkbox> |
| 79 | - <el-checkbox label="RTCP保活" v-model="platform.rtcp"></el-checkbox> | |
| 79 | + <el-checkbox label="共享所有直播流" v-model="platform.shareAllLiveStream"></el-checkbox> | |
| 80 | 80 | </el-form-item> |
| 81 | 81 | <el-form-item> |
| 82 | 82 | <el-button type="primary" @click="onSubmit">{{ |
| ... | ... | @@ -97,28 +97,6 @@ export default { |
| 97 | 97 | name: "platformEdit", |
| 98 | 98 | props: {}, |
| 99 | 99 | computed: {}, |
| 100 | - created() { | |
| 101 | - this.platform = { | |
| 102 | - id: null, | |
| 103 | - enable: true, | |
| 104 | - ptz: true, | |
| 105 | - rtcp: false, | |
| 106 | - name: null, | |
| 107 | - serverGBId: null, | |
| 108 | - serverGBDomain: null, | |
| 109 | - serverIP: null, | |
| 110 | - serverPort: null, | |
| 111 | - deviceGBId: null, | |
| 112 | - deviceIp: null, | |
| 113 | - devicePort: null, | |
| 114 | - username: null, | |
| 115 | - password: null, | |
| 116 | - expires: 300, | |
| 117 | - keepTimeout: 60, | |
| 118 | - transport: "UDP", | |
| 119 | - characterSet: "GB2312", | |
| 120 | - } | |
| 121 | - }, | |
| 122 | 100 | data() { |
| 123 | 101 | var deviceGBIdRules = async (rule, value, callback) => { |
| 124 | 102 | console.log(value); |
| ... | ... | @@ -158,6 +136,7 @@ export default { |
| 158 | 136 | keepTimeout: 60, |
| 159 | 137 | transport: "UDP", |
| 160 | 138 | characterSet: "GB2312", |
| 139 | + shareAllLiveStream: false, | |
| 161 | 140 | }, |
| 162 | 141 | rules: { |
| 163 | 142 | name: [{ required: true, message: "请输入平台名称", trigger: "blur" }], |
| ... | ... | @@ -198,12 +177,39 @@ export default { |
| 198 | 177 | console.log(error); |
| 199 | 178 | }); |
| 200 | 179 | }else { |
| 201 | - this.platform = platform; | |
| 180 | + this.platform.id = platform.id; | |
| 181 | + this.platform.enable = platform.enable; | |
| 182 | + this.platform.ptz = platform.ptz; | |
| 183 | + this.platform.rtcp = platform.rtcp; | |
| 184 | + this.platform.name = platform.name; | |
| 185 | + this.platform.serverGBId = platform.serverGBId; | |
| 186 | + this.platform.serverGBDomain = platform.serverGBDomain; | |
| 187 | + this.platform.serverIP = platform.serverIP; | |
| 188 | + this.platform.serverPort = platform.serverPort; | |
| 189 | + this.platform.deviceGBId = platform.deviceGBId; | |
| 190 | + this.platform.deviceIp = platform.deviceIp; | |
| 191 | + this.platform.devicePort = platform.devicePort; | |
| 192 | + this.platform.username = platform.username; | |
| 193 | + this.platform.password = platform.password; | |
| 194 | + this.platform.expires = platform.expires; | |
| 195 | + this.platform.keepTimeout = platform.keepTimeout; | |
| 196 | + this.platform.transport = platform.transport; | |
| 197 | + this.platform.characterSet = platform.characterSet; | |
| 198 | + this.platform.shareAllLiveStream = platform.shareAllLiveStream; | |
| 202 | 199 | this.onSubmit_text = "保存"; |
| 203 | 200 | } |
| 204 | 201 | this.showDialog = true; |
| 205 | 202 | this.listChangeCallback = callback; |
| 206 | 203 | }, |
| 204 | + serverGBIdChange: function () { | |
| 205 | + if (this.platform.serverGBId.length > 10) { | |
| 206 | + this.platform.serverGBDomain = this.platform.serverGBId.substr(0, 10); | |
| 207 | + } | |
| 208 | + }, | |
| 209 | + deviceGBIdChange: function () { | |
| 210 | + | |
| 211 | + this.platform.username = this.platform.deviceGBId ; | |
| 212 | + }, | |
| 207 | 213 | onSubmit: function () { |
| 208 | 214 | console.log("onSubmit"); |
| 209 | 215 | var that = this; |
| ... | ... | @@ -228,10 +234,30 @@ export default { |
| 228 | 234 | }); |
| 229 | 235 | }, |
| 230 | 236 | close: function () { |
| 231 | - console.log("关闭添加视频平台"); | |
| 232 | 237 | this.showDialog = false; |
| 233 | 238 | this.$refs.platform1.resetFields(); |
| 234 | 239 | this.$refs.platform2.resetFields(); |
| 240 | + this.platform = { | |
| 241 | + id: null, | |
| 242 | + enable: true, | |
| 243 | + ptz: true, | |
| 244 | + rtcp: false, | |
| 245 | + name: null, | |
| 246 | + serverGBId: null, | |
| 247 | + serverGBDomain: null, | |
| 248 | + serverIP: null, | |
| 249 | + serverPort: null, | |
| 250 | + deviceGBId: null, | |
| 251 | + deviceIp: null, | |
| 252 | + devicePort: null, | |
| 253 | + username: null, | |
| 254 | + password: null, | |
| 255 | + expires: 300, | |
| 256 | + keepTimeout: 60, | |
| 257 | + transport: "UDP", | |
| 258 | + characterSet: "GB2312", | |
| 259 | + shareAllLiveStream: false, | |
| 260 | + } | |
| 235 | 261 | }, |
| 236 | 262 | deviceGBIdExit: async function (deviceGbId) { |
| 237 | 263 | var result = false; | ... | ... |