Commit 0f3898910c42d4303347ac7295d3c7f0cc8e8457
1 parent
f68f6d20
收流时设置re_use_port参数解决端口关闭快速打开造成的端口未释放问题, 支持点播的tcp主动
Showing
8 changed files
with
79 additions
and
30 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
| ... | ... | @@ -247,6 +247,17 @@ public class Device { |
| 247 | 247 | return streamMode; |
| 248 | 248 | } |
| 249 | 249 | |
| 250 | + public Integer getStreamModeForParam() { | |
| 251 | + if (streamMode.equalsIgnoreCase("UDP")) { | |
| 252 | + return 0; | |
| 253 | + }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) { | |
| 254 | + return 1; | |
| 255 | + }else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) { | |
| 256 | + return 2; | |
| 257 | + } | |
| 258 | + return 0; | |
| 259 | + } | |
| 260 | + | |
| 250 | 261 | public void setStreamMode(String streamMode) { |
| 251 | 262 | this.streamMode = streamMode; |
| 252 | 263 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -425,7 +425,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 425 | 425 | sendRtpItem.setApp("rtp"); |
| 426 | 426 | if ("Playback".equalsIgnoreCase(sessionName)) { |
| 427 | 427 | sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); |
| 428 | - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, device.isSsrcCheck(), true); | |
| 428 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); | |
| 429 | 429 | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| 430 | 430 | // 写入redis, 超时时回复 |
| 431 | 431 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| ... | ... | @@ -469,7 +469,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 469 | 469 | if (mediaServerItem.isRtpEnable()) { |
| 470 | 470 | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| 471 | 471 | } |
| 472 | - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false); | |
| 472 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); | |
| 473 | 473 | logger.info(JSONObject.toJSONString(ssrcInfo)); |
| 474 | 474 | sendRtpItem.setStreamId(ssrcInfo.getStream()); |
| 475 | 475 | sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
| ... | ... | @@ -346,4 +346,12 @@ public class ZLMRESTfulUtils { |
| 346 | 346 | param.put("stream_id", streamId); |
| 347 | 347 | return sendPost(mediaServerItem, "resumeRtpCheck",param, null); |
| 348 | 348 | } |
| 349 | + | |
| 350 | + public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) { | |
| 351 | + Map<String, Object> param = new HashMap<>(1); | |
| 352 | + param.put("dst_url", dst_url); | |
| 353 | + param.put("dst_port", dst_port); | |
| 354 | + param.put("stream_id", stream_id); | |
| 355 | + return sendPost(mediaServerItem, "connectRtpServer",param, null); | |
| 356 | + } | |
| 349 | 357 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| ... | ... | @@ -91,7 +91,17 @@ public class ZLMRTPServerFactory { |
| 91 | 91 | return result; |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | - public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port) { | |
| 94 | + /** | |
| 95 | + * 开启rtpServer | |
| 96 | + * @param mediaServerItem zlm服务实例 | |
| 97 | + * @param streamId 流Id | |
| 98 | + * @param ssrc ssrc | |
| 99 | + * @param port 端口, 0/null为使用随机 | |
| 100 | + * @param reUsePort 是否重用端口 | |
| 101 | + * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。 | |
| 102 | + * @return | |
| 103 | + */ | |
| 104 | + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean reUsePort, Integer tcpMode) { | |
| 95 | 105 | int result = -1; |
| 96 | 106 | // 查询此rtp server 是否已经存在 |
| 97 | 107 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); |
| ... | ... | @@ -107,7 +117,7 @@ public class ZLMRTPServerFactory { |
| 107 | 117 | JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param); |
| 108 | 118 | if (jsonObject != null ) { |
| 109 | 119 | if (jsonObject.getInteger("code") == 0) { |
| 110 | - return createRTPServer(mediaServerItem, streamId, ssrc, port); | |
| 120 | + return createRTPServer(mediaServerItem, streamId, ssrc, port, reUsePort, tcpMode); | |
| 111 | 121 | }else { |
| 112 | 122 | logger.warn("[开启rtpServer], 重启RtpServer错误"); |
| 113 | 123 | } |
| ... | ... | @@ -121,8 +131,14 @@ public class ZLMRTPServerFactory { |
| 121 | 131 | |
| 122 | 132 | Map<String, Object> param = new HashMap<>(); |
| 123 | 133 | |
| 124 | - param.put("enable_tcp", 1); | |
| 134 | + if (tcpMode == null) { | |
| 135 | + tcpMode = 0; | |
| 136 | + } | |
| 137 | + param.put("tcp_mode", tcpMode); | |
| 125 | 138 | param.put("stream_id", streamId); |
| 139 | + if (reUsePort != null) { | |
| 140 | + param.put("re_use_port", reUsePort?"1":"0"); | |
| 141 | + } | |
| 126 | 142 | // 推流端口设置0则使用随机端口 |
| 127 | 143 | if (port == null) { |
| 128 | 144 | param.put("port", 0); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
| ... | ... | @@ -44,11 +44,8 @@ public interface IMediaServerService { |
| 44 | 44 | |
| 45 | 45 | void updateVmServer(List<MediaServerItem> mediaServerItemList); |
| 46 | 46 | |
| 47 | - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); | |
| 48 | - | |
| 49 | - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); | |
| 50 | - | |
| 51 | - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port); | |
| 47 | + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, | |
| 48 | + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode); | |
| 52 | 49 | |
| 53 | 50 | void closeRTPServer(MediaServerItem mediaServerItem, String streamId); |
| 54 | 51 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
| ... | ... | @@ -125,13 +125,10 @@ public class MediaServerServiceImpl implements IMediaServerService { |
| 125 | 125 | } |
| 126 | 126 | } |
| 127 | 127 | |
| 128 | - @Override | |
| 129 | - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { | |
| 130 | - return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback); | |
| 131 | - } | |
| 132 | 128 | |
| 133 | 129 | @Override |
| 134 | - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) { | |
| 130 | + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, | |
| 131 | + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode) { | |
| 135 | 132 | if (mediaServerItem == null || mediaServerItem.getId() == null) { |
| 136 | 133 | logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null"); |
| 137 | 134 | return null; |
| ... | ... | @@ -153,7 +150,7 @@ public class MediaServerServiceImpl implements IMediaServerService { |
| 153 | 150 | } |
| 154 | 151 | int rtpServerPort; |
| 155 | 152 | if (mediaServerItem.isRtpEnable()) { |
| 156 | - rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port); | |
| 153 | + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, reUsePort, tcpMode); | |
| 157 | 154 | } else { |
| 158 | 155 | rtpServerPort = mediaServerItem.getRtpProxyPort(); |
| 159 | 156 | } |
| ... | ... | @@ -161,11 +158,6 @@ public class MediaServerServiceImpl implements IMediaServerService { |
| 161 | 158 | } |
| 162 | 159 | |
| 163 | 160 | @Override |
| 164 | - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) { | |
| 165 | - return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null); | |
| 166 | - } | |
| 167 | - | |
| 168 | - @Override | |
| 169 | 161 | public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) { |
| 170 | 162 | if (mediaServerItem == null) { |
| 171 | 163 | return; | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -43,6 +43,7 @@ import org.springframework.data.redis.core.RedisTemplate; |
| 43 | 43 | import org.springframework.stereotype.Service; |
| 44 | 44 | import org.springframework.util.ObjectUtils; |
| 45 | 45 | |
| 46 | +import javax.sdp.*; | |
| 46 | 47 | import javax.sip.InvalidArgumentException; |
| 47 | 48 | import javax.sip.ResponseEvent; |
| 48 | 49 | import javax.sip.SipException; |
| ... | ... | @@ -51,6 +52,7 @@ import java.math.RoundingMode; |
| 51 | 52 | import java.text.ParseException; |
| 52 | 53 | import java.util.List; |
| 53 | 54 | import java.util.UUID; |
| 55 | +import java.util.Vector; | |
| 54 | 56 | |
| 55 | 57 | @SuppressWarnings(value = {"rawtypes", "unchecked"}) |
| 56 | 58 | @Service |
| ... | ... | @@ -180,7 +182,7 @@ public class PlayServiceImpl implements IPlayService { |
| 180 | 182 | if (mediaServerItem.isRtpEnable()) { |
| 181 | 183 | streamId = String.format("%s_%s", device.getDeviceId(), channelId); |
| 182 | 184 | } |
| 183 | - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); | |
| 185 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); | |
| 184 | 186 | if (ssrcInfo == null) { |
| 185 | 187 | WVPResult wvpResult = new WVPResult(); |
| 186 | 188 | wvpResult.setCode(ErrorCode.ERROR100.getCode()); |
| ... | ... | @@ -296,6 +298,29 @@ public class PlayServiceImpl implements IPlayService { |
| 296 | 298 | String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); |
| 297 | 299 | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| 298 | 300 | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| 301 | + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { | |
| 302 | + String substring = contentString.substring(0, contentString.indexOf("y=")); | |
| 303 | + try { | |
| 304 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 305 | + int port = -1; | |
| 306 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | |
| 307 | + for (Object description : mediaDescriptions) { | |
| 308 | + MediaDescription mediaDescription = (MediaDescription) description; | |
| 309 | + Media media = mediaDescription.getMedia(); | |
| 310 | + | |
| 311 | + Vector mediaFormats = media.getMediaFormats(false); | |
| 312 | + if (mediaFormats.contains("96")) { | |
| 313 | + port = media.getMediaPort(); | |
| 314 | + break; | |
| 315 | + } | |
| 316 | + } | |
| 317 | + logger.info("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 318 | + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); | |
| 319 | + logger.info("[点播-TCP主动连接对方] 结果: {}", jsonObject); | |
| 320 | + } catch (SdpException e) { | |
| 321 | + logger.error("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); | |
| 322 | + } | |
| 323 | + } | |
| 299 | 324 | return; |
| 300 | 325 | } |
| 301 | 326 | logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); |
| ... | ... | @@ -331,7 +356,7 @@ public class PlayServiceImpl implements IPlayService { |
| 331 | 356 | mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ |
| 332 | 357 | if (result) { |
| 333 | 358 | // 重新开启ssrc server |
| 334 | - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort()); | |
| 359 | + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort(), true, device.getStreamModeForParam()); | |
| 335 | 360 | }else { |
| 336 | 361 | try { |
| 337 | 362 | logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); |
| ... | ... | @@ -475,8 +500,7 @@ public class PlayServiceImpl implements IPlayService { |
| 475 | 500 | return; |
| 476 | 501 | } |
| 477 | 502 | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| 478 | - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true); | |
| 479 | - | |
| 503 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); | |
| 480 | 504 | playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback); |
| 481 | 505 | } |
| 482 | 506 | |
| ... | ... | @@ -595,7 +619,7 @@ public class PlayServiceImpl implements IPlayService { |
| 595 | 619 | mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ |
| 596 | 620 | if (result) { |
| 597 | 621 | // 重新开启ssrc server |
| 598 | - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort()); | |
| 622 | + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), true, device.getStreamModeForParam()); | |
| 599 | 623 | }else { |
| 600 | 624 | try { |
| 601 | 625 | logger.warn("[回放消息]停止 {}/{}", device.getDeviceId(), channelId); |
| ... | ... | @@ -645,8 +669,7 @@ public class PlayServiceImpl implements IPlayService { |
| 645 | 669 | playBackCallback.call(downloadResult); |
| 646 | 670 | return; |
| 647 | 671 | } |
| 648 | - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true); | |
| 649 | - | |
| 672 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); | |
| 650 | 673 | download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, infoCallBack, playBackCallback); |
| 651 | 674 | } |
| 652 | 675 | |
| ... | ... | @@ -755,7 +778,7 @@ public class PlayServiceImpl implements IPlayService { |
| 755 | 778 | mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ |
| 756 | 779 | if (result) { |
| 757 | 780 | // 重新开启ssrc server |
| 758 | - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort()); | |
| 781 | + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), true, device.getStreamModeForParam()); | |
| 759 | 782 | }else { |
| 760 | 783 | try { |
| 761 | 784 | logger.warn("[录像下载] 停止{}/{}", device.getDeviceId(), channelId); | ... | ... |
web_src/src/components/DeviceList.vue
| ... | ... | @@ -25,11 +25,13 @@ |
| 25 | 25 | </el-table-column> |
| 26 | 26 | <el-table-column prop="manufacturer" label="厂家" min-width="120" > |
| 27 | 27 | </el-table-column> |
| 28 | + <el-table-column prop="transport" label="信令传输模式" min-width="120" > | |
| 29 | + </el-table-column> | |
| 28 | 30 | <el-table-column label="流传输模式" min-width="160" > |
| 29 | 31 | <template slot-scope="scope"> |
| 30 | 32 | <el-select size="mini" @change="transportChange(scope.row)" v-model="scope.row.streamMode" placeholder="请选择" style="width: 120px"> |
| 31 | 33 | <el-option key="UDP" label="UDP" value="UDP"></el-option> |
| 32 | - <el-option key="TCP-ACTIVE" label="TCP主动模式" :disabled="true" value="TCP-ACTIVE"></el-option> | |
| 34 | + <el-option key="TCP-ACTIVE" label="TCP主动模式" value="TCP-ACTIVE"></el-option> | |
| 33 | 35 | <el-option key="TCP-PASSIVE" label="TCP被动模式" value="TCP-PASSIVE"></el-option> |
| 34 | 36 | </el-select> |
| 35 | 37 | </template> | ... | ... |