Commit 06bbe3fe01e5af9486c309693a975077df813f7c
1 parent
ebe8667b
添加第二种语音对讲实现
Showing
29 changed files
with
876 additions
and
576 deletions
pom.xml
| ... | ... | @@ -273,7 +273,6 @@ |
| 273 | 273 | <dateFormat>yyyyMMdd</dateFormat> |
| 274 | 274 | </configuration> |
| 275 | 275 | </plugin> |
| 276 | - | |
| 277 | 276 | <plugin> |
| 278 | 277 | <groupId>org.apache.maven.plugins</groupId> |
| 279 | 278 | <artifactId>maven-surefire-plugin</artifactId> |
| ... | ... | @@ -282,7 +281,6 @@ |
| 282 | 281 | <skipTests>true</skipTests> |
| 283 | 282 | </configuration> |
| 284 | 283 | </plugin> |
| 285 | - | |
| 286 | 284 | </plugins> |
| 287 | 285 | <resources> |
| 288 | 286 | <resource> | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java
| ... | ... | @@ -39,12 +39,12 @@ public class SsrcTransactionNotFoundException extends Exception{ |
| 39 | 39 | @Override |
| 40 | 40 | public String getMessage() { |
| 41 | 41 | StringBuffer msg = new StringBuffer(); |
| 42 | - msg.append(StringFormatter.format("缓存事务信息未找到,device:%s channel: %s ", deviceId, channelId)); | |
| 42 | + msg.append(String.format("缓存事务信息未找到,device:%s channel: %s ", deviceId, channelId)); | |
| 43 | 43 | if (callId != null) { |
| 44 | - msg.append("callId: " + callId); | |
| 44 | + msg.append(",callId: " + callId); | |
| 45 | 45 | } |
| 46 | 46 | if (stream != null) { |
| 47 | - msg.append("stream: " + stream); | |
| 47 | + msg.append(",stream: " + stream); | |
| 48 | 48 | } |
| 49 | 49 | return msg.toString(); |
| 50 | 50 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/AudioBroadcastCatch.java
| ... | ... | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.bean; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | 4 | import gov.nist.javax.sip.message.SIPRequest; |
| 5 | +import gov.nist.javax.sip.message.SIPResponse; | |
| 5 | 6 | import gov.nist.javax.sip.stack.SIPDialog; |
| 6 | 7 | |
| 7 | 8 | import javax.sip.Dialog; |
| ... | ... | @@ -40,12 +41,7 @@ public class AudioBroadcastCatch { |
| 40 | 41 | /** |
| 41 | 42 | * 请求信息 |
| 42 | 43 | */ |
| 43 | - private SIPRequest request; | |
| 44 | - | |
| 45 | - /** | |
| 46 | - * 会话信息 | |
| 47 | - */ | |
| 48 | - private SIPDialog dialog; | |
| 44 | + private SipTransactionInfo sipTransactionInfo; | |
| 49 | 45 | |
| 50 | 46 | |
| 51 | 47 | public String getDeviceId() { |
| ... | ... | @@ -72,19 +68,15 @@ public class AudioBroadcastCatch { |
| 72 | 68 | this.status = status; |
| 73 | 69 | } |
| 74 | 70 | |
| 75 | - public void setDialog(SIPDialog dialog) { | |
| 76 | - this.dialog = dialog; | |
| 77 | - } | |
| 78 | - | |
| 79 | - public SIPDialog getDialog() { | |
| 80 | - return dialog; | |
| 71 | + public SipTransactionInfo getSipTransactionInfo() { | |
| 72 | + return sipTransactionInfo; | |
| 81 | 73 | } |
| 82 | 74 | |
| 83 | - public SIPRequest getRequest() { | |
| 84 | - return request; | |
| 75 | + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { | |
| 76 | + this.sipTransactionInfo = sipTransactionInfo; | |
| 85 | 77 | } |
| 86 | 78 | |
| 87 | - public void setRequest(SIPRequest request) { | |
| 88 | - this.request = request; | |
| 79 | + public void setSipTransactionInfoByRequset(SIPResponse response) { | |
| 80 | + this.sipTransactionInfo = new SipTransactionInfo(response); | |
| 89 | 81 | } |
| 90 | 82 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.event; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | |
| 3 | 4 | import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent; |
| 4 | 5 | import gov.nist.javax.sip.message.SIPRequest; |
| 5 | 6 | import org.slf4j.Logger; |
| ... | ... | @@ -57,7 +58,7 @@ public class SipSubscribe { |
| 57 | 58 | logger.debug("errorSubscribes.size:{}",errorSubscribes.size()); |
| 58 | 59 | } |
| 59 | 60 | |
| 60 | - public interface Event { void response(EventResult eventResult) ; | |
| 61 | + public interface Event { void response(EventResult eventResult); | |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 64 | /** | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| ... | ... | @@ -8,18 +8,12 @@ import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 8 | 8 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 9 | 9 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 10 | 10 | import gov.nist.javax.sip.message.SIPRequest; |
| 11 | -import gov.nist.javax.sip.message.SIPRequest; | |
| 12 | -import gov.nist.javax.sip.stack.SIPDialog; | |
| 13 | 11 | |
| 14 | -import javax.sip.Dialog; | |
| 15 | -import javax.sip.InvalidArgumentException; | |
| 16 | -import javax.sip.SipException; | |
| 17 | -import java.text.ParseException; | |
| 18 | 12 | import javax.sip.InvalidArgumentException; |
| 19 | 13 | import javax.sip.PeerUnavailableException; |
| 20 | 14 | import javax.sip.SipException; |
| 21 | -import javax.sip.message.Request; | |
| 22 | 15 | import java.text.ParseException; |
| 16 | +import javax.sip.message.Request; | |
| 23 | 17 | |
| 24 | 18 | /** |
| 25 | 19 | * @description:设备能力接口,用于定义设备的控制、查询能力 |
| ... | ... | @@ -130,14 +124,18 @@ public interface ISIPCommander { |
| 130 | 124 | String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, |
| 131 | 125 | SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; |
| 132 | 126 | |
| 127 | + | |
| 133 | 128 | /** |
| 134 | 129 | * 视频流停止 |
| 135 | 130 | */ |
| 136 | - void streamByeCmd(String deviceId, String channelId, String stream, String callId, SipSubscribe.Event okEvent); | |
| 137 | - void streamByeCmd(String deviceId, String channelId, String stream, String callId); | |
| 138 | 131 | void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; |
| 132 | + | |
| 133 | + void talkStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; | |
| 134 | + | |
| 139 | 135 | void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException; |
| 140 | 136 | |
| 137 | + void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; | |
| 138 | + | |
| 141 | 139 | /** |
| 142 | 140 | * 回放暂停 |
| 143 | 141 | */ |
| ... | ... | @@ -168,22 +166,12 @@ public interface ISIPCommander { |
| 168 | 166 | |
| 169 | 167 | |
| 170 | 168 | /** |
| 171 | - | |
| 172 | - /** | |
| 173 | - * 语音广播 | |
| 174 | - * | |
| 175 | - * @param device 视频设备 | |
| 176 | - */ | |
| 177 | - boolean audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent); | |
| 178 | - void audioBroadcastCmd(Device device,String channelId); | |
| 179 | - | |
| 180 | - /** | |
| 169 | + * /** | |
| 181 | 170 | * 语音广播 |
| 182 | 171 | * |
| 183 | - * @param device 视频设备 | |
| 172 | + * @param device 视频设备 | |
| 184 | 173 | */ |
| 185 | - void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; | |
| 186 | - void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException; | |
| 174 | + void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; | |
| 187 | 175 | |
| 188 | 176 | /** |
| 189 | 177 | * 音视频录像控制 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -326,4 +326,37 @@ public class SIPRequestHeaderProvider { |
| 326 | 326 | |
| 327 | 327 | return request; |
| 328 | 328 | } |
| 329 | + | |
| 330 | + public Request createBroadcastMessageRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { | |
| 331 | + Request request = null; | |
| 332 | + // sipuri | |
| 333 | + SipURI requestURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); | |
| 334 | + // via | |
| 335 | + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); | |
| 336 | + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); | |
| 337 | + viaHeader.setRPort(); | |
| 338 | + viaHeaders.add(viaHeader); | |
| 339 | + // from | |
| 340 | + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); | |
| 341 | + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); | |
| 342 | + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); | |
| 343 | + // to | |
| 344 | + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); | |
| 345 | + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); | |
| 346 | + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); | |
| 347 | + | |
| 348 | + // Forwards | |
| 349 | + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); | |
| 350 | + // ceq | |
| 351 | + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); | |
| 352 | + | |
| 353 | + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); | |
| 354 | + | |
| 355 | + request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, | |
| 356 | + toHeader, viaHeaders, maxForwards, contentTypeHeader, content); | |
| 357 | + | |
| 358 | + request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil)); | |
| 359 | + | |
| 360 | + return request; | |
| 361 | + } | |
| 329 | 362 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | 4 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 5 | -import com.genersoft.iot.vmp.conf.DynamicTask; | |
| 6 | 5 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 7 | 6 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 8 | 7 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| ... | ... | @@ -12,45 +11,32 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 12 | 11 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 13 | 12 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; |
| 14 | 13 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| 14 | +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | |
| 15 | 15 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; |
| 16 | 16 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; |
| 17 | +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamPush; | |
| 17 | 18 | import com.genersoft.iot.vmp.utils.DateUtil; |
| 18 | 19 | import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; |
| 19 | 20 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 20 | 21 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 21 | 22 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 22 | 23 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 23 | -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 24 | -import com.genersoft.iot.vmp.storager.IVideoManagerStorage; | |
| 25 | 24 | import com.genersoft.iot.vmp.utils.GitUtil; |
| 26 | -import gov.nist.javax.sip.SIPConstants; | |
| 27 | 25 | import gov.nist.javax.sip.SipProviderImpl; |
| 28 | -import gov.nist.javax.sip.SipStackImpl; | |
| 29 | 26 | import gov.nist.javax.sip.message.SIPRequest; |
| 30 | 27 | import gov.nist.javax.sip.message.SIPResponse; |
| 31 | -import gov.nist.javax.sip.stack.SIPClientTransaction; | |
| 32 | -import gov.nist.javax.sip.stack.SIPClientTransactionImpl; | |
| 33 | -import gov.nist.javax.sip.stack.SIPDialog; | |
| 34 | 28 | import org.slf4j.Logger; |
| 35 | 29 | import org.slf4j.LoggerFactory; |
| 36 | 30 | import org.springframework.beans.factory.annotation.Autowired; |
| 37 | 31 | import org.springframework.beans.factory.annotation.Qualifier; |
| 38 | 32 | import org.springframework.context.annotation.DependsOn; |
| 39 | -import org.springframework.context.annotation.Lazy; | |
| 40 | 33 | import org.springframework.stereotype.Component; |
| 41 | 34 | import org.springframework.util.ObjectUtils; |
| 42 | 35 | |
| 43 | 36 | import javax.sip.*; |
| 44 | -import javax.sip.address.Address; | |
| 45 | -import javax.sip.address.SipURI; | |
| 46 | 37 | import javax.sip.header.*; |
| 47 | 38 | import javax.sip.message.Request; |
| 48 | -import javax.sip.message.Response; | |
| 49 | -import java.lang.reflect.Field; | |
| 50 | 39 | import java.text.ParseException; |
| 51 | -import java.util.ArrayList; | |
| 52 | -import java.util.HashSet; | |
| 53 | -import java.util.List; | |
| 54 | 40 | |
| 55 | 41 | /** |
| 56 | 42 | * @description:设备能力接口,用于定义设备的控制、查询能力 |
| ... | ... | @@ -98,6 +84,9 @@ public class SIPCommander implements ISIPCommander { |
| 98 | 84 | @Autowired |
| 99 | 85 | private IMediaServerService mediaServerService; |
| 100 | 86 | |
| 87 | + @Autowired | |
| 88 | + private ZLMRTPServerFactory zlmrtpServerFactory; | |
| 89 | + | |
| 101 | 90 | |
| 102 | 91 | /** |
| 103 | 92 | * 云台方向放控制,使用配置文件中的默认镜头移动速度 |
| ... | ... | @@ -591,11 +580,73 @@ public class SIPCommander implements ISIPCommander { |
| 591 | 580 | }); |
| 592 | 581 | } |
| 593 | 582 | |
| 583 | + @Override | |
| 584 | + public void talkStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String callId, ZlmHttpHookSubscribe.Event event, ZlmHttpHookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { | |
| 585 | + | |
| 586 | + String stream = ssrcInfo.getStream(); | |
| 587 | + | |
| 588 | + if (device == null) { | |
| 589 | + return; | |
| 590 | + } | |
| 591 | + if (!mediaServerItem.isRtpEnable()) { | |
| 592 | + // 单端口暂不支持语音对讲 | |
| 593 | + logger.info("[语音对讲] 单端口暂不支持此操作"); | |
| 594 | + return; | |
| 595 | + } | |
| 596 | + | |
| 597 | + logger.info("[语音对讲] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); | |
| 598 | + HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); | |
| 599 | + subscribe.addSubscribe(hookSubscribeForStreamChange, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { | |
| 600 | + if (event != null) { | |
| 601 | + event.response(mediaServerItemInUse, json); | |
| 602 | + subscribe.removeSubscribe(hookSubscribeForStreamChange); | |
| 603 | + } | |
| 604 | + }); | |
| 605 | + | |
| 606 | + CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId() | |
| 607 | + : udpSipProvider.getNewCallId(); | |
| 608 | + callIdHeader.setCallId(callId); | |
| 609 | + HookSubscribeForStreamPush hookSubscribeForStreamPush = HookSubscribeFactory.on_publish("rtp", stream, null, mediaServerItem.getId()); | |
| 610 | + subscribe.addSubscribe(hookSubscribeForStreamPush, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { | |
| 611 | + if (eventForPush != null) { | |
| 612 | + eventForPush.response(mediaServerItemInUse, json); | |
| 613 | + } | |
| 614 | + }); | |
| 615 | + // | |
| 616 | + StringBuffer content = new StringBuffer(200); | |
| 617 | + content.append("v=0\r\n"); | |
| 618 | + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); | |
| 619 | + content.append("s=Talk\r\n"); | |
| 620 | + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); | |
| 621 | + content.append("t=0 0\r\n"); | |
| 622 | + | |
| 623 | + content.append("m=audio " + ssrcInfo.getPort() + " RTP/AVP 8\r\n"); | |
| 624 | + content.append("a=sendrecv\r\n"); | |
| 625 | + content.append("a=rtpmap:8 PCMA/8000\r\n"); | |
| 626 | + | |
| 627 | + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc | |
| 628 | + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 | |
| 629 | + content.append("f=v/////a/1/8/1" + "\r\n"); | |
| 630 | + | |
| 631 | + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(), callIdHeader); | |
| 632 | + transmitRequest(device.getTransport(), request, (e -> { | |
| 633 | + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); | |
| 634 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); | |
| 635 | + errorEvent.response(e); | |
| 636 | + }), e -> { | |
| 637 | + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 | |
| 638 | + ResponseEvent responseEvent = (ResponseEvent) e.event; | |
| 639 | + SIPResponse response = (SIPResponse) responseEvent.getResponse(); | |
| 640 | + streamSession.put(device.getDeviceId(), channelId, "talk", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play); | |
| 641 | + okEvent.response(e); | |
| 642 | + }); | |
| 643 | + } | |
| 644 | + | |
| 594 | 645 | /** |
| 595 | 646 | * 视频流停止, 不使用回调 |
| 596 | 647 | */ |
| 597 | 648 | @Override |
| 598 | - public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException { | |
| 649 | + public synchronized void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException { | |
| 599 | 650 | streamByeCmd(device, channelId, stream, callId, null); |
| 600 | 651 | } |
| 601 | 652 | |
| ... | ... | @@ -603,7 +654,7 @@ public class SIPCommander implements ISIPCommander { |
| 603 | 654 | * 视频流停止 |
| 604 | 655 | */ |
| 605 | 656 | @Override |
| 606 | - public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { | |
| 657 | + public synchronized void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { | |
| 607 | 658 | SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callId, stream); |
| 608 | 659 | if (ssrcTransaction == null) { |
| 609 | 660 | throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); |
| ... | ... | @@ -617,67 +668,34 @@ public class SIPCommander implements ISIPCommander { |
| 617 | 668 | transmitRequest(device.getTransport(), byteRequest, null, okEvent); |
| 618 | 669 | } |
| 619 | 670 | |
| 620 | - /** | |
| 671 | + @Override | |
| 672 | + public synchronized void streamByeCmd(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { | |
| 673 | + Request byteRequest = headerProvider.createByteRequest(device, channelId, sipTransactionInfo); | |
| 674 | + transmitRequest(device.getTransport(), byteRequest, null, okEvent); | |
| 675 | + } | |
| 676 | + | |
| 677 | + /** | |
| 621 | 678 | * 语音广播 |
| 622 | 679 | * |
| 623 | 680 | * @param device 视频设备 |
| 624 | 681 | */ |
| 625 | - @Override | |
| 626 | - public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException { | |
| 627 | - | |
| 628 | - StringBuffer broadcastXml = new StringBuffer(200); | |
| 629 | - String charset = device.getCharset(); | |
| 630 | - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); | |
| 631 | - broadcastXml.append("<Notify>\r\n"); | |
| 632 | - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | |
| 633 | - broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n"); | |
| 634 | - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); | |
| 635 | - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | |
| 636 | - broadcastXml.append("</Notify>\r\n"); | |
| 637 | - /** | |
| 638 | - * 语音广播 | |
| 639 | - * | |
| 640 | - * @param device 视频设备 | |
| 641 | - */ | |
| 642 | 682 | @Override |
| 643 | - public boolean audioBroadcastCmd(Device device,String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { | |
| 644 | - try { | |
| 645 | - StringBuffer broadcastXml = new StringBuffer(200); | |
| 646 | - String charset = device.getCharset(); | |
| 647 | - broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); | |
| 648 | - broadcastXml.append("<Notify>\r\n"); | |
| 649 | - broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); | |
| 650 | - broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | |
| 651 | - broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); | |
| 652 | - broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n"); | |
| 653 | - broadcastXml.append("</Notify>\r\n"); | |
| 654 | - | |
| 655 | - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() | |
| 656 | - : udpSipProvider.getNewCallId(); | |
| 657 | - | |
| 658 | - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader); | |
| 659 | - transmitRequest(device.getTransport(), request); | |
| 660 | - | |
| 661 | - } | |
| 662 | - | |
| 663 | - @Override | |
| 664 | - public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { | |
| 665 | - | |
| 683 | + public void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { | |
| 666 | 684 | StringBuffer broadcastXml = new StringBuffer(200); |
| 667 | 685 | String charset = device.getCharset(); |
| 668 | 686 | broadcastXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n"); |
| 669 | 687 | broadcastXml.append("<Notify>\r\n"); |
| 670 | 688 | broadcastXml.append("<CmdType>Broadcast</CmdType>\r\n"); |
| 671 | - broadcastXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n"); | |
| 689 | + broadcastXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n"); | |
| 672 | 690 | broadcastXml.append("<SourceID>" + sipConfig.getId() + "</SourceID>\r\n"); |
| 673 | - broadcastXml.append("<TargetID>" + device.getDeviceId() + "</TargetID>\r\n"); | |
| 691 | + broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n"); | |
| 674 | 692 | broadcastXml.append("</Notify>\r\n"); |
| 675 | 693 | |
| 676 | 694 | CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() |
| 677 | 695 | : udpSipProvider.getNewCallId(); |
| 678 | 696 | |
| 679 | 697 | Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader); |
| 680 | - transmitRequest(device.getTransport(), request, errorEvent); | |
| 698 | + transmitRequest(device.getTransport(), request, errorEvent, okEvent); | |
| 681 | 699 | |
| 682 | 700 | } |
| 683 | 701 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
| ... | ... | @@ -676,7 +676,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { |
| 676 | 676 | } |
| 677 | 677 | |
| 678 | 678 | @Override |
| 679 | - public void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { | |
| 679 | + public synchronized void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { | |
| 680 | 680 | if (sendRtpItem == null ) { |
| 681 | 681 | logger.info("[向上级发送BYE], sendRtpItem 为NULL"); |
| 682 | 682 | return; | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.conf.SipConfig; | |
| 3 | 4 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 4 | 5 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| 5 | 6 | import gov.nist.javax.sip.SipProviderImpl; |
| 6 | 7 | import gov.nist.javax.sip.SipStackImpl; |
| 7 | 8 | import gov.nist.javax.sip.message.SIPRequest; |
| 8 | 9 | import gov.nist.javax.sip.message.SIPResponse; |
| 9 | -import gov.nist.javax.sip.stack.SIPServerTransaction; | |
| 10 | 10 | import gov.nist.javax.sip.stack.SIPServerTransactionImpl; |
| 11 | 11 | import org.apache.commons.lang3.ArrayUtils; |
| 12 | 12 | import org.dom4j.Document; |
| ... | ... | @@ -27,8 +27,6 @@ import javax.sip.message.MessageFactory; |
| 27 | 27 | import javax.sip.message.Request; |
| 28 | 28 | import javax.sip.message.Response; |
| 29 | 29 | import java.io.ByteArrayInputStream; |
| 30 | -import java.nio.ByteBuffer; | |
| 31 | -import java.nio.charset.StandardCharsets; | |
| 32 | 30 | import java.text.ParseException; |
| 33 | 31 | import java.util.ArrayList; |
| 34 | 32 | import java.util.Arrays; |
| ... | ... | @@ -51,6 +49,9 @@ public abstract class SIPRequestProcessorParent { |
| 51 | 49 | @Qualifier(value="udpSipProvider") |
| 52 | 50 | private SipProviderImpl udpSipProvider; |
| 53 | 51 | |
| 52 | + @Autowired | |
| 53 | + private SipConfig sipConfig; | |
| 54 | + | |
| 54 | 55 | /** |
| 55 | 56 | * 根据 RequestEvent 获取 ServerTransaction |
| 56 | 57 | * @param evt |
| ... | ... | @@ -60,13 +61,15 @@ public abstract class SIPRequestProcessorParent { |
| 60 | 61 | Request request = evt.getRequest(); |
| 61 | 62 | SIPServerTransactionImpl serverTransaction = (SIPServerTransactionImpl)evt.getServerTransaction(); |
| 62 | 63 | // 判断TCP还是UDP |
| 64 | + boolean isTcp = false; | |
| 63 | 65 | ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); |
| 64 | 66 | String transport = reqViaHeader.getTransport(); |
| 67 | + if (transport.equalsIgnoreCase("TCP")) { | |
| 68 | + isTcp = true; | |
| 69 | + } | |
| 65 | 70 | if (serverTransaction != null && serverTransaction.getOriginalRequest() == null) { |
| 66 | 71 | serverTransaction.setOriginalRequest((SIPRequest) evt.getRequest()); |
| 67 | 72 | } |
| 68 | - boolean isTcp = "TCP".equals(transport); | |
| 69 | - | |
| 70 | 73 | if (serverTransaction == null) { |
| 71 | 74 | try { |
| 72 | 75 | if (isTcp) { |
| ... | ... | @@ -187,7 +190,6 @@ public abstract class SIPRequestProcessorParent { |
| 187 | 190 | * 回复带sdp的200 |
| 188 | 191 | */ |
| 189 | 192 | public SIPResponse responseSdpAck(ServerTransaction serverTransaction, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException { |
| 190 | - | |
| 191 | 193 | ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
| 192 | 194 | |
| 193 | 195 | // 兼容国标中的使用编码@域名作为RequestURI的情况 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
| ... | ... | @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | 4 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 5 | +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | |
| 5 | 6 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 6 | 7 | import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; |
| 7 | 8 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| ... | ... | @@ -14,6 +15,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP |
| 14 | 15 | import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; |
| 15 | 16 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 16 | 17 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 18 | +import com.genersoft.iot.vmp.service.IDeviceService; | |
| 17 | 19 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 18 | 20 | import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; |
| 19 | 21 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; |
| ... | ... | @@ -21,7 +23,6 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 21 | 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 22 | 24 | import gov.nist.javax.sip.message.SIPRequest; |
| 23 | 25 | import gov.nist.javax.sip.stack.SIPDialog; |
| 24 | -import com.genersoft.iot.vmp.utils.SerializeUtils; | |
| 25 | 26 | import org.slf4j.Logger; |
| 26 | 27 | import org.slf4j.LoggerFactory; |
| 27 | 28 | import org.springframework.beans.factory.InitializingBean; |
| ... | ... | @@ -82,6 +83,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In |
| 82 | 83 | private ISIPCommander cmder; |
| 83 | 84 | |
| 84 | 85 | @Autowired |
| 86 | + private IDeviceService deviceService; | |
| 87 | + | |
| 88 | + @Autowired | |
| 85 | 89 | private ISIPCommanderForPlatform commanderForPlatform; |
| 86 | 90 | |
| 87 | 91 | @Autowired |
| ... | ... | @@ -106,7 +110,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In |
| 106 | 110 | // 取消设置的超时任务 |
| 107 | 111 | dynamicTask.stop(callIdHeader.getCallId()); |
| 108 | 112 | String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); |
| 109 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId()); | |
| 113 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); | |
| 110 | 114 | if (sendRtpItem == null) { |
| 111 | 115 | logger.warn("[收到ACK]:未找到通道({})的推流信息", channelId); |
| 112 | 116 | return; |
| ... | ... | @@ -123,7 +127,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In |
| 123 | 127 | param.put("pt", sendRtpItem.getPt()); |
| 124 | 128 | param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); |
| 125 | 129 | param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); |
| 126 | - if (!sendRtpItem.isTcp() && parentPlatform.isRtcp()) { | |
| 130 | + if (!sendRtpItem.isTcp() && parentPlatform != null && parentPlatform.isRtcp()) { | |
| 127 | 131 | // 开启rtcp保活 |
| 128 | 132 | param.put("udp_rtcp_timeout", "1"); |
| 129 | 133 | } |
| ... | ... | @@ -141,29 +145,28 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In |
| 141 | 145 | if (jsonObject == null) { |
| 142 | 146 | logger.error("RTP推流失败: 请检查ZLM服务"); |
| 143 | 147 | } else if (jsonObject.getInteger("code") == 0) { |
| 144 | - | |
| 145 | - if (sendRtpItem.isOnlyAudio()) { | |
| 146 | - AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | |
| 147 | - audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.Ok); | |
| 148 | - audioBroadcastCatch.setDialog((SIPDialog) evt.getDialog()); | |
| 149 | - audioBroadcastCatch.setRequest((SIPRequest) evt.getRequest()); | |
| 150 | - audioBroadcastManager.update(audioBroadcastCatch); | |
| 151 | - String waiteStreamTimeoutTaskKey = "waite-stream-" + audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(); | |
| 152 | - dynamicTask.stop(waiteStreamTimeoutTaskKey); | |
| 153 | - } | |
| 154 | 148 | logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, ", param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); |
| 155 | 149 | } else { |
| 156 | 150 | logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSONObject.toJSON(param)); |
| 157 | 151 | if (sendRtpItem.isOnlyAudio()) { |
| 158 | 152 | // 语音对讲 |
| 159 | - try { | |
| 160 | - cmder.streamByeCmd((SIPDialog) evt.getDialog(), sendRtpItem.getChannelId(), (SIPRequest) evt.getRequest(), null); | |
| 161 | - } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 162 | - logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); | |
| 153 | + Device device = deviceService.queryDevice(platformGbId); | |
| 154 | + if (device != null) { | |
| 155 | + try { | |
| 156 | + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), sendRtpItem.getStreamId(), null); | |
| 157 | + } catch (SipException | ParseException | InvalidArgumentException | | |
| 158 | + SsrcTransactionNotFoundException e) { | |
| 159 | + logger.error("[命令发送失败] 停止语音对讲: {}", e.getMessage()); | |
| 160 | + } | |
| 163 | 161 | } |
| 162 | + | |
| 164 | 163 | } else { |
| 165 | 164 | // 向上级平台 |
| 166 | - commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); | |
| 165 | + try { | |
| 166 | + commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); | |
| 167 | + } catch (SipException | InvalidArgumentException | ParseException e) { | |
| 168 | + logger.error("[命令发送失败] 国标级联, 回复BYE: {}", e.getMessage()); | |
| 169 | + } | |
| 167 | 170 | } |
| 168 | 171 | if (mediaInfo == null) { |
| 169 | 172 | RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( |
| ... | ... | @@ -179,7 +182,6 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In |
| 179 | 182 | } |
| 180 | 183 | |
| 181 | 184 | |
| 182 | - } | |
| 183 | 185 | } |
| 184 | 186 | } |
| 185 | 187 | private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
| ... | ... | @@ -2,10 +2,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 4 | 4 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| 5 | -import com.genersoft.iot.vmp.gb28181.bean.Device; | |
| 6 | -import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; | |
| 7 | -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; | |
| 8 | -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; | |
| 5 | +import com.genersoft.iot.vmp.gb28181.bean.*; | |
| 6 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | |
| 9 | 7 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 10 | 8 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 11 | 9 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| ... | ... | @@ -55,6 +53,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In |
| 55 | 53 | private IDeviceService deviceService; |
| 56 | 54 | |
| 57 | 55 | @Autowired |
| 56 | + private AudioBroadcastManager audioBroadcastManager; | |
| 57 | + | |
| 58 | + @Autowired | |
| 58 | 59 | private IVideoManagerStorage storager; |
| 59 | 60 | |
| 60 | 61 | @Autowired |
| ... | ... | @@ -91,78 +92,79 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In |
| 91 | 92 | logger.error("[回复BYE信息失败],{}", e.getMessage()); |
| 92 | 93 | } |
| 93 | 94 | CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); |
| 94 | - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | |
| 95 | - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | |
| 96 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId()); | |
| 97 | - logger.info("[收到bye] {}/{}", platformGbId, channelId); | |
| 98 | - if (sendRtpItem != null){ | |
| 99 | - String streamId = sendRtpItem.getStreamId(); | |
| 100 | - Map<String, Object> param = new HashMap<>(); | |
| 101 | - param.put("vhost","__defaultVhost__"); | |
| 102 | - param.put("app",sendRtpItem.getApp()); | |
| 103 | - param.put("stream",streamId); | |
| 104 | - param.put("ssrc",sendRtpItem.getSsrc()); | |
| 105 | - logger.info("[收到bye] 停止向上级推流:{}", streamId); | |
| 106 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 107 | - redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null); | |
| 108 | - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); | |
| 109 | - int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); | |
| 110 | - if (totalReaderCount <= 0) { | |
| 111 | - logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); | |
| 112 | - if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { | |
| 113 | - Device device = deviceService.queryDevice(sendRtpItem.getDeviceId()); | |
| 114 | - if (device == null) { | |
| 115 | - logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); | |
| 116 | - } | |
| 117 | - try { | |
| 118 | - logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId); | |
| 119 | - cmder.streamByeCmd(device, channelId, streamId, null); | |
| 120 | - } catch (InvalidArgumentException | ParseException | SipException | | |
| 121 | - SsrcTransactionNotFoundException e) { | |
| 122 | - logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); | |
| 123 | - } | |
| 95 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); | |
| 96 | + | |
| 97 | + if (sendRtpItem != null){ | |
| 98 | + logger.info("[收到bye] {}/{}", sendRtpItem.getPlatformId(), sendRtpItem.getChannelId()); | |
| 99 | + String streamId = sendRtpItem.getStreamId(); | |
| 100 | + Map<String, Object> param = new HashMap<>(); | |
| 101 | + param.put("vhost","__defaultVhost__"); | |
| 102 | + param.put("app",sendRtpItem.getApp()); | |
| 103 | + param.put("stream",streamId); | |
| 104 | + param.put("ssrc",sendRtpItem.getSsrc()); | |
| 105 | + logger.info("[收到bye] 停止向上级推流:{}", streamId); | |
| 106 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 107 | + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(), sendRtpItem.getChannelId(), callIdHeader.getCallId(), null); | |
| 108 | + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); | |
| 109 | + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); | |
| 110 | + if (totalReaderCount <= 0) { | |
| 111 | + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); | |
| 112 | + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { | |
| 113 | + Device device = deviceService.queryDevice(sendRtpItem.getDeviceId()); | |
| 114 | + if (device == null) { | |
| 115 | + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); | |
| 124 | 116 | } |
| 125 | - if (sendRtpItem.isOnlyAudio()) { | |
| 126 | - playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | |
| 117 | + try { | |
| 118 | + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | |
| 119 | + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), streamId, null); | |
| 120 | + } catch (InvalidArgumentException | ParseException | SipException | | |
| 121 | + SsrcTransactionNotFoundException e) { | |
| 122 | + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); | |
| 127 | 123 | } |
| 128 | - if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { | |
| 129 | - MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | |
| 130 | - sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | |
| 131 | - sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId()); | |
| 132 | - redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); | |
| 133 | - } | |
| 134 | - } | |
| 135 | - } | |
| 136 | - // 可能是设备主动停止 | |
| 137 | - Device device = storager.queryVideoDeviceByChannelId(platformGbId); | |
| 138 | - if (device != null) { | |
| 139 | - storager.stopPlay(device.getDeviceId(), channelId); | |
| 140 | - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId); | |
| 141 | - if (streamInfo != null) { | |
| 142 | - redisCatchStorage.stopPlay(streamInfo); | |
| 143 | - mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream()); | |
| 144 | 124 | } |
| 145 | - SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); | |
| 146 | - if (ssrcTransactionForPlay != null){ | |
| 147 | - if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ | |
| 148 | - // 释放ssrc | |
| 149 | - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); | |
| 150 | - if (mediaServerItem != null) { | |
| 151 | - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc()); | |
| 152 | - } | |
| 153 | - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); | |
| 154 | - } | |
| 125 | + | |
| 126 | + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { | |
| 127 | + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, | |
| 128 | + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), | |
| 129 | + sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId()); | |
| 130 | + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); | |
| 155 | 131 | } |
| 156 | - SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null); | |
| 157 | - if (ssrcTransactionForPlayBack != null) { | |
| 132 | + } | |
| 133 | + playService.stopAudioBroadcast(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | |
| 134 | + } | |
| 135 | + | |
| 136 | + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); | |
| 137 | + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); | |
| 138 | + | |
| 139 | + // 可能是设备主动停止 | |
| 140 | + Device device = storager.queryVideoDeviceByChannelId(platformGbId); | |
| 141 | + if (device != null) { | |
| 142 | + storager.stopPlay(device.getDeviceId(), channelId); | |
| 143 | + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId); | |
| 144 | + if (streamInfo != null) { | |
| 145 | + redisCatchStorage.stopPlay(streamInfo); | |
| 146 | + mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream()); | |
| 147 | + } | |
| 148 | + SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); | |
| 149 | + if (ssrcTransactionForPlay != null){ | |
| 150 | + if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ | |
| 158 | 151 | // 释放ssrc |
| 159 | - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId()); | |
| 152 | + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); | |
| 160 | 153 | if (mediaServerItem != null) { |
| 161 | - mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc()); | |
| 154 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc()); | |
| 162 | 155 | } |
| 163 | - streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream()); | |
| 156 | + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); | |
| 164 | 157 | } |
| 165 | 158 | } |
| 166 | - | |
| 159 | + SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null); | |
| 160 | + if (ssrcTransactionForPlayBack != null) { | |
| 161 | + // 释放ssrc | |
| 162 | + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId()); | |
| 163 | + if (mediaServerItem != null) { | |
| 164 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc()); | |
| 165 | + } | |
| 166 | + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream()); | |
| 167 | + } | |
| 168 | + } | |
| 167 | 169 | } |
| 168 | 170 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | |
| 3 | -import com.alibaba.fastjson.JSON; | |
| 4 | -import com.alibaba.fastjson.JSONArray; | |
| 5 | 3 | import com.alibaba.fastjson.JSONObject; |
| 6 | 4 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 7 | 5 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 8 | 6 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 7 | +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; | |
| 9 | 8 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 10 | 9 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 11 | 10 | import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; |
| ... | ... | @@ -14,7 +13,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 14 | 13 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 15 | 14 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 16 | 15 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; |
| 17 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | |
| 16 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | |
| 17 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | |
| 18 | 18 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| 19 | 19 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 20 | 20 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| ... | ... | @@ -30,20 +30,16 @@ import com.genersoft.iot.vmp.service.IStreamProxyService; |
| 30 | 30 | import com.genersoft.iot.vmp.service.IStreamPushService; |
| 31 | 31 | import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; |
| 32 | 32 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 33 | -import com.genersoft.iot.vmp.service.impl.RedisGbPlayMsgListener; | |
| 34 | 33 | import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; |
| 35 | 34 | import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; |
| 36 | 35 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 37 | 36 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 38 | 37 | import com.genersoft.iot.vmp.utils.DateUtil; |
| 39 | -import com.genersoft.iot.vmp.utils.SerializeUtils; | |
| 40 | 38 | import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; |
| 41 | 39 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 42 | 40 | import gov.nist.javax.sdp.TimeDescriptionImpl; |
| 43 | 41 | import gov.nist.javax.sdp.fields.TimeField; |
| 44 | 42 | import gov.nist.javax.sip.message.SIPRequest; |
| 45 | -import gov.nist.javax.sip.stack.SIPDialog; | |
| 46 | -import gov.nist.javax.sip.message.SIPRequest; | |
| 47 | 43 | import gov.nist.javax.sip.message.SIPResponse; |
| 48 | 44 | import org.slf4j.Logger; |
| 49 | 45 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -72,7 +68,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 72 | 68 | private final String method = "INVITE"; |
| 73 | 69 | |
| 74 | 70 | @Autowired |
| 75 | - private SIPCommanderFroPlatform cmderFroPlatform; | |
| 71 | + private ISIPCommanderForPlatform cmderFroPlatform; | |
| 76 | 72 | |
| 77 | 73 | @Autowired |
| 78 | 74 | private IVideoManagerStorage storager; |
| ... | ... | @@ -174,7 +170,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 174 | 170 | // 查询请求是否来自上级平台\设备 |
| 175 | 171 | ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); |
| 176 | 172 | if (platform == null) { |
| 177 | - inviteFromDeviceHandle(serverTransaction, requesterId); | |
| 173 | + inviteFromDeviceHandle(serverTransaction, requesterId, channelId); | |
| 178 | 174 | } else { |
| 179 | 175 | // 查询平台下是否有该通道 |
| 180 | 176 | DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); |
| ... | ... | @@ -393,14 +389,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 393 | 389 | }; |
| 394 | 390 | SipSubscribe.Event errorEvent = ((event) -> { |
| 395 | 391 | // 未知错误。直接转发设备点播的错误 |
| 396 | - Response response = null; | |
| 397 | 392 | try { |
| 398 | - response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); | |
| 393 | + Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); | |
| 399 | 394 | serverTransaction.sendResponse(response); |
| 400 | 395 | System.out.println("未知错误。直接转发设备点播的错误"); |
| 401 | 396 | if (serverTransaction.getDialog() != null) { |
| 402 | 397 | serverTransaction.getDialog().delete(); |
| 403 | 398 | } |
| 399 | + serverTransaction.getDialog().delete(); | |
| 400 | + | |
| 404 | 401 | } catch (ParseException | SipException | InvalidArgumentException e) { |
| 405 | 402 | e.printStackTrace(); |
| 406 | 403 | } |
| ... | ... | @@ -817,7 +814,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 817 | 814 | } |
| 818 | 815 | |
| 819 | 816 | public void inviteFromDeviceHandle(ServerTransaction serverTransaction, String requesterId, String channelId) throws InvalidArgumentException, ParseException, SipException, SdpException { |
| 820 | - | |
| 821 | 817 | // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) |
| 822 | 818 | Device device = redisCatchStorage.getDevice(requesterId); |
| 823 | 819 | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId); |
| ... | ... | @@ -918,125 +914,64 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 918 | 914 | sendRtpItem.setOnlyAudio(true); |
| 919 | 915 | redisCatchStorage.updateSendRTPSever(sendRtpItem); |
| 920 | 916 | |
| 921 | - // hook监听等待设备推流上来 | |
| 922 | - // 添加订阅 | |
| 923 | - HookSubscribeForStreamChange subscribeKey = HookSubscribeFactory.on_stream_changed(app, stream, true, "rtsp", mediaServerItem.getId()); | |
| 924 | - | |
| 925 | - String finalSsrc = ssrc; | |
| 926 | - // 流已经存在时直接推流 | |
| 927 | - // 设置等待推流的超时; 默认20s | |
| 928 | - String waiteStreamTimeoutTaskKey = "waite-stream-" + device.getDeviceId() + audioBroadcastCatch.getChannelId(); | |
| 929 | - dynamicTask.startDelay(waiteStreamTimeoutTaskKey, ()->{ | |
| 930 | - logger.info("等待推流超时: {}/{}", app, stream); | |
| 931 | - subscribe.removeSubscribe(subscribeKey); | |
| 932 | - playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 933 | - // 发送bye | |
| 934 | - try { | |
| 935 | - responseAck(evt, Response.BUSY_HERE); | |
| 936 | - } catch (SipException e) { | |
| 937 | - throw new RuntimeException(e); | |
| 938 | - } catch (InvalidArgumentException e) { | |
| 939 | - throw new RuntimeException(e); | |
| 940 | - } catch (ParseException e) { | |
| 941 | - throw new RuntimeException(e); | |
| 942 | - } | |
| 943 | - }, 20*1000); | |
| 944 | - | |
| 945 | - boolean finalMediaTransmissionTCP = mediaTransmissionTCP; | |
| 946 | - subscribe.addSubscribe(subscribeKey, | |
| 947 | - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ | |
| 948 | - logger.info("收到语音对讲推流"); | |
| 949 | - dynamicTask.stop(waiteStreamTimeoutTaskKey); | |
| 950 | - MediaItem mediaItem = JSON.toJavaObject(json, MediaItem.class); | |
| 951 | - Integer audioCodecId = null; | |
| 952 | - if (mediaItem.getTracks() != null && mediaItem.getTracks().size() > 0) { | |
| 953 | - for (int i = 0; i < mediaItem.getTracks().size(); i++) { | |
| 954 | - MediaItem.MediaTrack mediaTrack = mediaItem.getTracks().get(i); | |
| 955 | - if (mediaTrack.getCodecType() == 1) { | |
| 956 | - audioCodecId = mediaTrack.getCodecId(); | |
| 957 | - break; | |
| 958 | - } | |
| 959 | - } | |
| 960 | - } | |
| 961 | - | |
| 962 | - try { | |
| 963 | - sendRtpItem.setStatus(2); | |
| 964 | - redisCatchStorage.updateSendRTPSever(sendRtpItem); | |
| 965 | - StringBuffer content = new StringBuffer(200); | |
| 966 | - content.append("v=0\r\n"); | |
| 967 | - content.append("o="+ config.getId() +" "+ sdp.getOrigin().getSessionId() +" " + sdp.getOrigin().getSessionVersion() + " IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | |
| 968 | - content.append("s=Play\r\n"); | |
| 969 | - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | |
| 970 | - content.append("t=0 0\r\n"); | |
| 971 | - if (audioCodecId == null) { | |
| 972 | - if (finalMediaTransmissionTCP) { | |
| 973 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" TCP/RTP/AVP 8\r\n"); | |
| 974 | - }else { | |
| 975 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n"); | |
| 976 | - } | |
| 977 | - | |
| 978 | - content.append("a=rtpmap:8 PCMA/8000\r\n"); | |
| 979 | - }else { | |
| 980 | - if (audioCodecId == 4) { | |
| 981 | - if (finalMediaTransmissionTCP) { | |
| 982 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" TCP/RTP/AVP 0\r\n"); | |
| 983 | - }else { | |
| 984 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 0\r\n"); | |
| 985 | - } | |
| 986 | - content.append("a=rtpmap:0 PCMU/8000\r\n"); | |
| 987 | - }else { | |
| 988 | - if (finalMediaTransmissionTCP) { | |
| 989 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" TCP/RTP/AVP 8\r\n"); | |
| 990 | - }else { | |
| 991 | - content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n"); | |
| 992 | - } | |
| 993 | - content.append("a=rtpmap:8 PCMA/8000\r\n"); | |
| 994 | - } | |
| 995 | - } | |
| 996 | - content.append("a=sendonly\r\n"); | |
| 997 | - if (sendRtpItem.isTcp()) { | |
| 998 | - content.append("a=connection:new\r\n"); | |
| 999 | - if (!sendRtpItem.isTcpActive()) { | |
| 1000 | - content.append("a=setup:active\r\n"); | |
| 1001 | - }else { | |
| 1002 | - content.append("a=setup:passive\r\n"); | |
| 1003 | - } | |
| 1004 | - } | |
| 1005 | - content.append("y="+ finalSsrc + "\r\n"); | |
| 1006 | - content.append("f=v/////a/1/8/1\r\n"); | |
| 1007 | - | |
| 1008 | - ParentPlatform parentPlatform = new ParentPlatform(); | |
| 1009 | - parentPlatform.setServerIP(device.getIp()); | |
| 1010 | - parentPlatform.setServerPort(device.getPort()); | |
| 1011 | - parentPlatform.setServerGBId(device.getDeviceId()); | |
| 1012 | - | |
| 1013 | - responseSdpAck(serverTransaction, content.toString(), parentPlatform); | |
| 1014 | - Dialog dialog = evt.getDialog(); | |
| 1015 | - audioBroadcastCatch.setDialog((SIPDialog) dialog); | |
| 1016 | - audioBroadcastCatch.setRequest((SIPRequest) request); | |
| 1017 | - audioBroadcastManager.update(audioBroadcastCatch); | |
| 1018 | - } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) { | |
| 1019 | - logger.error("[命令发送失败] 语音对讲: {}", e.getMessage()); | |
| 1020 | - } | |
| 1021 | - }); | |
| 1022 | -// } | |
| 1023 | - String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId(); | |
| 1024 | - WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | |
| 1025 | - wvpResult.setCode(0); | |
| 1026 | - wvpResult.setMsg("success"); | |
| 1027 | - AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); | |
| 1028 | - audioBroadcastResult.setApp(app); | |
| 1029 | - audioBroadcastResult.setStream(stream); | |
| 1030 | - audioBroadcastResult.setStreamInfo(mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null,false)); | |
| 1031 | - audioBroadcastResult.setCodec("G.711"); | |
| 1032 | - wvpResult.setData(audioBroadcastResult); | |
| 1033 | - RequestMessage requestMessage = new RequestMessage(); | |
| 1034 | - requestMessage.setKey(key); | |
| 1035 | - requestMessage.setData(wvpResult); | |
| 1036 | - resultHolder.invokeAllResult(requestMessage); | |
| 917 | + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream); | |
| 918 | + if (streamReady) { | |
| 919 | + sendOk(device, sendRtpItem, sdp, serverTransaction, mediaServerItem, mediaTransmissionTCP, ssrc); | |
| 920 | + }else { | |
| 921 | + logger.warn("[语音通话], 未发现待推送的流,app={},stream={}", app, stream); | |
| 922 | + playService.stopAudioBroadcast(device.getDeviceId(), audioBroadcastCatch.getChannelId()); | |
| 923 | + } | |
| 1037 | 924 | } else { |
| 1038 | 925 | logger.warn("来自无效设备/平台的请求"); |
| 1039 | 926 | responseAck(serverTransaction, Response.BAD_REQUEST); |
| 1040 | 927 | } |
| 1041 | 928 | } |
| 929 | + | |
| 930 | + void sendOk(Device device, SendRtpItem sendRtpItem, SessionDescription sdp, ServerTransaction serverTransaction, MediaServerItem mediaServerItem, boolean mediaTransmissionTCP, String ssrc){ | |
| 931 | + try { | |
| 932 | + sendRtpItem.setStatus(2); | |
| 933 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | |
| 934 | + StringBuffer content = new StringBuffer(200); | |
| 935 | + content.append("v=0\r\n"); | |
| 936 | + content.append("o="+ config.getId() +" "+ sdp.getOrigin().getSessionId() +" " + sdp.getOrigin().getSessionVersion() + " IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | |
| 937 | + content.append("s=Play\r\n"); | |
| 938 | + content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); | |
| 939 | + content.append("t=0 0\r\n"); | |
| 940 | + | |
| 941 | + if (mediaTransmissionTCP) { | |
| 942 | + content.append("m=audio "+ sendRtpItem.getLocalPort()+" TCP/RTP/AVP 8\r\n"); | |
| 943 | + }else { | |
| 944 | + content.append("m=audio "+ sendRtpItem.getLocalPort()+" RTP/AVP 8\r\n"); | |
| 945 | + } | |
| 946 | + | |
| 947 | + content.append("a=rtpmap:8 PCMA/8000/1\r\n"); | |
| 948 | + | |
| 949 | + content.append("a=sendonly\r\n"); | |
| 950 | + if (sendRtpItem.isTcp()) { | |
| 951 | + content.append("a=connection:new\r\n"); | |
| 952 | + if (!sendRtpItem.isTcpActive()) { | |
| 953 | + content.append("a=setup:active\r\n"); | |
| 954 | + }else { | |
| 955 | + content.append("a=setup:passive\r\n"); | |
| 956 | + } | |
| 957 | + } | |
| 958 | + content.append("y="+ ssrc + "\r\n"); | |
| 959 | + content.append("f=v/////a/1/8/1\r\n"); | |
| 960 | + | |
| 961 | + ParentPlatform parentPlatform = new ParentPlatform(); | |
| 962 | + parentPlatform.setServerIP(device.getIp()); | |
| 963 | + parentPlatform.setServerPort(device.getPort()); | |
| 964 | + parentPlatform.setServerGBId(device.getDeviceId()); | |
| 965 | + | |
| 966 | + SIPResponse sipResponse = responseSdpAck(serverTransaction, content.toString(), parentPlatform); | |
| 967 | + | |
| 968 | + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), sendRtpItem.getChannelId()); | |
| 969 | + | |
| 970 | + audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.Ok); | |
| 971 | + audioBroadcastCatch.setSipTransactionInfoByRequset(sipResponse); | |
| 972 | + audioBroadcastManager.update(audioBroadcastCatch); | |
| 973 | + } catch (SipException | InvalidArgumentException | ParseException | SdpParseException e) { | |
| 974 | + logger.error("[命令发送失败] 语音对讲 回复200OK(SDP): {}", e.getMessage()); | |
| 975 | + } | |
| 976 | + } | |
| 1042 | 977 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
| ... | ... | @@ -20,12 +20,8 @@ import org.springframework.beans.factory.InitializingBean; |
| 20 | 20 | import org.springframework.beans.factory.annotation.Autowired; |
| 21 | 21 | import org.springframework.stereotype.Component; |
| 22 | 22 | import org.springframework.util.ObjectUtils; |
| 23 | -import org.springframework.util.StringUtils; | |
| 24 | 23 | |
| 25 | -import javax.sip.InvalidArgumentException; | |
| 26 | -import javax.sip.RequestEvent; | |
| 27 | -import javax.sip.ServerTransaction; | |
| 28 | -import javax.sip.SipException; | |
| 24 | +import javax.sip.*; | |
| 29 | 25 | import javax.sip.header.*; |
| 30 | 26 | import javax.sip.message.Request; |
| 31 | 27 | import javax.sip.message.Response; |
| ... | ... | @@ -116,10 +112,12 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen |
| 116 | 112 | |
| 117 | 113 | if (expiresHeader == null) { |
| 118 | 114 | response = getMessageFactory().createResponse(Response.BAD_REQUEST, request); |
| 119 | - ServerTransaction serverTransaction = getServerTransaction(evt); | |
| 120 | - serverTransaction.sendResponse(response); | |
| 121 | - if (serverTransaction.getDialog() != null) { | |
| 122 | - serverTransaction.getDialog().delete(); | |
| 115 | + if (evt.getDialog() != null ) { | |
| 116 | + if (evt.getDialog().isServer()) { | |
| 117 | + ServerTransaction serverTransaction = getServerTransaction(evt); | |
| 118 | + serverTransaction.sendResponse(response); | |
| 119 | + serverTransaction.getDialog().delete(); | |
| 120 | + } | |
| 123 | 121 | } |
| 124 | 122 | return; |
| 125 | 123 | } |
| ... | ... | @@ -176,19 +174,13 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen |
| 176 | 174 | } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { |
| 177 | 175 | e.printStackTrace(); |
| 178 | 176 | } |
| 179 | - | |
| 180 | 177 | } |
| 181 | 178 | |
| 182 | 179 | private void sendResponse(RequestEvent evt, Response response) throws InvalidArgumentException, SipException { |
| 183 | 180 | ServerTransaction serverTransaction = getServerTransaction(evt); |
| 184 | - if (serverTransaction == null) { | |
| 185 | - logger.warn("[回复失败]:{}", response); | |
| 186 | - return; | |
| 187 | - } | |
| 188 | 181 | serverTransaction.sendResponse(response); |
| 189 | 182 | if (serverTransaction.getDialog() != null) { |
| 190 | 183 | serverTransaction.getDialog().delete(); |
| 191 | 184 | } |
| 192 | 185 | } |
| 193 | - | |
| 194 | 186 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; |
| 2 | 2 | |
| 3 | -import com.genersoft.iot.vmp.common.VideoManagerConstants; | |
| 4 | 3 | import com.genersoft.iot.vmp.conf.DynamicTask; |
| 5 | 4 | import com.genersoft.iot.vmp.conf.UserSetting; |
| 6 | 5 | import com.genersoft.iot.vmp.gb28181.bean.CmdType; |
| 7 | 6 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 8 | 7 | import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; |
| 9 | 8 | import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; |
| 10 | -import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask; | |
| 11 | 9 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 12 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | |
| 13 | 10 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; |
| 14 | 11 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 15 | 12 | import com.genersoft.iot.vmp.gb28181.utils.SipUtils; |
| 16 | 13 | import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; |
| 17 | -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 18 | 14 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 19 | 15 | import gov.nist.javax.sip.SipProviderImpl; |
| 20 | 16 | import gov.nist.javax.sip.message.SIPRequest; | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageHandlerAbstract.java
| ... | ... | @@ -3,11 +3,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message; |
| 3 | 3 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 4 | 4 | import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; |
| 5 | 5 | import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; |
| 6 | +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.AckRequestProcessor; | |
| 6 | 7 | import org.dom4j.Element; |
| 8 | +import org.slf4j.Logger; | |
| 9 | +import org.slf4j.LoggerFactory; | |
| 7 | 10 | import org.springframework.beans.factory.annotation.Autowired; |
| 8 | 11 | |
| 9 | 12 | import javax.sip.InvalidArgumentException; |
| 10 | 13 | import javax.sip.RequestEvent; |
| 14 | +import javax.sip.ServerTransaction; | |
| 11 | 15 | import javax.sip.SipException; |
| 12 | 16 | import javax.sip.message.Response; |
| 13 | 17 | import java.text.ParseException; |
| ... | ... | @@ -18,6 +22,8 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; |
| 18 | 22 | |
| 19 | 23 | public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{ |
| 20 | 24 | |
| 25 | + private Logger logger = LoggerFactory.getLogger(MessageHandlerAbstract.class); | |
| 26 | + | |
| 21 | 27 | public Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>(); |
| 22 | 28 | |
| 23 | 29 | public void addHandler(String cmdType, IMessageHandler messageHandler) { |
| ... | ... | @@ -48,14 +54,10 @@ public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent i |
| 48 | 54 | |
| 49 | 55 | public void handNullCmd(RequestEvent evt){ |
| 50 | 56 | try { |
| 51 | - responseAck(evt, Response.OK); | |
| 52 | - } catch (SipException e) { | |
| 53 | - throw new RuntimeException(e); | |
| 54 | - } catch (InvalidArgumentException e) { | |
| 55 | - throw new RuntimeException(e); | |
| 56 | - } catch (ParseException e) { | |
| 57 | - throw new RuntimeException(e); | |
| 57 | + ServerTransaction serverTransaction = getServerTransaction(evt); | |
| 58 | + responseAck(serverTransaction, Response.OK); | |
| 59 | + } catch (SipException | InvalidArgumentException | ParseException e) { | |
| 60 | + logger.error("[命令发送失败] 回复200 OK: {}", e.getMessage()); | |
| 58 | 61 | } |
| 59 | - return; | |
| 60 | 62 | } |
| 61 | 63 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
| ... | ... | @@ -52,16 +52,17 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i |
| 52 | 52 | public void handForDevice(RequestEvent evt, Device device, Element rootElement) { |
| 53 | 53 | try { |
| 54 | 54 | String channelId = getText(rootElement, "DeviceID"); |
| 55 | + ServerTransaction serverTransaction = getServerTransaction(evt); | |
| 55 | 56 | if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) { |
| 56 | 57 | // 回复410 |
| 57 | - responseAck(evt, Response.GONE); | |
| 58 | + responseAck(serverTransaction, Response.GONE); | |
| 58 | 59 | return; |
| 59 | 60 | } |
| 60 | 61 | logger.info("收到语音广播的回复:{}/{}", device.getDeviceId(), channelId ); |
| 61 | 62 | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId); |
| 62 | 63 | audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite); |
| 63 | 64 | audioBroadcastManager.update(audioBroadcastCatch); |
| 64 | - responseAck(evt, Response.OK); | |
| 65 | + responseAck(serverTransaction, Response.OK); | |
| 65 | 66 | } catch (ParseException | SipException | InvalidArgumentException e) { |
| 66 | 67 | logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); |
| 67 | 68 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| ... | ... | @@ -11,12 +11,16 @@ import com.genersoft.iot.vmp.conf.UserSetting; |
| 11 | 11 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| 12 | 12 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 13 | 13 | import com.genersoft.iot.vmp.gb28181.event.EventPublisher; |
| 14 | +import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; | |
| 14 | 15 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 15 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | |
| 16 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | |
| 17 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | |
| 16 | 18 | import com.genersoft.iot.vmp.media.zlm.dto.*; |
| 17 | 19 | import com.genersoft.iot.vmp.service.*; |
| 18 | 20 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 19 | 21 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 22 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | |
| 23 | +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | |
| 20 | 24 | import org.slf4j.Logger; |
| 21 | 25 | import org.slf4j.LoggerFactory; |
| 22 | 26 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -51,7 +55,13 @@ public class ZLMHttpHookListener { |
| 51 | 55 | private SIPCommander cmder; |
| 52 | 56 | |
| 53 | 57 | @Autowired |
| 54 | - private SIPCommanderFroPlatform commanderFroPlatform; | |
| 58 | + private ISIPCommanderForPlatform commanderFroPlatform; | |
| 59 | + | |
| 60 | + @Autowired | |
| 61 | + private AudioBroadcastManager audioBroadcastManager; | |
| 62 | + | |
| 63 | + @Autowired | |
| 64 | + private ZLMRTPServerFactory zlmrtpServerFactory; | |
| 55 | 65 | |
| 56 | 66 | @Autowired |
| 57 | 67 | private IPlayService playService; |
| ... | ... | @@ -466,7 +476,127 @@ public class ZLMHttpHookListener { |
| 466 | 476 | streamInfo.getStream(), null); |
| 467 | 477 | } |
| 468 | 478 | } |
| 469 | - }else { | |
| 479 | + }else if ("broadcast".equals(app)){ | |
| 480 | + // 语音对讲推流 stream需要满足格式deviceId_channelId | |
| 481 | + if (regist && stream.indexOf("_") > 0) { | |
| 482 | + String[] streamArray = stream.split("_"); | |
| 483 | + if (streamArray.length == 2) { | |
| 484 | + String deviceId = streamArray[0]; | |
| 485 | + String channelId = streamArray[1]; | |
| 486 | + Device device = deviceService.queryDevice(deviceId); | |
| 487 | + if (device != null) { | |
| 488 | + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 489 | + if (deviceChannel != null) { | |
| 490 | + if (audioBroadcastManager.exit(deviceId, channelId)) { | |
| 491 | + // 直接推流 | |
| 492 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, stream, null); | |
| 493 | + if (sendRtpItem == null) { | |
| 494 | + // TODO 可能数据错误,重新开启语音通道 | |
| 495 | + }else { | |
| 496 | + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; | |
| 497 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 498 | + logger.info("rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); | |
| 499 | + Map<String, Object> param = new HashMap<>(12); | |
| 500 | + param.put("vhost","__defaultVhost__"); | |
| 501 | + param.put("app",sendRtpItem.getApp()); | |
| 502 | + param.put("stream",sendRtpItem.getStreamId()); | |
| 503 | + param.put("ssrc", sendRtpItem.getSsrc()); | |
| 504 | + param.put("src_port", sendRtpItem.getLocalPort()); | |
| 505 | + param.put("pt", sendRtpItem.getPt()); | |
| 506 | + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); | |
| 507 | + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); | |
| 508 | + | |
| 509 | + JSONObject jsonObject; | |
| 510 | + if (sendRtpItem.isTcpActive()) { | |
| 511 | + jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param); | |
| 512 | + } else { | |
| 513 | + param.put("is_udp", is_Udp); | |
| 514 | + param.put("dst_url", sendRtpItem.getIp()); | |
| 515 | + param.put("dst_port", sendRtpItem.getPort()); | |
| 516 | + jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | |
| 517 | + } | |
| 518 | + if (jsonObject != null && jsonObject.getInteger("code") == 0) { | |
| 519 | + logger.info("[语音对讲] 自动推流成功, device: {}, channel: {}", deviceId, channelId); | |
| 520 | + } | |
| 521 | + | |
| 522 | + } | |
| 523 | + }else { | |
| 524 | + // 开启语音对讲通道 | |
| 525 | + try { | |
| 526 | + playService.audioBroadcastCmd(device, channelId, 60, (msg)->{ | |
| 527 | + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); | |
| 528 | + }); | |
| 529 | + } catch (InvalidArgumentException | ParseException | SipException e) { | |
| 530 | + logger.error("[命令发送失败] 语音对讲: {}", e.getMessage()); | |
| 531 | + } | |
| 532 | + } | |
| 533 | + | |
| 534 | + } | |
| 535 | + } | |
| 536 | + } | |
| 537 | + } | |
| 538 | + | |
| 539 | + }else if ("talk".equals(app)){ | |
| 540 | + // 语音对讲推流 stream需要满足格式deviceId_channelId | |
| 541 | + if (regist && stream.indexOf("_") > 0) { | |
| 542 | + String[] streamArray = stream.split("_"); | |
| 543 | + if (streamArray.length == 2) { | |
| 544 | + String deviceId = streamArray[0]; | |
| 545 | + String channelId = streamArray[1]; | |
| 546 | + Device device = deviceService.queryDevice(deviceId); | |
| 547 | + if (device != null) { | |
| 548 | + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 549 | + if (deviceChannel != null) { | |
| 550 | + if (audioBroadcastManager.exit(deviceId, channelId)) { | |
| 551 | + // 直接推流 | |
| 552 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, stream, null); | |
| 553 | + if (sendRtpItem == null) { | |
| 554 | + // TODO 可能数据错误,重新开启语音通道 | |
| 555 | + }else { | |
| 556 | + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; | |
| 557 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 558 | + logger.info("rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); | |
| 559 | + Map<String, Object> param = new HashMap<>(12); | |
| 560 | + param.put("vhost","__defaultVhost__"); | |
| 561 | + param.put("app",sendRtpItem.getApp()); | |
| 562 | + param.put("stream",sendRtpItem.getStreamId()); | |
| 563 | + param.put("ssrc", sendRtpItem.getSsrc()); | |
| 564 | + param.put("src_port", sendRtpItem.getLocalPort()); | |
| 565 | + param.put("pt", sendRtpItem.getPt()); | |
| 566 | + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); | |
| 567 | + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); | |
| 568 | + | |
| 569 | + JSONObject jsonObject; | |
| 570 | + if (sendRtpItem.isTcpActive()) { | |
| 571 | + jsonObject = zlmrtpServerFactory.startSendRtpPassive(mediaInfo, param); | |
| 572 | + } else { | |
| 573 | + param.put("is_udp", is_Udp); | |
| 574 | + param.put("dst_url", sendRtpItem.getIp()); | |
| 575 | + param.put("dst_port", sendRtpItem.getPort()); | |
| 576 | + jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); | |
| 577 | + } | |
| 578 | + if (jsonObject != null && jsonObject.getInteger("code") == 0) { | |
| 579 | + logger.info("[语音对讲] 自动推流成功, device: {}, channel: {}", deviceId, channelId); | |
| 580 | + } | |
| 581 | + } | |
| 582 | + }else { | |
| 583 | + // 开启语音对讲通道 | |
| 584 | + MediaServerItem mediaServerForMinimumLoad = mediaServerService.getMediaServerForMinimumLoad(); | |
| 585 | + playService.talk(mediaServerForMinimumLoad, device, channelId, (mediaServerItem, jsonObject)->{ | |
| 586 | + System.out.println("开始推流"); | |
| 587 | + }, eventResult -> { | |
| 588 | + System.out.println(eventResult.msg); | |
| 589 | + }, ()->{ | |
| 590 | + System.out.println("超时"); | |
| 591 | + }); | |
| 592 | + } | |
| 593 | + | |
| 594 | + } | |
| 595 | + } | |
| 596 | + } | |
| 597 | + } | |
| 598 | + | |
| 599 | + }else{ | |
| 470 | 600 | if (!"rtp".equals(app)){ |
| 471 | 601 | String type = OriginType.values()[item.getOriginType()].getType(); |
| 472 | 602 | MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); |
| ... | ... | @@ -521,10 +651,23 @@ public class ZLMHttpHookListener { |
| 521 | 651 | if (sendRtpItem.getApp().equals(app)) { |
| 522 | 652 | String platformId = sendRtpItem.getPlatformId(); |
| 523 | 653 | ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); |
| 654 | + Device device = deviceService.queryDevice(platformId); | |
| 524 | 655 | |
| 525 | 656 | try { |
| 526 | - commanderFroPlatform.streamByeCmd(platform, sendRtpItem); | |
| 527 | - } catch (SipException | InvalidArgumentException | ParseException e) { | |
| 657 | + if (platform != null) { | |
| 658 | + commanderFroPlatform.streamByeCmd(platform, sendRtpItem); | |
| 659 | + }else { | |
| 660 | + if (sendRtpItem.isOnlyAudio()) { | |
| 661 | + AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId()); | |
| 662 | + if (device != null && audioBroadcastCatch != null) { | |
| 663 | +// cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); | |
| 664 | + } | |
| 665 | + }else { | |
| 666 | + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), stream, sendRtpItem.getCallId()); | |
| 667 | + } | |
| 668 | + | |
| 669 | + } | |
| 670 | + } catch (SipException | InvalidArgumentException | ParseException | SsrcTransactionNotFoundException e) { | |
| 528 | 671 | logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); |
| 529 | 672 | } |
| 530 | 673 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
| ... | ... | @@ -164,33 +164,6 @@ public class ZLMRTPServerFactory { |
| 164 | 164 | return result; |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | -// private int getPortFromportRange(MediaServerItem mediaServerItem) { | |
| 168 | -// int currentPort = mediaServerItem.getCurrentPort(); | |
| 169 | -// if (currentPort == 0) { | |
| 170 | -// String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(","); | |
| 171 | -// if (portRangeStrArray.length != 2) { | |
| 172 | -// portRangeArray[0] = 30000; | |
| 173 | -// portRangeArray[1] = 30500; | |
| 174 | -// }else { | |
| 175 | -// portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]); | |
| 176 | -// portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]); | |
| 177 | -// } | |
| 178 | -// } | |
| 179 | -// | |
| 180 | -// if (currentPort == 0 || currentPort++ > portRangeArray[1]) { | |
| 181 | -// currentPort = portRangeArray[0]; | |
| 182 | -// mediaServerItem.setCurrentPort(currentPort); | |
| 183 | -// return portRangeArray[0]; | |
| 184 | -// } else { | |
| 185 | -// if (currentPort % 2 == 1) { | |
| 186 | -// currentPort++; | |
| 187 | -// } | |
| 188 | -// currentPort++; | |
| 189 | -// mediaServerItem.setCurrentPort(currentPort); | |
| 190 | -// return currentPort; | |
| 191 | -// } | |
| 192 | -// } | |
| 193 | - | |
| 194 | 167 | /** |
| 195 | 168 | * 创建一个国标推流 |
| 196 | 169 | * @param ip 推流ip | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java
| ... | ... | @@ -38,6 +38,7 @@ public class ZlmHttpHookSubscribe { |
| 38 | 38 | hookSubscribe.setExpires(expiresInstant); |
| 39 | 39 | } |
| 40 | 40 | allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event); |
| 41 | + System.out.println(allSubscribes); | |
| 41 | 42 | } |
| 42 | 43 | |
| 43 | 44 | public ZlmHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) { |
| ... | ... | @@ -48,6 +49,7 @@ public class ZlmHttpHookSubscribe { |
| 48 | 49 | } |
| 49 | 50 | for (IHookSubscribe key : eventMap.keySet()) { |
| 50 | 51 | Boolean result = null; |
| 52 | + | |
| 51 | 53 | for (String s : key.getContent().keySet()) { |
| 52 | 54 | if (result == null) { |
| 53 | 55 | result = key.getContent().getString(s).equals(hookResponse.getString(s)); | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
| ... | ... | @@ -24,10 +24,26 @@ public class HookSubscribeFactory { |
| 24 | 24 | return hookSubscribe; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | + public static HookSubscribeForStreamPush on_publish(String app, String stream, String scheam, String mediaServerId) { | |
| 28 | + HookSubscribeForStreamPush hookSubscribe = new HookSubscribeForStreamPush(); | |
| 29 | + JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject(); | |
| 30 | + subscribeKey.put("app", app); | |
| 31 | + subscribeKey.put("stream", stream); | |
| 32 | + if (scheam != null) { | |
| 33 | + subscribeKey.put("schema", scheam); | |
| 34 | + } | |
| 35 | + subscribeKey.put("mediaServerId", mediaServerId); | |
| 36 | + hookSubscribe.setContent(subscribeKey); | |
| 37 | + | |
| 38 | + return hookSubscribe; | |
| 39 | + } | |
| 40 | + | |
| 41 | + | |
| 27 | 42 | public static HookSubscribeForServerStarted on_server_started() { |
| 28 | 43 | HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); |
| 29 | 44 | hookSubscribe.setContent(new JSONObject()); |
| 30 | 45 | |
| 31 | 46 | return hookSubscribe; |
| 32 | 47 | } |
| 48 | + | |
| 33 | 49 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamPush.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.media.zlm.dto; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | + | |
| 5 | +import java.time.Instant; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * hook订阅-开始推流 | |
| 9 | + * @author lin | |
| 10 | + */ | |
| 11 | +public class HookSubscribeForStreamPush implements IHookSubscribe{ | |
| 12 | + | |
| 13 | + private HookType hookType = HookType.on_publish; | |
| 14 | + | |
| 15 | + private JSONObject content; | |
| 16 | + | |
| 17 | + private Instant expires; | |
| 18 | + | |
| 19 | + @Override | |
| 20 | + public HookType getHookType() { | |
| 21 | + return hookType; | |
| 22 | + } | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + public JSONObject getContent() { | |
| 26 | + return content; | |
| 27 | + } | |
| 28 | + | |
| 29 | + public void setContent(JSONObject content) { | |
| 30 | + this.content = content; | |
| 31 | + } | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public Instant getExpires() { | |
| 35 | + return expires; | |
| 36 | + } | |
| 37 | + | |
| 38 | + @Override | |
| 39 | + public void setExpires(Instant expires) { | |
| 40 | + this.expires = expires; | |
| 41 | + } | |
| 42 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
| ... | ... | @@ -11,11 +11,16 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 11 | 11 | import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; |
| 12 | 12 | import com.genersoft.iot.vmp.service.bean.PlayBackCallback; |
| 13 | 13 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 14 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | |
| 14 | 15 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 15 | 16 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 16 | 17 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 17 | 18 | import org.springframework.web.context.request.async.DeferredResult; |
| 18 | 19 | |
| 20 | +import javax.sip.InvalidArgumentException; | |
| 21 | +import javax.sip.SipException; | |
| 22 | +import java.text.ParseException; | |
| 23 | + | |
| 19 | 24 | /** |
| 20 | 25 | * 点播处理 |
| 21 | 26 | */ |
| ... | ... | @@ -23,6 +28,10 @@ public interface IPlayService { |
| 23 | 28 | |
| 24 | 29 | void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid); |
| 25 | 30 | |
| 31 | + void talk(MediaServerItem mediaServerItem, Device device, String channelId, | |
| 32 | + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, | |
| 33 | + Runnable timeoutCallback); | |
| 34 | + | |
| 26 | 35 | void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, |
| 27 | 36 | ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, |
| 28 | 37 | InviteTimeOutCallback timeoutCallback, String uuid); |
| ... | ... | @@ -44,6 +53,8 @@ public interface IPlayService { |
| 44 | 53 | |
| 45 | 54 | void zlmServerOnline(String mediaServerId); |
| 46 | 55 | |
| 47 | - void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event); | |
| 56 | + AudioBroadcastResult audioBroadcast(Device device, String channelId); | |
| 48 | 57 | void stopAudioBroadcast(String deviceId, String channelId); |
| 58 | + | |
| 59 | + void audioBroadcastCmd(Device device, String channelId, int timeout, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; | |
| 49 | 60 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
| ... | ... | @@ -69,7 +69,7 @@ public class MediaServiceImpl implements IMediaService { |
| 69 | 69 | JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); |
| 70 | 70 | JSONArray tracks = mediaJSON.getJSONArray("tracks"); |
| 71 | 71 | if (authority) { |
| 72 | - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld); | |
| 72 | + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld, true); | |
| 73 | 73 | }else { |
| 74 | 74 | streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null, true); |
| 75 | 75 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -12,8 +12,10 @@ import javax.sip.SipException; |
| 12 | 12 | import com.genersoft.iot.vmp.gb28181.bean.*; |
| 13 | 13 | import com.genersoft.iot.vmp.conf.exception.ControllerException; |
| 14 | 14 | import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; |
| 15 | -import com.genersoft.iot.vmp.gb28181.bean.*; | |
| 15 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; | |
| 16 | +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; | |
| 16 | 17 | import com.genersoft.iot.vmp.service.IDeviceService; |
| 18 | +import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | |
| 17 | 19 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| 18 | 20 | import org.slf4j.Logger; |
| 19 | 21 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -36,7 +38,6 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 36 | 38 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 37 | 39 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 38 | 40 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 39 | -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; | |
| 40 | 41 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 41 | 42 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; |
| 42 | 43 | import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; |
| ... | ... | @@ -54,33 +55,10 @@ import com.genersoft.iot.vmp.service.bean.PlayBackResult; |
| 54 | 55 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 55 | 56 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 56 | 57 | import com.genersoft.iot.vmp.storager.IVideoManagerStorage; |
| 57 | -import com.genersoft.iot.vmp.utils.redis.RedisUtil; | |
| 58 | -import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult; | |
| 59 | -import com.genersoft.iot.vmp.utils.DateUtil; | |
| 60 | 58 | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| 61 | 59 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.AudioBroadcastEvent; |
| 62 | 60 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 63 | 61 | |
| 64 | -import gov.nist.javax.sip.stack.SIPDialog; | |
| 65 | -import org.slf4j.Logger; | |
| 66 | -import org.slf4j.LoggerFactory; | |
| 67 | -import org.springframework.beans.factory.annotation.Autowired; | |
| 68 | -import org.springframework.http.HttpStatus; | |
| 69 | -import org.springframework.http.ResponseEntity; | |
| 70 | -import org.springframework.stereotype.Service; | |
| 71 | -import org.springframework.util.ResourceUtils; | |
| 72 | -import org.springframework.web.context.request.async.DeferredResult; | |
| 73 | - | |
| 74 | -import javax.sip.ResponseEvent; | |
| 75 | -import javax.sip.SipException; | |
| 76 | -import java.io.FileNotFoundException; | |
| 77 | -import java.math.BigDecimal; | |
| 78 | -import java.text.ParseException; | |
| 79 | -import java.math.RoundingMode; | |
| 80 | -import java.util.*; | |
| 81 | -import java.util.stream.Collectors; | |
| 82 | -import java.util.stream.Stream; | |
| 83 | - | |
| 84 | 62 | @SuppressWarnings(value = {"rawtypes", "unchecked"}) |
| 85 | 63 | @Service |
| 86 | 64 | public class PlayServiceImpl implements IPlayService { |
| ... | ... | @@ -97,7 +75,10 @@ public class PlayServiceImpl implements IPlayService { |
| 97 | 75 | private AudioBroadcastManager audioBroadcastManager; |
| 98 | 76 | |
| 99 | 77 | @Autowired |
| 100 | - private SIPCommanderFroPlatform sipCommanderFroPlatform; | |
| 78 | + private IDeviceService deviceService; | |
| 79 | + | |
| 80 | + @Autowired | |
| 81 | + private ISIPCommanderForPlatform sipCommanderFroPlatform; | |
| 101 | 82 | |
| 102 | 83 | @Autowired |
| 103 | 84 | private IRedisCatchStorage redisCatchStorage; |
| ... | ... | @@ -123,10 +104,6 @@ public class PlayServiceImpl implements IPlayService { |
| 123 | 104 | @Autowired |
| 124 | 105 | private VideoStreamSessionManager streamSession; |
| 125 | 106 | |
| 126 | - | |
| 127 | - @Autowired | |
| 128 | - private IDeviceService deviceService; | |
| 129 | - | |
| 130 | 107 | @Autowired |
| 131 | 108 | private UserSetting userSetting; |
| 132 | 109 | |
| ... | ... | @@ -145,7 +122,6 @@ public class PlayServiceImpl implements IPlayService { |
| 145 | 122 | private ThreadPoolTaskExecutor taskExecutor; |
| 146 | 123 | |
| 147 | 124 | |
| 148 | - | |
| 149 | 125 | @Override |
| 150 | 126 | public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, |
| 151 | 127 | ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, |
| ... | ... | @@ -169,15 +145,15 @@ public class PlayServiceImpl implements IPlayService { |
| 169 | 145 | StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); |
| 170 | 146 | playResult.setDevice(device); |
| 171 | 147 | |
| 172 | - result.onCompletion(()->{ | |
| 148 | + result.onCompletion(() -> { | |
| 173 | 149 | // 点播结束时调用截图接口 |
| 174 | - taskExecutor.execute(()->{ | |
| 150 | + taskExecutor.execute(() -> { | |
| 175 | 151 | // TODO 应该在上流时调用更好,结束也可能是错误结束 |
| 176 | - String path = "snap"; | |
| 177 | - String fileName = deviceId + "_" + channelId + ".jpg"; | |
| 178 | - WVPResult wvpResult = (WVPResult)result.getResult(); | |
| 152 | + String path = "snap"; | |
| 153 | + String fileName = deviceId + "_" + channelId + ".jpg"; | |
| 154 | + WVPResult wvpResult = (WVPResult) result.getResult(); | |
| 179 | 155 | if (Objects.requireNonNull(wvpResult).getCode() == 0) { |
| 180 | - StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData(); | |
| 156 | + StreamInfo streamInfoForSuccess = (StreamInfo) wvpResult.getData(); | |
| 181 | 157 | MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId()); |
| 182 | 158 | String streamUrl = streamInfoForSuccess.getFmp4(); |
| 183 | 159 | // 请求截图 |
| ... | ... | @@ -201,7 +177,7 @@ public class PlayServiceImpl implements IPlayService { |
| 201 | 177 | MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); |
| 202 | 178 | |
| 203 | 179 | JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); |
| 204 | - if(rtpInfo.getInteger("code") == 0){ | |
| 180 | + if (rtpInfo.getInteger("code") == 0) { | |
| 205 | 181 | if (rtpInfo.getBoolean("exist")) { |
| 206 | 182 | int localPort = rtpInfo.getInteger("local_port"); |
| 207 | 183 | if (localPort == 0) { |
| ... | ... | @@ -214,7 +190,7 @@ public class PlayServiceImpl implements IPlayService { |
| 214 | 190 | |
| 215 | 191 | resultHolder.invokeAllResult(msg); |
| 216 | 192 | return playResult; |
| 217 | - }else { | |
| 193 | + } else { | |
| 218 | 194 | WVPResult wvpResult = new WVPResult(); |
| 219 | 195 | wvpResult.setCode(ErrorCode.SUCCESS.getCode()); |
| 220 | 196 | wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); |
| ... | ... | @@ -227,12 +203,12 @@ public class PlayServiceImpl implements IPlayService { |
| 227 | 203 | } |
| 228 | 204 | } |
| 229 | 205 | |
| 230 | - }else { | |
| 206 | + } else { | |
| 231 | 207 | redisCatchStorage.stopPlay(streamInfo); |
| 232 | 208 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| 233 | 209 | streamInfo = null; |
| 234 | 210 | } |
| 235 | - }else { | |
| 211 | + } else { | |
| 236 | 212 | //zlm连接失败 |
| 237 | 213 | redisCatchStorage.stopPlay(streamInfo); |
| 238 | 214 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| ... | ... | @@ -246,7 +222,7 @@ public class PlayServiceImpl implements IPlayService { |
| 246 | 222 | } |
| 247 | 223 | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| 248 | 224 | logger.info(JSONObject.toJSONString(ssrcInfo)); |
| 249 | - play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response)->{ | |
| 225 | + play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> { | |
| 250 | 226 | if (hookEvent != null) { |
| 251 | 227 | hookEvent.response(mediaServerItem, response); |
| 252 | 228 | } |
| ... | ... | @@ -260,13 +236,13 @@ public class PlayServiceImpl implements IPlayService { |
| 260 | 236 | if (errorEvent != null) { |
| 261 | 237 | errorEvent.response(event); |
| 262 | 238 | } |
| 263 | - }, (code, msgStr)->{ | |
| 239 | + }, (code, msgStr) -> { | |
| 264 | 240 | // invite点播超时 |
| 265 | 241 | WVPResult wvpResult = new WVPResult(); |
| 266 | 242 | wvpResult.setCode(ErrorCode.ERROR100.getCode()); |
| 267 | 243 | if (code == 0) { |
| 268 | 244 | wvpResult.setMsg("点播超时,请稍候重试"); |
| 269 | - }else if (code == 1) { | |
| 245 | + } else if (code == 1) { | |
| 270 | 246 | wvpResult.setMsg("收流超时,请稍候重试"); |
| 271 | 247 | } |
| 272 | 248 | msg.setData(wvpResult); |
| ... | ... | @@ -277,6 +253,186 @@ public class PlayServiceImpl implements IPlayService { |
| 277 | 253 | return playResult; |
| 278 | 254 | } |
| 279 | 255 | |
| 256 | + @Override | |
| 257 | + public void talk(MediaServerItem mediaServerItem, Device device, String channelId, | |
| 258 | + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, | |
| 259 | + Runnable timeoutCallback) { | |
| 260 | + String streamId = null; | |
| 261 | + if (mediaServerItem.isRtpEnable()) { | |
| 262 | + streamId = String.format("%s_%s", device.getDeviceId(), channelId); | |
| 263 | + } | |
| 264 | + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); | |
| 265 | + logger.info("[对讲开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 266 | + // 超时处理 | |
| 267 | + String timeOutTaskKey = UUID.randomUUID().toString(); | |
| 268 | + SSRCInfo finalSsrcInfo = ssrcInfo; | |
| 269 | + System.out.println("设置超时任务: " + timeOutTaskKey); | |
| 270 | + dynamicTask.startDelay(timeOutTaskKey, () -> { | |
| 271 | + | |
| 272 | + logger.info("[对讲超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc()); | |
| 273 | + timeoutCallback.run(); | |
| 274 | + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | |
| 275 | + try { | |
| 276 | + cmder.streamByeCmd(device, channelId, finalSsrcInfo.getStream(), null); | |
| 277 | + } catch (InvalidArgumentException | ParseException | SipException e) { | |
| 278 | + logger.error("[对讲超时], 发送BYE失败 {}", e.getMessage()); | |
| 279 | + } catch (SsrcTransactionNotFoundException e) { | |
| 280 | + timeoutCallback.run(); | |
| 281 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 282 | + mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 283 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 284 | + } | |
| 285 | + }, userSetting.getPlayTimeout()); | |
| 286 | + final String ssrc = ssrcInfo.getSsrc(); | |
| 287 | + final String stream = ssrcInfo.getStream(); | |
| 288 | + //端口获取失败的ssrcInfo 没有必要发送点播指令 | |
| 289 | + if (ssrcInfo.getPort() <= 0) { | |
| 290 | + logger.info("[对讲] 端口分配异常,deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); | |
| 291 | + return; | |
| 292 | + } | |
| 293 | + try { | |
| 294 | + String callId = SipUtils.getNewCallId(); | |
| 295 | + cmder.talkStreamCmd(mediaServerItem, ssrcInfo, device, channelId, callId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { | |
| 296 | + logger.info("[对讲] 流已生成, 开始推流: " + response.toJSONString()); | |
| 297 | + dynamicTask.stop(timeOutTaskKey); | |
| 298 | + // TODO 暂不做处理 | |
| 299 | + }, (MediaServerItem mediaServerItemInuse, JSONObject json) -> { | |
| 300 | + logger.info("[对讲] 开始推流: " + json.toJSONString()); | |
| 301 | + dynamicTask.stop(timeOutTaskKey); | |
| 302 | + // 获取远程IP端口 作为回复语音流的地址 | |
| 303 | + String ip = json.getString("ip"); | |
| 304 | + Integer port = json.getInteger("port"); | |
| 305 | + logger.info("[远端设备开始推流]{}/{}, 来自ip:{}, 端口:{}", device.getDeviceId(), channelId, ip, port); | |
| 306 | + // 查看平台推流是否就绪 | |
| 307 | + Boolean ready = zlmrtpServerFactory.isStreamReady(mediaServerItemInuse, "talk", stream); | |
| 308 | + if (!ready) { | |
| 309 | + try { | |
| 310 | + cmder.streamByeCmd(device, channelId, finalSsrcInfo.getStream(), null); | |
| 311 | + } catch (InvalidArgumentException | ParseException | SipException e) { | |
| 312 | + logger.error("[对讲超时], 发送BYE失败 {}", e.getMessage()); | |
| 313 | + } catch (SsrcTransactionNotFoundException e) { | |
| 314 | + timeoutCallback.run(); | |
| 315 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 316 | + mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 317 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 318 | + } | |
| 319 | + }else { | |
| 320 | + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, ip, port, ssrcInfo.getSsrc(), device.getDeviceId(), | |
| 321 | + device.getDeviceId(), channelId, | |
| 322 | + false); | |
| 323 | + | |
| 324 | + sendRtpItem.setTcpActive(false); | |
| 325 | + if (sendRtpItem == null || sendRtpItem.getLocalPort() == 0) { | |
| 326 | + logger.warn("服务器端口资源不足"); | |
| 327 | + try { | |
| 328 | + cmder.streamByeCmd(device, channelId, finalSsrcInfo.getStream(), null); | |
| 329 | + } catch (InvalidArgumentException | ParseException | SipException e) { | |
| 330 | + logger.error("[对讲超时], 发送BYE失败 {}", e.getMessage()); | |
| 331 | + } catch (SsrcTransactionNotFoundException e) { | |
| 332 | + timeoutCallback.run(); | |
| 333 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 334 | + mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 335 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 336 | + } | |
| 337 | + return; | |
| 338 | + } | |
| 339 | + sendRtpItem.setCallId(callId); | |
| 340 | + sendRtpItem.setPlayType(InviteStreamType.TALK); | |
| 341 | + sendRtpItem.setStatus(1); | |
| 342 | + sendRtpItem.setIp(ip); | |
| 343 | + sendRtpItem.setPort(port); | |
| 344 | + sendRtpItem.setTcpActive(false); | |
| 345 | + sendRtpItem.setStreamId(ssrcInfo.getStream()); | |
| 346 | + sendRtpItem.setApp("talk"); | |
| 347 | + sendRtpItem.setSsrc(ssrc); | |
| 348 | + redisCatchStorage.updateSendRTPSever(sendRtpItem); | |
| 349 | + | |
| 350 | + Map<String, Object> param = new HashMap<>(12); | |
| 351 | + param.put("vhost","__defaultVhost__"); | |
| 352 | + param.put("app",sendRtpItem.getApp()); | |
| 353 | + param.put("stream",sendRtpItem.getStreamId()); | |
| 354 | + param.put("ssrc", sendRtpItem.getSsrc()); | |
| 355 | + param.put("src_port", sendRtpItem.getLocalPort()); | |
| 356 | + param.put("pt", sendRtpItem.getPt()); | |
| 357 | + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); | |
| 358 | + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); | |
| 359 | + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaServerItemInuse, param); | |
| 360 | + System.out.println(11111); | |
| 361 | + System.out.println(jsonObject); | |
| 362 | + } | |
| 363 | + | |
| 364 | + }, (event) -> { | |
| 365 | +// ResponseEvent responseEvent = (ResponseEvent) event.event; | |
| 366 | +// String contentString = new String(responseEvent.getResponse().getRawContent()); | |
| 367 | +// // 获取ssrc | |
| 368 | +// int ssrcIndex = contentString.indexOf("y="); | |
| 369 | +// // 检查是否有y字段 | |
| 370 | +// if (ssrcIndex >= 0) { | |
| 371 | +// //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
| 372 | +// String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 373 | +// // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | |
| 374 | +// if (ssrc.equals(ssrcInResponse)) { | |
| 375 | +// return; | |
| 376 | +// } | |
| 377 | +// logger.info("[对讲消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | |
| 378 | +// if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { | |
| 379 | +// logger.info("[对讲消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); | |
| 380 | +// | |
| 381 | +// if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { | |
| 382 | +// // ssrc 不可用 | |
| 383 | +// // 释放ssrc | |
| 384 | +// mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 385 | +// streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 386 | +// event.msg = "下级自定义了ssrc,但是此ssrc不可用"; | |
| 387 | +// event.statusCode = 400; | |
| 388 | +// errorEvent.response(event); | |
| 389 | +// return; | |
| 390 | +// } | |
| 391 | +// | |
| 392 | +// // 单端口模式streamId也有变化,需要重新设置监听 | |
| 393 | +// if (!mediaServerItem.isRtpEnable()) { | |
| 394 | +// // 添加订阅 | |
| 395 | +// HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); | |
| 396 | +// subscribe.removeSubscribe(hookSubscribe); | |
| 397 | +// hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | |
| 398 | +// subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { | |
| 399 | +// logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); | |
| 400 | +// dynamicTask.stop(timeOutTaskKey); | |
| 401 | +// // hook响应 | |
| 402 | +// onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid); | |
| 403 | +// hookEvent.response(mediaServerItemInUse, response); | |
| 404 | +// }); | |
| 405 | +// } | |
| 406 | +// // 关闭rtp server | |
| 407 | +// mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 408 | +// // 重新开启ssrc server | |
| 409 | +// mediaServerService.openRTPServer(mediaServerItem, finalSsrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, finalSsrcInfo.getPort()); | |
| 410 | +// | |
| 411 | +// } | |
| 412 | +// } | |
| 413 | + }, (event) -> { | |
| 414 | + dynamicTask.stop(timeOutTaskKey); | |
| 415 | + mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 416 | + // 释放ssrc | |
| 417 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 418 | + | |
| 419 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 420 | + errorEvent.response(event); | |
| 421 | + }); | |
| 422 | + } catch (InvalidArgumentException | SipException | ParseException e) { | |
| 423 | + | |
| 424 | + logger.error("[命令发送失败] 对讲消息: {}", e.getMessage()); | |
| 425 | + dynamicTask.stop(timeOutTaskKey); | |
| 426 | + mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); | |
| 427 | + // 释放ssrc | |
| 428 | + mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc()); | |
| 429 | + | |
| 430 | + streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream()); | |
| 431 | + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null)); | |
| 432 | + eventResult.msg = "命令发送失败"; | |
| 433 | + errorEvent.response(eventResult); | |
| 434 | + } | |
| 435 | + } | |
| 280 | 436 | |
| 281 | 437 | |
| 282 | 438 | @Override |
| ... | ... | @@ -291,12 +447,12 @@ public class PlayServiceImpl implements IPlayService { |
| 291 | 447 | if (ssrcInfo == null) { |
| 292 | 448 | ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); |
| 293 | 449 | } |
| 294 | - logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck() ); | |
| 450 | + logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); | |
| 295 | 451 | // 超时处理 |
| 296 | 452 | String timeOutTaskKey = UUID.randomUUID().toString(); |
| 297 | 453 | SSRCInfo finalSsrcInfo = ssrcInfo; |
| 298 | 454 | System.out.println("设置超时任务: " + timeOutTaskKey); |
| 299 | - dynamicTask.startDelay( timeOutTaskKey,()->{ | |
| 455 | + dynamicTask.startDelay(timeOutTaskKey, () -> { | |
| 300 | 456 | |
| 301 | 457 | logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, finalSsrcInfo.getPort(), finalSsrcInfo.getSsrc()); |
| 302 | 458 | timeoutCallback.run(1, "收流超时"); |
| ... | ... | @@ -315,7 +471,7 @@ public class PlayServiceImpl implements IPlayService { |
| 315 | 471 | final String ssrc = ssrcInfo.getSsrc(); |
| 316 | 472 | final String stream = ssrcInfo.getStream(); |
| 317 | 473 | //端口获取失败的ssrcInfo 没有必要发送点播指令 |
| 318 | - if(ssrcInfo.getPort() <= 0){ | |
| 474 | + if (ssrcInfo.getPort() <= 0) { | |
| 319 | 475 | logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); |
| 320 | 476 | return; |
| 321 | 477 | } |
| ... | ... | @@ -330,7 +486,7 @@ public class PlayServiceImpl implements IPlayService { |
| 330 | 486 | logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); |
| 331 | 487 | |
| 332 | 488 | }, (event) -> { |
| 333 | - ResponseEvent responseEvent = (ResponseEvent)event.event; | |
| 489 | + ResponseEvent responseEvent = (ResponseEvent) event.event; | |
| 334 | 490 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| 335 | 491 | // 获取ssrc |
| 336 | 492 | int ssrcIndex = contentString.indexOf("y="); |
| ... | ... | @@ -342,7 +498,7 @@ public class PlayServiceImpl implements IPlayService { |
| 342 | 498 | if (ssrc.equals(ssrcInResponse)) { |
| 343 | 499 | return; |
| 344 | 500 | } |
| 345 | - logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse ); | |
| 501 | + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | |
| 346 | 502 | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| 347 | 503 | logger.info("[点播消息] SSRC修正 {}->{}", ssrc, ssrcInResponse); |
| 348 | 504 | |
| ... | ... | @@ -363,13 +519,13 @@ public class PlayServiceImpl implements IPlayService { |
| 363 | 519 | HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); |
| 364 | 520 | subscribe.removeSubscribe(hookSubscribe); |
| 365 | 521 | hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); |
| 366 | - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{ | |
| 367 | - logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); | |
| 368 | - dynamicTask.stop(timeOutTaskKey); | |
| 369 | - // hook响应 | |
| 370 | - onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid); | |
| 371 | - hookEvent.response(mediaServerItemInUse, response); | |
| 372 | - }); | |
| 522 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { | |
| 523 | + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); | |
| 524 | + dynamicTask.stop(timeOutTaskKey); | |
| 525 | + // hook响应 | |
| 526 | + onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId, uuid); | |
| 527 | + hookEvent.response(mediaServerItemInUse, response); | |
| 528 | + }); | |
| 373 | 529 | } |
| 374 | 530 | // 关闭rtp server |
| 375 | 531 | mediaServerService.closeRTPServer(mediaServerItem, finalSsrcInfo.getStream()); |
| ... | ... | @@ -441,7 +597,7 @@ public class PlayServiceImpl implements IPlayService { |
| 441 | 597 | MediaServerItem mediaServerItem; |
| 442 | 598 | if (mediaServerId == null) { |
| 443 | 599 | mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(); |
| 444 | - }else { | |
| 600 | + } else { | |
| 445 | 601 | mediaServerItem = mediaServerService.getOne(mediaServerId); |
| 446 | 602 | } |
| 447 | 603 | if (mediaServerItem == null) { |
| ... | ... | @@ -452,8 +608,8 @@ public class PlayServiceImpl implements IPlayService { |
| 452 | 608 | |
| 453 | 609 | @Override |
| 454 | 610 | public DeferredResult<WVPResult<StreamInfo>> playBack(String deviceId, String channelId, String startTime, |
| 455 | - String endTime,InviteStreamCallback inviteStreamCallback, | |
| 456 | - PlayBackCallback callback) { | |
| 611 | + String endTime, InviteStreamCallback inviteStreamCallback, | |
| 612 | + PlayBackCallback callback) { | |
| 457 | 613 | Device device = storager.queryVideoDevice(deviceId); |
| 458 | 614 | if (device == null) { |
| 459 | 615 | return null; |
| ... | ... | @@ -466,9 +622,9 @@ public class PlayServiceImpl implements IPlayService { |
| 466 | 622 | |
| 467 | 623 | @Override |
| 468 | 624 | public DeferredResult<WVPResult<StreamInfo>> playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, |
| 469 | - String deviceId, String channelId, String startTime, | |
| 470 | - String endTime, InviteStreamCallback infoCallBack, | |
| 471 | - PlayBackCallback playBackCallback) { | |
| 625 | + String deviceId, String channelId, String startTime, | |
| 626 | + String endTime, InviteStreamCallback infoCallBack, | |
| 627 | + PlayBackCallback playBackCallback) { | |
| 472 | 628 | if (mediaServerItem == null || ssrcInfo == null) { |
| 473 | 629 | return null; |
| 474 | 630 | } |
| ... | ... | @@ -485,7 +641,7 @@ public class PlayServiceImpl implements IPlayService { |
| 485 | 641 | requestMessage.setKey(key); |
| 486 | 642 | PlayBackResult<RequestMessage> playBackResult = new PlayBackResult<>(); |
| 487 | 643 | String playBackTimeOutTaskKey = UUID.randomUUID().toString(); |
| 488 | - dynamicTask.startDelay(playBackTimeOutTaskKey, ()->{ | |
| 644 | + dynamicTask.startDelay(playBackTimeOutTaskKey, () -> { | |
| 489 | 645 | logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
| 490 | 646 | playBackResult.setCode(ErrorCode.ERROR100.getCode()); |
| 491 | 647 | playBackResult.setMsg("回放超时"); |
| ... | ... | @@ -545,7 +701,7 @@ public class PlayServiceImpl implements IPlayService { |
| 545 | 701 | cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, |
| 546 | 702 | hookEvent, eventResult -> { |
| 547 | 703 | if (eventResult.type == SipSubscribe.EventResultType.response) { |
| 548 | - ResponseEvent responseEvent = (ResponseEvent)eventResult.event; | |
| 704 | + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; | |
| 549 | 705 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| 550 | 706 | // 获取ssrc |
| 551 | 707 | int ssrcIndex = contentString.indexOf("y="); |
| ... | ... | @@ -557,7 +713,7 @@ public class PlayServiceImpl implements IPlayService { |
| 557 | 713 | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| 558 | 714 | return; |
| 559 | 715 | } |
| 560 | - logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse ); | |
| 716 | + logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | |
| 561 | 717 | if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { |
| 562 | 718 | logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); |
| 563 | 719 | |
| ... | ... | @@ -578,7 +734,7 @@ public class PlayServiceImpl implements IPlayService { |
| 578 | 734 | HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); |
| 579 | 735 | subscribe.removeSubscribe(hookSubscribe); |
| 580 | 736 | hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); |
| 581 | - subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response)->{ | |
| 737 | + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { | |
| 582 | 738 | logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); |
| 583 | 739 | dynamicTask.stop(playBackTimeOutTaskKey); |
| 584 | 740 | // hook响应 |
| ... | ... | @@ -614,7 +770,7 @@ public class PlayServiceImpl implements IPlayService { |
| 614 | 770 | MediaServerItem newMediaServerItem = getNewMediaServerItem(device); |
| 615 | 771 | SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true, true); |
| 616 | 772 | |
| 617 | - return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed,infoCallBack, hookCallBack); | |
| 773 | + return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, infoCallBack, hookCallBack); | |
| 618 | 774 | } |
| 619 | 775 | |
| 620 | 776 | @Override |
| ... | ... | @@ -640,7 +796,7 @@ public class PlayServiceImpl implements IPlayService { |
| 640 | 796 | downloadResult.setData(requestMessage); |
| 641 | 797 | |
| 642 | 798 | String downLoadTimeOutTaskKey = UUID.randomUUID().toString(); |
| 643 | - dynamicTask.startDelay(downLoadTimeOutTaskKey, ()->{ | |
| 799 | + dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> { | |
| 644 | 800 | logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId)); |
| 645 | 801 | wvpResult.setCode(ErrorCode.ERROR100.getCode()); |
| 646 | 802 | wvpResult.setMsg("录像下载请求超时"); |
| ... | ... | @@ -723,15 +879,15 @@ public class PlayServiceImpl implements IPlayService { |
| 723 | 879 | |
| 724 | 880 | if (duration == 0) { |
| 725 | 881 | streamInfo.setProgress(0); |
| 726 | - }else { | |
| 882 | + } else { | |
| 727 | 883 | String startTime = streamInfo.getStartTime(); |
| 728 | 884 | String endTime = streamInfo.getEndTime(); |
| 729 | 885 | long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime); |
| 730 | 886 | long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime); |
| 731 | 887 | |
| 732 | - BigDecimal currentCount = new BigDecimal(duration/1000); | |
| 733 | - BigDecimal totalCount = new BigDecimal(end-start); | |
| 734 | - BigDecimal divide = currentCount.divide(totalCount,2, RoundingMode.HALF_UP); | |
| 888 | + BigDecimal currentCount = new BigDecimal(duration / 1000); | |
| 889 | + BigDecimal totalCount = new BigDecimal(end - start); | |
| 890 | + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP); | |
| 735 | 891 | double process = divide.doubleValue(); |
| 736 | 892 | streamInfo.setProgress(process); |
| 737 | 893 | } |
| ... | ... | @@ -762,7 +918,7 @@ public class PlayServiceImpl implements IPlayService { |
| 762 | 918 | public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId) { |
| 763 | 919 | String streamId = resonse.getString("stream"); |
| 764 | 920 | JSONArray tracks = resonse.getJSONArray("tracks"); |
| 765 | - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks, null); | |
| 921 | + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null); | |
| 766 | 922 | streamInfo.setDeviceID(deviceId); |
| 767 | 923 | streamInfo.setChannelId(channelId); |
| 768 | 924 | return streamInfo; |
| ... | ... | @@ -788,7 +944,7 @@ public class PlayServiceImpl implements IPlayService { |
| 788 | 944 | List<SsrcTransaction> allSsrc = streamSession.getAllSsrc(); |
| 789 | 945 | if (allSsrc.size() > 0) { |
| 790 | 946 | for (SsrcTransaction ssrcTransaction : allSsrc) { |
| 791 | - if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) { | |
| 947 | + if (ssrcTransaction.getMediaServerId().equals(mediaServerId)) { | |
| 792 | 948 | Device device = deviceService.queryDevice(ssrcTransaction.getDeviceId()); |
| 793 | 949 | if (device == null) { |
| 794 | 950 | continue; |
| ... | ... | @@ -806,10 +962,36 @@ public class PlayServiceImpl implements IPlayService { |
| 806 | 962 | } |
| 807 | 963 | |
| 808 | 964 | @Override |
| 809 | - public void audioBroadcast(Device device, String channelId, int timeout, AudioBroadcastEvent event) { | |
| 965 | + public AudioBroadcastResult audioBroadcast(Device device, String channelId) { | |
| 966 | + if (device == null || channelId == null) { | |
| 967 | + return null; | |
| 968 | + } | |
| 969 | + logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); | |
| 970 | + DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); | |
| 971 | + if (deviceChannel == null) { | |
| 972 | + logger.warn("开启语音广播的时候未找到通道: {}", channelId); | |
| 973 | + return null; | |
| 974 | + } | |
| 975 | + MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(); | |
| 976 | +// String app = "broadcast"; | |
| 977 | + // TODO 从sip user agent中判断是什么品牌设备,大华默认使用talk模式,其他使用broadcast模式 | |
| 978 | + String app = "talk"; | |
| 979 | + String stream = device.getDeviceId() + "_" + channelId; | |
| 980 | + StreamInfo broadcast = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "broadcast", stream, null, null, null, false); | |
| 981 | + AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); | |
| 982 | + audioBroadcastResult.setApp(app); | |
| 983 | + audioBroadcastResult.setStream(stream); | |
| 984 | + audioBroadcastResult.setStreamInfo(mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null,false)); | |
| 985 | + audioBroadcastResult.setCodec("G.711"); | |
| 986 | + return audioBroadcastResult; | |
| 987 | + } | |
| 988 | + | |
| 989 | + @Override | |
| 990 | + public void audioBroadcastCmd(Device device, String channelId, int timeout, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { | |
| 810 | 991 | if (device == null || channelId == null) { |
| 811 | 992 | return; |
| 812 | 993 | } |
| 994 | + logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); | |
| 813 | 995 | DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); |
| 814 | 996 | if (deviceChannel == null) { |
| 815 | 997 | logger.warn("开启语音广播的时候未找到通道: {}", channelId); |
| ... | ... | @@ -818,7 +1000,7 @@ public class PlayServiceImpl implements IPlayService { |
| 818 | 1000 | } |
| 819 | 1001 | // 查询通道使用状态 |
| 820 | 1002 | if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { |
| 821 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); | |
| 1003 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); | |
| 822 | 1004 | if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { |
| 823 | 1005 | // 查询流是否存在,不存在则认为是异常状态 |
| 824 | 1006 | MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); |
| ... | ... | @@ -827,8 +1009,8 @@ public class PlayServiceImpl implements IPlayService { |
| 827 | 1009 | logger.warn("语音广播已经开启: {}", channelId); |
| 828 | 1010 | event.call("语音广播已经开启"); |
| 829 | 1011 | return; |
| 830 | - }else { | |
| 831 | - audioBroadcastManager.del(deviceChannel.getDeviceId(),channelId); | |
| 1012 | + } else { | |
| 1013 | + audioBroadcastManager.del(deviceChannel.getDeviceId(), channelId); | |
| 832 | 1014 | redisCatchStorage.deleteSendRTPServer(device.getDeviceId(), channelId, sendRtpItem.getCallId(), sendRtpItem.getStreamId()); |
| 833 | 1015 | } |
| 834 | 1016 | } |
| ... | ... | @@ -847,39 +1029,33 @@ public class PlayServiceImpl implements IPlayService { |
| 847 | 1029 | }); |
| 848 | 1030 | } |
| 849 | 1031 | |
| 1032 | + | |
| 1033 | + | |
| 850 | 1034 | @Override |
| 851 | - public void stopAudioBroadcast(String deviceId, String channelId){ | |
| 1035 | + public void stopAudioBroadcast(String deviceId, String channelId) { | |
| 852 | 1036 | AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(deviceId, channelId); |
| 853 | 1037 | if (audioBroadcastCatch != null) { |
| 854 | 1038 | |
| 855 | - try { | |
| 856 | - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); | |
| 857 | - if (sendRtpItem != null) { | |
| 858 | - redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); | |
| 859 | - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 860 | - Map<String, Object> param = new HashMap<>(); | |
| 861 | - param.put("vhost", "__defaultVhost__"); | |
| 862 | - param.put("app", sendRtpItem.getApp()); | |
| 863 | - param.put("stream", sendRtpItem.getStreamId()); | |
| 864 | - zlmresTfulUtils.stopSendRtp(mediaInfo, param); | |
| 865 | - // 立刻结束设备的推流,等待自行结束太慢 | |
| 866 | - zlmresTfulUtils.closeStreams(mediaInfo, sendRtpItem.getApp(), sendRtpItem.getStreamId()); | |
| 867 | - } | |
| 868 | - if (audioBroadcastCatch.getStatus() == AudioBroadcastCatchStatus.Ok) { | |
| 869 | - cmder.streamByeCmd(audioBroadcastCatch.getDialog(), audioBroadcastCatch.getChannelId(), audioBroadcastCatch.getRequest(), null); | |
| 870 | - } | |
| 871 | - audioBroadcastManager.del(deviceId, channelId); | |
| 872 | - | |
| 873 | - } catch (SipException e) { | |
| 874 | - throw new RuntimeException(e); | |
| 875 | - } catch (ParseException e) { | |
| 876 | - throw new RuntimeException(e); | |
| 877 | - } catch (InvalidArgumentException e) { | |
| 878 | - throw new RuntimeException(e); | |
| 1039 | + Device device = deviceService.queryDevice(deviceId); | |
| 1040 | + if (device == null) { | |
| 1041 | + return; | |
| 1042 | + } | |
| 1043 | +// if (audioBroadcastCatch.getStatus() == AudioBroadcastCatchStatus.Ok) { | |
| 1044 | +// cmder.streamByeCmd(device, audioBroadcastCatch.getChannelId(), null, audioBroadcastCatch.getSipTransactionInfo().getCallId()); | |
| 1045 | +// } | |
| 1046 | + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); | |
| 1047 | + if (sendRtpItem != null) { | |
| 1048 | + redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); | |
| 1049 | + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); | |
| 1050 | + Map<String, Object> param = new HashMap<>(); | |
| 1051 | + param.put("vhost", "__defaultVhost__"); | |
| 1052 | + param.put("app", sendRtpItem.getApp()); | |
| 1053 | + param.put("stream", sendRtpItem.getStreamId()); | |
| 1054 | + zlmresTfulUtils.stopSendRtp(mediaInfo, param); | |
| 879 | 1055 | } |
| 880 | - } | |
| 881 | - | |
| 882 | 1056 | |
| 1057 | + audioBroadcastManager.del(deviceId, channelId); | |
| 1058 | + } | |
| 883 | 1059 | } |
| 884 | 1060 | |
| 885 | 1061 | @Override | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
| ... | ... | @@ -216,65 +216,20 @@ public class PlayController { |
| 216 | 216 | @Parameter(name = "timeout", description = "推流超时时间(秒)", required = true) |
| 217 | 217 | @GetMapping("/broadcast/{deviceId}/{channelId}") |
| 218 | 218 | @PostMapping("/broadcast/{deviceId}/{channelId}") |
| 219 | - public DeferredResult<String> broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout) { | |
| 219 | + public AudioBroadcastResult broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout) { | |
| 220 | 220 | if (logger.isDebugEnabled()) { |
| 221 | 221 | logger.debug("语音广播API调用"); |
| 222 | 222 | } |
| 223 | 223 | Device device = storager.queryVideoDevice(deviceId); |
| 224 | - DeferredResult<String> result = new DeferredResult<>(3 * 1000L); | |
| 225 | 224 | if (device == null) { |
| 226 | - result.setResult("未找到设备: " + deviceId); | |
| 227 | - return result; | |
| 225 | + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); | |
| 228 | 226 | } |
| 229 | 227 | if (channelId == null) { |
| 230 | - result.setResult("未找到通道: " + channelId); | |
| 231 | - return result; | |
| 228 | + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到通道: " + channelId); | |
| 232 | 229 | } |
| 233 | - String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; | |
| 234 | - if (resultHolder.exist(key, null)) { | |
| 235 | - result.setResult("设备使用中"); | |
| 236 | - return result; | |
| 237 | - } | |
| 238 | - if (timeout == null){ | |
| 239 | - timeout = 30; | |
| 240 | - } | |
| 241 | - String uuid = UUID.randomUUID().toString(); | |
| 242 | - | |
| 243 | - result.onTimeout(() -> { | |
| 244 | - logger.warn("语音广播操作超时, 设备未返回应答指令"); | |
| 245 | - RequestMessage msg = new RequestMessage(); | |
| 246 | - msg.setKey(key); | |
| 247 | - msg.setId(uuid); | |
| 248 | - JSONObject json = new JSONObject(); | |
| 249 | - json.put("DeviceID", deviceId); | |
| 250 | - json.put("CmdType", "Broadcast"); | |
| 251 | - json.put("Result", "Failed"); | |
| 252 | - json.put("Error", "Timeout. Device did not response to broadcast command."); | |
| 253 | - msg.setData(json); | |
| 254 | - resultHolder.invokeResult(msg); | |
| 255 | - }); | |
| 256 | - | |
| 257 | - result.onTimeout(()->{ | |
| 258 | - WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | |
| 259 | - wvpResult.setCode(-1); | |
| 260 | - wvpResult.setMsg("请求超时"); | |
| 261 | - RequestMessage requestMessage = new RequestMessage(); | |
| 262 | - requestMessage.setKey(key); | |
| 263 | - requestMessage.setData(wvpResult); | |
| 264 | - resultHolder.invokeAllResult(requestMessage); | |
| 265 | - }); | |
| 266 | - playService.audioBroadcast(device, channelId, timeout, (msg)->{ | |
| 267 | - WVPResult<AudioBroadcastResult> wvpResult = new WVPResult<>(); | |
| 268 | - wvpResult.setCode(-1); | |
| 269 | - wvpResult.setMsg(msg); | |
| 270 | - RequestMessage requestMessage = new RequestMessage(); | |
| 271 | - requestMessage.setKey(key); | |
| 272 | - requestMessage.setData(wvpResult); | |
| 273 | - resultHolder.invokeAllResult(requestMessage); | |
| 274 | - }); | |
| 275 | - resultHolder.put(key, uuid, result); | |
| 276 | - | |
| 277 | - return result; | |
| 230 | + | |
| 231 | + return playService.audioBroadcast(device, channelId); | |
| 232 | + | |
| 278 | 233 | } |
| 279 | 234 | |
| 280 | 235 | |
| ... | ... | @@ -283,10 +238,16 @@ public class PlayController { |
| 283 | 238 | @Parameter(name = "channelId", description = "通道Id", required = true) |
| 284 | 239 | @GetMapping("/broadcast/stop/{deviceId}/{channelId}") |
| 285 | 240 | @PostMapping("/broadcast/stop/{deviceId}/{channelId}") |
| 286 | - public void stopBroadcastA(@PathVariable String deviceId, @PathVariable String channelId) { | |
| 241 | + public void stopBroadcast(@PathVariable String deviceId, @PathVariable String channelId) { | |
| 287 | 242 | if (logger.isDebugEnabled()) { |
| 288 | 243 | logger.debug("停止语音广播API调用"); |
| 289 | 244 | } |
| 245 | +// try { | |
| 246 | +// playService.stopAudioBroadcast(deviceId, channelId); | |
| 247 | +// } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) { | |
| 248 | +// logger.error("[命令发送失败] 停止语音: {}", e.getMessage()); | |
| 249 | +// throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); | |
| 250 | +// } | |
| 290 | 251 | playService.stopAudioBroadcast(deviceId, channelId); |
| 291 | 252 | } |
| 292 | 253 | ... | ... |
web_src/config/index.js
| ... | ... | @@ -12,14 +12,14 @@ module.exports = { |
| 12 | 12 | assetsPublicPath: '/', |
| 13 | 13 | proxyTable: { |
| 14 | 14 | '/debug': { |
| 15 | - target: 'http://localhost:38080', | |
| 15 | + target: 'https://default.wvp-pro.cn:18080', | |
| 16 | 16 | changeOrigin: true, |
| 17 | 17 | pathRewrite: { |
| 18 | 18 | '^/debug': '/' |
| 19 | 19 | } |
| 20 | 20 | }, |
| 21 | 21 | '/static/snap': { |
| 22 | - target: 'http://localhost:38080', | |
| 22 | + target: 'https://default.wvp-pro.cn:18080', | |
| 23 | 23 | changeOrigin: true, |
| 24 | 24 | // pathRewrite: { |
| 25 | 25 | // '^/static/snap': '/static/snap' | ... | ... |
web_src/src/components/dialog/devicePlayer.vue
| 1 | 1 | <template> |
| 2 | 2 | <div id="devicePlayer" v-loading="isLoging"> |
| 3 | 3 | |
| 4 | - <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" :destroy-on-close="true" @close="close()"> | |
| 4 | + <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()"> | |
| 5 | 5 | <!-- <LivePlayer v-if="showVideoDialog" ref="videoPlayer" :videoUrl="videoUrl" :error="videoError" :message="videoError" :hasaudio="hasaudio" fluent autoplay live></LivePlayer> --> |
| 6 | 6 | <div style="width: 100%; height: 100%"> |
| 7 | 7 | <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer" v-if="Object.keys(this.player).length > 1"> |
| ... | ... | @@ -119,6 +119,10 @@ |
| 119 | 119 | <el-tag >RTC:</el-tag> |
| 120 | 120 | <span>{{ streamInfo.rtc }}</span> |
| 121 | 121 | </el-dropdown-item> |
| 122 | + <el-dropdown-item :command="streamInfo.rtcs"> | |
| 123 | + <el-tag >RTCS:</el-tag> | |
| 124 | + <span>{{ streamInfo.rtcs }}</span> | |
| 125 | + </el-dropdown-item> | |
| 122 | 126 | <el-dropdown-item :command="streamInfo.rtmp"> |
| 123 | 127 | <el-tag >RTMP:</el-tag> |
| 124 | 128 | <span>{{ streamInfo.rtmp }}</span> |
| ... | ... | @@ -875,7 +879,8 @@ export default { |
| 875 | 879 | } |
| 876 | 880 | }); |
| 877 | 881 | }else if (this.broadcastStatus === 1) { |
| 878 | - this.stopBroadcast() | |
| 882 | + this.broadcastStatus = -1; | |
| 883 | + this.broadcastRtc.close() | |
| 879 | 884 | } |
| 880 | 885 | }, |
| 881 | 886 | startBroadcast(url){ |
| ... | ... | @@ -890,6 +895,7 @@ export default { |
| 890 | 895 | message: "获取推流鉴权Key失败", |
| 891 | 896 | type: "error", |
| 892 | 897 | }); |
| 898 | + this.broadcastStatus = -1; | |
| 893 | 899 | }else { |
| 894 | 900 | let pushKey = res.data.data.pushKey; |
| 895 | 901 | // 获取推流鉴权KEY |
| ... | ... | @@ -923,6 +929,7 @@ export default { |
| 923 | 929 | message: '不支持webrtc, 无法进行语音对讲', |
| 924 | 930 | type: 'error' |
| 925 | 931 | }); |
| 932 | + this.broadcastStatus = -1; | |
| 926 | 933 | }); |
| 927 | 934 | |
| 928 | 935 | this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错 |
| ... | ... | @@ -932,6 +939,7 @@ export default { |
| 932 | 939 | message: 'ICE 协商出错', |
| 933 | 940 | type: 'error' |
| 934 | 941 | }); |
| 942 | + this.broadcastStatus = -1; | |
| 935 | 943 | }); |
| 936 | 944 | |
| 937 | 945 | this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败 |
| ... | ... | @@ -941,6 +949,7 @@ export default { |
| 941 | 949 | message: 'offer anwser 交换失败' + e, |
| 942 | 950 | type: 'error' |
| 943 | 951 | }); |
| 952 | + this.broadcastStatus = -1; | |
| 944 | 953 | }); |
| 945 | 954 | this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,(e)=>{// offer anwser 交换失败 |
| 946 | 955 | console.log('状态改变',e) |
| ... | ... | @@ -959,36 +968,38 @@ export default { |
| 959 | 968 | message: '捕获流失败' + e, |
| 960 | 969 | type: 'error' |
| 961 | 970 | }); |
| 971 | + this.broadcastStatus = -1; | |
| 962 | 972 | }); |
| 963 | 973 | } |
| 974 | + }).catch((e) => { | |
| 975 | + this.$message({ | |
| 976 | + showClose: true, | |
| 977 | + message: e, | |
| 978 | + type: 'error' | |
| 979 | + }); | |
| 980 | + this.broadcastStatus = -1; | |
| 964 | 981 | }); |
| 965 | 982 | |
| 966 | 983 | |
| 967 | 984 | }, |
| 968 | 985 | stopBroadcast(){ |
| 969 | - if (this.broadcastStatus === -1) { | |
| 970 | - this.broadcastStatus = 1; | |
| 971 | - }else { | |
| 972 | - this.broadcastStatus = -2; | |
| 973 | - this.broadcastRtc = null; | |
| 974 | - this.$axios({ | |
| 975 | - method: 'get', | |
| 976 | - url: '/api/play/broadcast/stop/' + this.deviceId + '/' + this.channelId | |
| 977 | - }).then( (res)=> { | |
| 978 | - if (res.data.code == 0) { | |
| 979 | - // this.broadcastStatus = -1; | |
| 980 | - // this.broadcastRtc.close() | |
| 981 | - }else { | |
| 982 | - this.$message({ | |
| 983 | - showClose: true, | |
| 984 | - message: res.data.msg, | |
| 985 | - type: "error", | |
| 986 | - }); | |
| 987 | - } | |
| 988 | - }); | |
| 989 | - } | |
| 990 | - | |
| 991 | - | |
| 986 | + this.broadcastRtc.close(); | |
| 987 | + this.broadcastStatus = -1; | |
| 988 | + this.$axios({ | |
| 989 | + method: 'get', | |
| 990 | + url: '/api/play/broadcast/stop/' + this.deviceId + '/' + this.channelId | |
| 991 | + }).then( (res)=> { | |
| 992 | + if (res.data.code == 0) { | |
| 993 | + // this.broadcastStatus = -1; | |
| 994 | + // this.broadcastRtc.close() | |
| 995 | + }else { | |
| 996 | + this.$message({ | |
| 997 | + showClose: true, | |
| 998 | + message: res.data.msg, | |
| 999 | + type: "error", | |
| 1000 | + }); | |
| 1001 | + } | |
| 1002 | + }); | |
| 992 | 1003 | } |
| 993 | 1004 | } |
| 994 | 1005 | }; | ... | ... |