Commit d679a9fcf8355e56f7be79a535e81f8300c70cb5
1 parent
f6fa1eed
添加对点播时设备自定义ssrc的支持
Showing
8 changed files
with
66 additions
and
11 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| ... | ... | @@ -93,8 +93,8 @@ public interface ISIPCommander { |
| 93 | 93 | * @param device 视频设备 |
| 94 | 94 | * @param channelId 预览通道 |
| 95 | 95 | */ |
| 96 | - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); | |
| 97 | - | |
| 96 | + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent); | |
| 97 | + | |
| 98 | 98 | /** |
| 99 | 99 | * 请求回放视频流 |
| 100 | 100 | * | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -343,7 +343,7 @@ public class SIPCommander implements ISIPCommander { |
| 343 | 343 | */ |
| 344 | 344 | @Override |
| 345 | 345 | public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| 346 | - ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { | |
| 346 | + ZLMHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { | |
| 347 | 347 | String streamId = ssrcInfo.getStream(); |
| 348 | 348 | try { |
| 349 | 349 | if (device == null) return; |
| ... | ... | @@ -436,6 +436,7 @@ public class SIPCommander implements ISIPCommander { |
| 436 | 436 | // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 |
| 437 | 437 | streamSession.put(device.getDeviceId(), channelId ,"play", streamId, ssrcInfo.getSsrc(), mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction(), VideoStreamSessionManager.SessionType.play); |
| 438 | 438 | streamSession.put(device.getDeviceId(), channelId ,"play", e.dialog); |
| 439 | + okEvent.response(e); | |
| 439 | 440 | }); |
| 440 | 441 | |
| 441 | 442 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
| ... | ... | @@ -202,6 +202,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme |
| 202 | 202 | String platformId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); |
| 203 | 203 | String deviceID = XmlUtil.getText(rootElement, "DeviceID"); |
| 204 | 204 | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); |
| 205 | + if (platform == null)return; | |
| 205 | 206 | SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); |
| 206 | 207 | if (evt.getServerTransaction() == null) { |
| 207 | 208 | ServerTransaction serverTransaction = platform.getTransport().equals("TCP") ? tcpSipProvider.getNewServerTransaction(evt.getRequest()) | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
| ... | ... | @@ -222,7 +222,24 @@ public class XmlUtil { |
| 222 | 222 | // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 |
| 223 | 223 | deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")) == 1?1:0); |
| 224 | 224 | } |
| 225 | - deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID")); | |
| 225 | + /** | |
| 226 | + * 行政区划展示设备树与业务分组展示设备树是两种不同的模式 | |
| 227 | + * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下: | |
| 228 | + * 河北省 | |
| 229 | + * --> 石家庄市 | |
| 230 | + * --> 摄像头 | |
| 231 | + * --> 正定县 | |
| 232 | + * --> 摄像头 | |
| 233 | + * --> 摄像头 | |
| 234 | + * | |
| 235 | + * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织: | |
| 236 | + * 业务分组 | |
| 237 | + * --> 虚拟组织 | |
| 238 | + * --> 摄像头 | |
| 239 | + * --> 虚拟组织 | |
| 240 | + * --> 摄像头 | |
| 241 | + * --> 摄像头 | |
| 242 | + */ | |
| 226 | 243 | String parentId = XmlUtil.getText(itemDevice, "ParentID"); |
| 227 | 244 | if (parentId != null) { |
| 228 | 245 | if (parentId.contains("/")) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
| ... | ... | @@ -46,7 +46,7 @@ public interface IMediaServerService { |
| 46 | 46 | |
| 47 | 47 | SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck); |
| 48 | 48 | |
| 49 | - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); | |
| 49 | + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); | |
| 50 | 50 | |
| 51 | 51 | void closeRTPServer(String deviceId, String channelId, String ssrc); |
| 52 | 52 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
| ... | ... | @@ -118,11 +118,11 @@ public class MediaServerServiceImpl implements IMediaServerService { |
| 118 | 118 | |
| 119 | 119 | @Override |
| 120 | 120 | public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck) { |
| 121 | - return openRTPServer(mediaServerItem, streamId, ssrcCheck,false); | |
| 121 | + return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,false); | |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | 124 | @Override |
| 125 | - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { | |
| 125 | + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback) { | |
| 126 | 126 | if (mediaServerItem == null || mediaServerItem.getId() == null) { |
| 127 | 127 | return null; |
| 128 | 128 | } |
| ... | ... | @@ -135,10 +135,14 @@ public class MediaServerServiceImpl implements IMediaServerService { |
| 135 | 135 | return null; |
| 136 | 136 | }else { |
| 137 | 137 | String ssrc = null; |
| 138 | - if (isPlayback) { | |
| 139 | - ssrc = ssrcConfig.getPlayBackSsrc(); | |
| 138 | + if (presetSsrc != null) { | |
| 139 | + ssrc = presetSsrc; | |
| 140 | 140 | }else { |
| 141 | - ssrc = ssrcConfig.getPlaySsrc(); | |
| 141 | + if (isPlayback) { | |
| 142 | + ssrc = ssrcConfig.getPlayBackSsrc(); | |
| 143 | + }else { | |
| 144 | + ssrc = ssrcConfig.getPlaySsrc(); | |
| 145 | + } | |
| 142 | 146 | } |
| 143 | 147 | |
| 144 | 148 | if (streamId == null) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -39,6 +39,7 @@ import org.springframework.stereotype.Service; |
| 39 | 39 | import org.springframework.util.ResourceUtils; |
| 40 | 40 | import org.springframework.web.context.request.async.DeferredResult; |
| 41 | 41 | |
| 42 | +import javax.sip.ResponseEvent; | |
| 42 | 43 | import java.io.FileNotFoundException; |
| 43 | 44 | import java.math.BigDecimal; |
| 44 | 45 | import java.util.*; |
| ... | ... | @@ -256,7 +257,7 @@ public class PlayServiceImpl implements IPlayService { |
| 256 | 257 | } |
| 257 | 258 | } |
| 258 | 259 | }, userSetting.getPlayTimeout()); |
| 259 | - | |
| 260 | + final String ssrc = ssrcInfo.getSsrc(); | |
| 260 | 261 | cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { |
| 261 | 262 | logger.info("收到订阅消息: " + response.toJSONString()); |
| 262 | 263 | timer.cancel(); |
| ... | ... | @@ -264,10 +265,38 @@ public class PlayServiceImpl implements IPlayService { |
| 264 | 265 | onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid); |
| 265 | 266 | hookEvent.response(mediaServerItemInuse, response); |
| 266 | 267 | }, (event) -> { |
| 268 | + ResponseEvent responseEvent = (ResponseEvent)event.event; | |
| 269 | + String contentString = new String(responseEvent.getResponse().getRawContent()); | |
| 270 | + // 获取ssrc | |
| 271 | + int ssrcIndex = contentString.indexOf("y="); | |
| 272 | + // 检查是否有y字段 | |
| 273 | + if (ssrcIndex >= 0) { | |
| 274 | + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
| 275 | + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 276 | + if (!ssrc.equals(ssrcInResponse) && device.isSsrcCheck()) { // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | |
| 277 | + // 查询 ssrcInResponse 是否可用 | |
| 278 | + if (mediaServerItem.isRtpEnable() && !mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { | |
| 279 | + // ssrc 不可用 | |
| 280 | + // 释放ssrc | |
| 281 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 282 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 283 | + event.msg = "下级自定义了ssrc,但是此ssrc不可用"; | |
| 284 | + event.statusCode = 400; | |
| 285 | + errorEvent.response(event); | |
| 286 | + return; | |
| 287 | + } | |
| 288 | + // 关闭rtp server | |
| 289 | + mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 290 | + // 重新开启ssrc server | |
| 291 | + mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false); | |
| 292 | + } | |
| 293 | + } | |
| 294 | + }, (event) -> { | |
| 267 | 295 | timer.cancel(); |
| 268 | 296 | mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); |
| 269 | 297 | // 释放ssrc |
| 270 | 298 | mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); |
| 299 | + | |
| 271 | 300 | streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); |
| 272 | 301 | errorEvent.response(event); |
| 273 | 302 | }); | ... | ... |