Commit 66210ec51ae0f20288585293cff5ac187a6d39da
1 parent
c8b0e66e
支持国标级联语音喊话TCP主动模式
Showing
4 changed files
with
219 additions
and
55 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| @@ -978,8 +978,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -978,8 +978,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 978 | return; | 978 | return; |
| 979 | } | 979 | } |
| 980 | String contentString = new String(request.getRawContent()); | 980 | String contentString = new String(request.getRawContent()); |
| 981 | - // jainSip不支持y=字段, 移除移除以解析。 | ||
| 982 | - String ssrc = "0000000404"; | ||
| 983 | 981 | ||
| 984 | try { | 982 | try { |
| 985 | Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | 983 | Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); |
| @@ -1027,7 +1025,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1027,7 +1025,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1027 | return; | 1025 | return; |
| 1028 | } | 1026 | } |
| 1029 | String addressStr = sdp.getOrigin().getAddress(); | 1027 | String addressStr = sdp.getOrigin().getAddress(); |
| 1030 | - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, ssrc, | 1028 | + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}, {}", requesterId, addressStr, port, gb28181Sdp.getSsrc(), |
| 1031 | mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP"); | 1029 | mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP"); |
| 1032 | 1030 | ||
| 1033 | MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem(); | 1031 | MediaServerItem mediaServerItem = broadcastCatch.getMediaServerItem(); |
| @@ -1041,11 +1039,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1041,11 +1039,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1041 | } | 1039 | } |
| 1042 | return; | 1040 | return; |
| 1043 | } | 1041 | } |
| 1044 | - logger.info("设备{}请求语音流, 收流地址:{}:{},ssrc:{}, {}, 对讲方式:{}", requesterId, addressStr, port, ssrc, | 1042 | + logger.info("设备{}请求语音流, 收流地址:{}:{},ssrc:{}, {}, 对讲方式:{}", requesterId, addressStr, port, gb28181Sdp.getSsrc(), |
| 1045 | mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue()); | 1043 | mediaTransmissionTCP ? (tcpActive ? "TCP主动" : "TCP被动") : "UDP", sdp.getSessionName().getValue()); |
| 1046 | CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); | 1044 | CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); |
| 1047 | 1045 | ||
| 1048 | - SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, | 1046 | + SendRtpItem sendRtpItem = zlmServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, gb28181Sdp.getSsrc(), requesterId, |
| 1049 | device.getDeviceId(), broadcastCatch.getChannelId(), | 1047 | device.getDeviceId(), broadcastCatch.getChannelId(), |
| 1050 | mediaTransmissionTCP, false); | 1048 | mediaTransmissionTCP, false); |
| 1051 | 1049 | ||
| @@ -1081,7 +1079,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | @@ -1081,7 +1079,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements | ||
| 1081 | 1079 | ||
| 1082 | Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); | 1080 | Boolean streamReady = zlmServerFactory.isStreamReady(mediaServerItem, broadcastCatch.getApp(), broadcastCatch.getStream()); |
| 1083 | if (streamReady) { | 1081 | if (streamReady) { |
| 1084 | - sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, ssrc); | 1082 | + sendOk(device, sendRtpItem, sdp, request, mediaServerItem, mediaTransmissionTCP, gb28181Sdp.getSsrc()); |
| 1085 | } else { | 1083 | } else { |
| 1086 | logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); | 1084 | logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", broadcastCatch.getApp(), broadcastCatch.getStream()); |
| 1087 | try { | 1085 | try { |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
| @@ -206,7 +206,7 @@ public class MediaServerServiceImpl implements IMediaServerService { | @@ -206,7 +206,7 @@ public class MediaServerServiceImpl implements IMediaServerService { | ||
| 206 | @Override | 206 | @Override |
| 207 | public void closeRTPServer(String mediaServerId, String streamId) { | 207 | public void closeRTPServer(String mediaServerId, String streamId) { |
| 208 | MediaServerItem mediaServerItem = this.getOne(mediaServerId); | 208 | MediaServerItem mediaServerItem = this.getOne(mediaServerId); |
| 209 | - if (mediaServerItem.isRtpEnable()) { | 209 | + if (mediaServerItem != null && mediaServerItem.isRtpEnable()) { |
| 210 | closeRTPServer(mediaServerItem, streamId); | 210 | closeRTPServer(mediaServerItem, streamId); |
| 211 | } | 211 | } |
| 212 | zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId); | 212 | zlmresTfulUtils.closeStreams(mediaServerItem, "rtp", streamId); |
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
| 1 | package com.genersoft.iot.vmp.service.impl; | 1 | package com.genersoft.iot.vmp.service.impl; |
| 2 | 2 | ||
| 3 | +import com.alibaba.fastjson2.JSONObject; | ||
| 3 | import com.genersoft.iot.vmp.common.InviteInfo; | 4 | import com.genersoft.iot.vmp.common.InviteInfo; |
| 5 | +import com.genersoft.iot.vmp.common.InviteSessionStatus; | ||
| 4 | import com.genersoft.iot.vmp.common.InviteSessionType; | 6 | import com.genersoft.iot.vmp.common.InviteSessionType; |
| 5 | import com.baomidou.dynamic.datasource.annotation.DS; | 7 | import com.baomidou.dynamic.datasource.annotation.DS; |
| 6 | import com.genersoft.iot.vmp.conf.DynamicTask; | 8 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| @@ -11,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | @@ -11,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | ||
| 11 | import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; | 13 | import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; |
| 12 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | 14 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 13 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | 15 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| 16 | +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | ||
| 14 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; | 17 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 15 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; | 18 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; |
| 16 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; | 19 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; |
| @@ -22,20 +25,20 @@ import com.genersoft.iot.vmp.service.IInviteStreamService; | @@ -22,20 +25,20 @@ import com.genersoft.iot.vmp.service.IInviteStreamService; | ||
| 22 | import com.genersoft.iot.vmp.service.IMediaServerService; | 25 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 23 | import com.genersoft.iot.vmp.service.IPlatformService; | 26 | import com.genersoft.iot.vmp.service.IPlatformService; |
| 24 | import com.genersoft.iot.vmp.service.IPlayService; | 27 | import com.genersoft.iot.vmp.service.IPlayService; |
| 25 | -import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; | ||
| 26 | -import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; | ||
| 27 | -import com.genersoft.iot.vmp.service.bean.SSRCInfo; | 28 | +import com.genersoft.iot.vmp.service.bean.*; |
| 28 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 29 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 29 | import com.genersoft.iot.vmp.storager.dao.*; | 30 | import com.genersoft.iot.vmp.storager.dao.*; |
| 30 | import com.genersoft.iot.vmp.utils.DateUtil; | 31 | import com.genersoft.iot.vmp.utils.DateUtil; |
| 31 | import com.github.pagehelper.PageHelper; | 32 | import com.github.pagehelper.PageHelper; |
| 32 | import com.github.pagehelper.PageInfo; | 33 | import com.github.pagehelper.PageInfo; |
| 33 | import gov.nist.javax.sip.message.SIPRequest; | 34 | import gov.nist.javax.sip.message.SIPRequest; |
| 35 | +import gov.nist.javax.sip.message.SIPResponse; | ||
| 34 | import org.slf4j.Logger; | 36 | import org.slf4j.Logger; |
| 35 | import org.slf4j.LoggerFactory; | 37 | import org.slf4j.LoggerFactory; |
| 36 | import org.springframework.beans.factory.annotation.Autowired; | 38 | import org.springframework.beans.factory.annotation.Autowired; |
| 37 | import org.springframework.stereotype.Service; | 39 | import org.springframework.stereotype.Service; |
| 38 | 40 | ||
| 41 | +import javax.sdp.*; | ||
| 39 | import javax.sip.InvalidArgumentException; | 42 | import javax.sip.InvalidArgumentException; |
| 40 | import javax.sip.ResponseEvent; | 43 | import javax.sip.ResponseEvent; |
| 41 | import javax.sip.PeerUnavailableException; | 44 | import javax.sip.PeerUnavailableException; |
| @@ -109,6 +112,9 @@ public class PlatformServiceImpl implements IPlatformService { | @@ -109,6 +112,9 @@ public class PlatformServiceImpl implements IPlatformService { | ||
| 109 | @Autowired | 112 | @Autowired |
| 110 | private IInviteStreamService inviteStreamService; | 113 | private IInviteStreamService inviteStreamService; |
| 111 | 114 | ||
| 115 | + @Autowired | ||
| 116 | + private ZLMRESTfulUtils zlmresTfulUtils; | ||
| 117 | + | ||
| 112 | 118 | ||
| 113 | @Override | 119 | @Override |
| 114 | public ParentPlatform queryPlatformByServerGBId(String platformGbId) { | 120 | public ParentPlatform queryPlatformByServerGBId(String platformGbId) { |
| @@ -466,21 +472,21 @@ public class PlatformServiceImpl implements IPlatformService { | @@ -466,21 +472,21 @@ public class PlatformServiceImpl implements IPlatformService { | ||
| 466 | logger.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); | 472 | logger.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); |
| 467 | return; | 473 | return; |
| 468 | } | 474 | } |
| 469 | - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId); | 475 | + InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, platform.getServerGBId(), channelId); |
| 470 | 476 | ||
| 471 | - if (inviteInfo != null && inviteInfo.getStreamInfo() != null) { | 477 | + if (inviteInfoForOld != null && inviteInfoForOld.getStreamInfo() != null) { |
| 472 | // 如果zlm不存在这个流,则删除数据即可 | 478 | // 如果zlm不存在这个流,则删除数据即可 |
| 473 | - MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfo.getStreamInfo().getMediaServerId()); | 479 | + MediaServerItem mediaServerItemForStreamInfo = mediaServerService.getOne(inviteInfoForOld.getStreamInfo().getMediaServerId()); |
| 474 | if (mediaServerItemForStreamInfo != null) { | 480 | if (mediaServerItemForStreamInfo != null) { |
| 475 | - Boolean ready = zlmServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfo.getStreamInfo().getApp(), inviteInfo.getStreamInfo().getStream()); | 481 | + Boolean ready = zlmServerFactory.isStreamReady(mediaServerItemForStreamInfo, inviteInfoForOld.getStreamInfo().getApp(), inviteInfoForOld.getStreamInfo().getStream()); |
| 476 | if (!ready) { | 482 | if (!ready) { |
| 477 | // 错误存在于redis中的数据 | 483 | // 错误存在于redis中的数据 |
| 478 | - inviteStreamService.removeInviteInfo(inviteInfo); | 484 | + inviteStreamService.removeInviteInfo(inviteInfoForOld); |
| 479 | }else { | 485 | }else { |
| 480 | // 流确实尚在推流,直接回调结果 | 486 | // 流确实尚在推流,直接回调结果 |
| 481 | OnStreamChangedHookParam hookParam = new OnStreamChangedHookParam(); | 487 | OnStreamChangedHookParam hookParam = new OnStreamChangedHookParam(); |
| 482 | - hookParam.setApp(inviteInfo.getStreamInfo().getApp()); | ||
| 483 | - hookParam.setStream(inviteInfo.getStreamInfo().getStream()); | 488 | + hookParam.setApp(inviteInfoForOld.getStreamInfo().getApp()); |
| 489 | + hookParam.setStream(inviteInfoForOld.getStreamInfo().getStream()); | ||
| 484 | 490 | ||
| 485 | hookEvent.response(mediaServerItemForStreamInfo, hookParam); | 491 | hookEvent.response(mediaServerItemForStreamInfo, hookParam); |
| 486 | return; | 492 | return; |
| @@ -515,6 +521,11 @@ public class PlatformServiceImpl implements IPlatformService { | @@ -515,6 +521,11 @@ public class PlatformServiceImpl implements IPlatformService { | ||
| 515 | logger.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", | 521 | logger.info("[国标级联] 语音喊话,发起Invite消息 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", |
| 516 | platform.getServerGBId(), channelId, ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); | 522 | platform.getServerGBId(), channelId, ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); |
| 517 | 523 | ||
| 524 | + // 初始化redis中的invite消息状态 | ||
| 525 | + InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channelId, ssrcInfo.getStream(), ssrcInfo, | ||
| 526 | + mediaServerItem.getSdpIp(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), InviteSessionType.BROADCAST, | ||
| 527 | + InviteSessionStatus.ready); | ||
| 528 | + inviteStreamService.updateInviteInfo(inviteInfo); | ||
| 518 | String timeOutTaskKey = UUID.randomUUID().toString(); | 529 | String timeOutTaskKey = UUID.randomUUID().toString(); |
| 519 | dynamicTask.startDelay(timeOutTaskKey, () -> { | 530 | dynamicTask.startDelay(timeOutTaskKey, () -> { |
| 520 | // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 | 531 | // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 |
| @@ -545,50 +556,206 @@ public class PlatformServiceImpl implements IPlatformService { | @@ -545,50 +556,206 @@ public class PlatformServiceImpl implements IPlatformService { | ||
| 545 | hookEvent.response(mediaServerItem, hookParam); | 556 | hookEvent.response(mediaServerItem, hookParam); |
| 546 | } | 557 | } |
| 547 | }, event -> { | 558 | }, event -> { |
| 548 | - // 收到200OK 检测ssrc是否有变化,防止上级自定义了ssrc | ||
| 549 | - ResponseEvent responseEvent = (ResponseEvent) event.event; | ||
| 550 | - String contentString = new String(responseEvent.getResponse().getRawContent()); | ||
| 551 | - // 获取ssrc | ||
| 552 | - int ssrcIndex = contentString.indexOf("y="); | ||
| 553 | - // 检查是否有y字段 | ||
| 554 | - if (ssrcIndex >= 0) { | ||
| 555 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | ||
| 556 | - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | ||
| 557 | - // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | ||
| 558 | - if (ssrcInfo.getSsrc().equals(ssrcInResponse) || ssrcCheck) { | ||
| 559 | - return; | 559 | + |
| 560 | + inviteOKHandler(event, ssrcInfo, tcpMode, ssrcCheck, mediaServerItem, platform, channelId, timeOutTaskKey, | ||
| 561 | + null, inviteInfo, InviteSessionType.BROADCAST); | ||
| 562 | +// // 收到200OK 检测ssrc是否有变化,防止上级自定义了ssrc | ||
| 563 | +// ResponseEvent responseEvent = (ResponseEvent) event.event; | ||
| 564 | +// String contentString = new String(responseEvent.getResponse().getRawContent()); | ||
| 565 | +// // 获取ssrc | ||
| 566 | +// int ssrcIndex = contentString.indexOf("y="); | ||
| 567 | +// // 检查是否有y字段 | ||
| 568 | +// if (ssrcIndex >= 0) { | ||
| 569 | +// //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | ||
| 570 | +// String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | ||
| 571 | +// // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | ||
| 572 | +// if (ssrcInfo.getSsrc().equals(ssrcInResponse) || ssrcCheck) { | ||
| 573 | +// tcpActiveHandler(platform, ) | ||
| 574 | +// return; | ||
| 575 | +// } | ||
| 576 | +// logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | ||
| 577 | +// if (!mediaServerItem.isRtpEnable()) { | ||
| 578 | +// logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); | ||
| 579 | +// // 释放ssrc | ||
| 580 | +// mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | ||
| 581 | +// // 单端口模式streamId也有变化,需要重新设置监听 | ||
| 582 | +// if (!mediaServerItem.isRtpEnable()) { | ||
| 583 | +// // 添加订阅 | ||
| 584 | +// HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); | ||
| 585 | +// subscribe.removeSubscribe(hookSubscribe); | ||
| 586 | +// hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | ||
| 587 | +// subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { | ||
| 588 | +// logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); | ||
| 589 | +// dynamicTask.stop(timeOutTaskKey); | ||
| 590 | +// // hook响应 | ||
| 591 | +// playService.onPublishHandlerForPlay(mediaServerItemInUse, hookParam, platform.getServerGBId(), channelId); | ||
| 592 | +// hookEvent.response(mediaServerItemInUse, hookParam); | ||
| 593 | +// }); | ||
| 594 | +// } | ||
| 595 | +// // 关闭rtp server | ||
| 596 | +// mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); | ||
| 597 | +// // 重新开启ssrc server | ||
| 598 | +// mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, false, false, ssrcInfo.getPort(), true, false, tcpMode); | ||
| 599 | +// } | ||
| 600 | +// } | ||
| 601 | + }, eventResult -> { | ||
| 602 | + // 收到错误回复 | ||
| 603 | + if (errorEvent != null) { | ||
| 604 | + errorEvent.response(eventResult); | ||
| 605 | + } | ||
| 606 | + }); | ||
| 607 | + } | ||
| 608 | + | ||
| 609 | + private void inviteOKHandler(SipSubscribe.EventResult eventResult, SSRCInfo ssrcInfo, int tcpMode, boolean ssrcCheck, MediaServerItem mediaServerItem, | ||
| 610 | + ParentPlatform platform, String channelId, String timeOutTaskKey, ErrorCallback<Object> callback, | ||
| 611 | + InviteInfo inviteInfo, InviteSessionType inviteSessionType){ | ||
| 612 | + inviteInfo.setStatus(InviteSessionStatus.ok); | ||
| 613 | + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; | ||
| 614 | + String contentString = new String(responseEvent.getResponse().getRawContent()); | ||
| 615 | + System.out.println(1111); | ||
| 616 | + System.out.println(contentString); | ||
| 617 | + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); | ||
| 618 | + // 兼容回复的消息中缺少ssrc(y字段)的情况 | ||
| 619 | + if (ssrcInResponse == null) { | ||
| 620 | + ssrcInResponse = ssrcInfo.getSsrc(); | ||
| 621 | + } | ||
| 622 | + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { | ||
| 623 | + // ssrc 一致 | ||
| 624 | + if (mediaServerItem.isRtpEnable()) { | ||
| 625 | + // 多端口 | ||
| 626 | + if (tcpMode == 2) { | ||
| 627 | + tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, | ||
| 628 | + timeOutTaskKey, ssrcInfo, callback); | ||
| 629 | + } | ||
| 630 | + }else { | ||
| 631 | + // 单端口 | ||
| 632 | + if (tcpMode == 2) { | ||
| 633 | + logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); | ||
| 560 | } | 634 | } |
| 561 | - logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | ||
| 562 | - if (!mediaServerItem.isRtpEnable()) { | ||
| 563 | - logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); | 635 | + } |
| 636 | + }else { | ||
| 637 | + logger.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | ||
| 638 | + // ssrc 不一致 | ||
| 639 | + if (mediaServerItem.isRtpEnable()) { | ||
| 640 | + // 多端口 | ||
| 641 | + if (ssrcCheck) { | ||
| 642 | + // ssrc检验 | ||
| 643 | + // 更新ssrc | ||
| 644 | + logger.info("[Invite 200OK] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); | ||
| 564 | // 释放ssrc | 645 | // 释放ssrc |
| 565 | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | 646 | mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); |
| 566 | - // 单端口模式streamId也有变化,需要重新设置监听 | ||
| 567 | - if (!mediaServerItem.isRtpEnable()) { | ||
| 568 | - // 添加订阅 | ||
| 569 | - HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); | ||
| 570 | - subscribe.removeSubscribe(hookSubscribe); | ||
| 571 | - hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | ||
| 572 | - subscribe.addSubscribe(hookSubscribe, (mediaServerItemInUse, hookParam) -> { | ||
| 573 | - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + hookParam); | ||
| 574 | - dynamicTask.stop(timeOutTaskKey); | ||
| 575 | - // hook响应 | ||
| 576 | - playService.onPublishHandlerForPlay(mediaServerItemInUse, hookParam, platform.getServerGBId(), channelId); | ||
| 577 | - hookEvent.response(mediaServerItemInUse, hookParam); | ||
| 578 | - }); | 647 | + Boolean result = mediaServerService.updateRtpServerSSRC(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse); |
| 648 | + if (!result) { | ||
| 649 | + try { | ||
| 650 | + logger.warn("[Invite 200OK] 更新ssrc失败,停止喊话 {}/{}", platform.getServerGBId(), channelId); | ||
| 651 | + commanderForPlatform.streamByeCmd(platform, channelId, ssrcInfo.getStream(), null, null); | ||
| 652 | + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { | ||
| 653 | + logger.error("[命令发送失败] 停止播放, 发送BYE: {}", e.getMessage()); | ||
| 654 | + } | ||
| 655 | + | ||
| 656 | + dynamicTask.stop(timeOutTaskKey); | ||
| 657 | + // 释放ssrc | ||
| 658 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | ||
| 659 | + | ||
| 660 | + streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); | ||
| 661 | + | ||
| 662 | + callback.run(InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | ||
| 663 | + "下级自定义了ssrc,重新设置收流信息失败", null); | ||
| 664 | + inviteStreamService.call(inviteSessionType, platform.getServerGBId(), channelId, null, | ||
| 665 | + InviteErrorCode.ERROR_FOR_RESET_SSRC.getCode(), | ||
| 666 | + "下级自定义了ssrc,重新设置收流信息失败", null); | ||
| 667 | + | ||
| 668 | + }else { | ||
| 669 | + ssrcInfo.setSsrc(ssrcInResponse); | ||
| 670 | + inviteInfo.setSsrcInfo(ssrcInfo); | ||
| 671 | + inviteInfo.setStream(ssrcInfo.getStream()); | ||
| 672 | + if (tcpMode == 2) { | ||
| 673 | + if (mediaServerItem.isRtpEnable()) { | ||
| 674 | + tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, | ||
| 675 | + timeOutTaskKey, ssrcInfo, callback); | ||
| 676 | + }else { | ||
| 677 | + logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); | ||
| 678 | + } | ||
| 679 | + } | ||
| 680 | + inviteStreamService.updateInviteInfo(inviteInfo); | ||
| 579 | } | 681 | } |
| 580 | - // 关闭rtp server | ||
| 581 | - mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); | ||
| 582 | - // 重新开启ssrc server | ||
| 583 | - mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, false, false, ssrcInfo.getPort(), true, false, tcpMode); | 682 | + }else { |
| 683 | + ssrcInfo.setSsrc(ssrcInResponse); | ||
| 684 | + inviteInfo.setSsrcInfo(ssrcInfo); | ||
| 685 | + inviteInfo.setStream(ssrcInfo.getStream()); | ||
| 686 | + if (tcpMode == 2) { | ||
| 687 | + if (mediaServerItem.isRtpEnable()) { | ||
| 688 | + tcpActiveHandler(platform, channelId, contentString, mediaServerItem, tcpMode, ssrcCheck, | ||
| 689 | + timeOutTaskKey, ssrcInfo, callback); | ||
| 690 | + }else { | ||
| 691 | + logger.warn("[Invite 200OK] 单端口收流模式不支持tcp主动模式收流"); | ||
| 692 | + } | ||
| 693 | + } | ||
| 694 | + inviteStreamService.updateInviteInfo(inviteInfo); | ||
| 695 | + } | ||
| 696 | + }else { | ||
| 697 | + if (ssrcInResponse != null) { | ||
| 698 | + // 单端口 | ||
| 699 | + // 重新订阅流上线 | ||
| 700 | + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(inviteInfo.getDeviceId(), | ||
| 701 | + inviteInfo.getChannelId(), null, inviteInfo.getStream()); | ||
| 702 | + streamSession.remove(inviteInfo.getDeviceId(), | ||
| 703 | + inviteInfo.getChannelId(), inviteInfo.getStream()); | ||
| 704 | + inviteStreamService.updateInviteInfoForSSRC(inviteInfo, ssrcInResponse); | ||
| 705 | + streamSession.put(platform.getServerGBId(), channelId, ssrcTransaction.getCallId(), | ||
| 706 | + inviteInfo.getStream(), ssrcInResponse, mediaServerItem.getId(), (SIPResponse) responseEvent.getResponse(), inviteSessionType); | ||
| 584 | } | 707 | } |
| 585 | } | 708 | } |
| 586 | - }, eventResult -> { | ||
| 587 | - // 收到错误回复 | ||
| 588 | - if (errorEvent != null) { | ||
| 589 | - errorEvent.response(eventResult); | 709 | + } |
| 710 | + } | ||
| 711 | + | ||
| 712 | + | ||
| 713 | + private void tcpActiveHandler(ParentPlatform platform, String channelId, String contentString, | ||
| 714 | + MediaServerItem mediaServerItem, int tcpMode, boolean ssrcCheck, | ||
| 715 | + String timeOutTaskKey, SSRCInfo ssrcInfo, ErrorCallback<Object> callback){ | ||
| 716 | + if (tcpMode != 2) { | ||
| 717 | + return; | ||
| 718 | + } | ||
| 719 | + | ||
| 720 | + String substring; | ||
| 721 | + if (contentString.indexOf("y=") > 0) { | ||
| 722 | + substring = contentString.substring(0, contentString.indexOf("y=")); | ||
| 723 | + }else { | ||
| 724 | + substring = contentString; | ||
| 725 | + } | ||
| 726 | + try { | ||
| 727 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | ||
| 728 | + int port = -1; | ||
| 729 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | ||
| 730 | + for (Object description : mediaDescriptions) { | ||
| 731 | + MediaDescription mediaDescription = (MediaDescription) description; | ||
| 732 | + Media media = mediaDescription.getMedia(); | ||
| 733 | + | ||
| 734 | + Vector mediaFormats = media.getMediaFormats(false); | ||
| 735 | + if (mediaFormats.contains("8") || mediaFormats.contains("0")) { | ||
| 736 | + port = media.getMediaPort(); | ||
| 737 | + break; | ||
| 738 | + } | ||
| 590 | } | 739 | } |
| 591 | - }); | 740 | + logger.info("[TCP主动连接对方] serverGbId: {}, channelId: {}, 连接对方的地址:{}:{}, SSRC: {}, SSRC校验:{}", |
| 741 | + platform.getServerGBId(), channelId, sdp.getConnection().getAddress(), port, ssrcInfo.getSsrc(), ssrcCheck); | ||
| 742 | + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); | ||
| 743 | + logger.info("[TCP主动连接对方] 结果: {}", jsonObject); | ||
| 744 | + } catch (SdpException e) { | ||
| 745 | + logger.error("[TCP主动连接对方] serverGbId: {}, channelId: {}, 解析200OK的SDP信息失败", platform.getServerGBId(), channelId, e); | ||
| 746 | + dynamicTask.stop(timeOutTaskKey); | ||
| 747 | + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); | ||
| 748 | + // 释放ssrc | ||
| 749 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | ||
| 750 | + | ||
| 751 | + streamSession.remove(platform.getServerGBId(), channelId, ssrcInfo.getStream()); | ||
| 752 | + | ||
| 753 | + callback.run(InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), | ||
| 754 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); | ||
| 755 | + inviteStreamService.call(InviteSessionType.PLAY, platform.getServerGBId(), channelId, null, | ||
| 756 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getCode(), | ||
| 757 | + InviteErrorCode.ERROR_FOR_SDP_PARSING_EXCEPTIONS.getMsg(), null); | ||
| 758 | + } | ||
| 592 | } | 759 | } |
| 593 | 760 | ||
| 594 | @Override | 761 | @Override |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
| @@ -18,7 +18,6 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; | @@ -18,7 +18,6 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; | ||
| 18 | import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; | 18 | import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; |
| 19 | import com.github.pagehelper.PageHelper; | 19 | import com.github.pagehelper.PageHelper; |
| 20 | import com.github.pagehelper.PageInfo; | 20 | import com.github.pagehelper.PageInfo; |
| 21 | -import com.sun.org.apache.xml.internal.resolver.Catalog; | ||
| 22 | import org.slf4j.Logger; | 21 | import org.slf4j.Logger; |
| 23 | import org.slf4j.LoggerFactory; | 22 | import org.slf4j.LoggerFactory; |
| 24 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |