Commit ef742e715b8c0a983d661aa5b5f8980dade8c790
1 parent
118e4288
优化宕机后点播中设备发送bye
Showing
5 changed files
with
98 additions
and
3 deletions
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
| ... | ... | @@ -21,6 +21,15 @@ public class StreamInfo { |
| 21 | 21 | private String rtc; |
| 22 | 22 | private JSONArray tracks; |
| 23 | 23 | |
| 24 | + public static class TransactionInfo{ | |
| 25 | + public String callId; | |
| 26 | + public String localTag; | |
| 27 | + public String remoteTag; | |
| 28 | + public String branch; | |
| 29 | + } | |
| 30 | + | |
| 31 | + private TransactionInfo transactionInfo; | |
| 32 | + | |
| 24 | 33 | public String getApp() { |
| 25 | 34 | return app; |
| 26 | 35 | } |
| ... | ... | @@ -148,4 +157,12 @@ public class StreamInfo { |
| 148 | 157 | public void setRtc(String rtc) { |
| 149 | 158 | this.rtc = rtc; |
| 150 | 159 | } |
| 160 | + | |
| 161 | + public TransactionInfo getTransactionInfo() { | |
| 162 | + return transactionInfo; | |
| 163 | + } | |
| 164 | + | |
| 165 | + public void setTransactionInfo(TransactionInfo transactionInfo) { | |
| 166 | + this.transactionInfo = transactionInfo; | |
| 167 | + } | |
| 151 | 168 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -135,6 +135,36 @@ public class SIPRequestHeaderProvider { |
| 135 | 135 | return request; |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | + public Request createByteRequest(Device device, String channelId, String viaTag, String fromTag, String toTag, String callId) throws ParseException, InvalidArgumentException, PeerUnavailableException { | |
| 139 | + Request request = null; | |
| 140 | + //请求行 | |
| 141 | + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); | |
| 142 | + // via | |
| 143 | + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); | |
| 144 | + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag); | |
| 145 | + viaHeaders.add(viaHeader); | |
| 146 | + //from | |
| 147 | + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); | |
| 148 | + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); | |
| 149 | + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack | |
| 150 | + //to | |
| 151 | + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); | |
| 152 | + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); | |
| 153 | + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,toTag); | |
| 154 | + | |
| 155 | + //Forwards | |
| 156 | + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); | |
| 157 | + | |
| 158 | + //ceq | |
| 159 | + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.BYE); | |
| 160 | + CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(callId); | |
| 161 | + request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); | |
| 162 | + | |
| 163 | + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); | |
| 164 | + | |
| 165 | + return request; | |
| 166 | + } | |
| 167 | + | |
| 138 | 168 | public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| 139 | 169 | Request request = null; |
| 140 | 170 | // sipuri | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -10,11 +10,14 @@ import javax.sip.header.CallIdHeader; |
| 10 | 10 | import javax.sip.header.ViaHeader; |
| 11 | 11 | import javax.sip.message.Request; |
| 12 | 12 | |
| 13 | +import com.alibaba.fastjson.JSON; | |
| 14 | +import com.alibaba.fastjson.JSONArray; | |
| 13 | 15 | import com.alibaba.fastjson.JSONObject; |
| 14 | 16 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 15 | 17 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 16 | 18 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 17 | 19 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 20 | +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | |
| 18 | 21 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 19 | 22 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 20 | 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| ... | ... | @@ -75,6 +78,9 @@ public class SIPCommander implements ISIPCommander { |
| 75 | 78 | @Autowired |
| 76 | 79 | private ZLMRTPServerFactory zlmrtpServerFactory; |
| 77 | 80 | |
| 81 | + @Autowired | |
| 82 | + private ZLMRESTfulUtils zlmresTfulUtils; | |
| 83 | + | |
| 78 | 84 | @Value("${media.rtp.enable}") |
| 79 | 85 | private boolean rtpEnable; |
| 80 | 86 | |
| ... | ... | @@ -577,13 +583,39 @@ public class SIPCommander implements ISIPCommander { |
| 577 | 583 | |
| 578 | 584 | try { |
| 579 | 585 | ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); |
| 580 | - // 服务重启后 | |
| 586 | + // 服务重启后, 无法直接发送bye, 通过手动构建发送 | |
| 581 | 587 | if (transaction == null) { |
| 588 | + | |
| 582 | 589 | StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); |
| 583 | 590 | if (streamInfo != null) { |
| 591 | + JSONObject mediaList = zlmresTfulUtils.getMediaList(streamInfo.getApp(), streamInfo.getStreamId()); | |
| 592 | + if (mediaList != null) { // 仍在推流才发送 | |
| 593 | + if (mediaList.getInteger("code") == 0) { | |
| 594 | + JSONArray data = mediaList.getJSONArray("data"); | |
| 595 | + if (data != null && data.size() > 0) { | |
| 596 | + Device device = storager.queryVideoDevice(deviceId); | |
| 597 | + if (device != null) { | |
| 598 | + StreamInfo.TransactionInfo transactionInfo = streamInfo.getTransactionInfo(); | |
| 599 | + try { | |
| 600 | + Request byteRequest = headerProvider.createByteRequest(device, channelId, | |
| 601 | + transactionInfo.branch, | |
| 602 | + transactionInfo.localTag, | |
| 603 | + transactionInfo.remoteTag, | |
| 604 | + transactionInfo.callId); | |
| 605 | + transmitRequest(device, byteRequest); | |
| 606 | + } catch (InvalidArgumentException e) { | |
| 607 | + e.printStackTrace(); | |
| 608 | + } | |
| 609 | + } | |
| 610 | + } | |
| 611 | + } | |
| 612 | + } | |
| 584 | 613 | redisCatchStorage.stopPlay(streamInfo); |
| 585 | 614 | } |
| 586 | - okEvent.response(null); | |
| 615 | + | |
| 616 | + if (okEvent != null) { | |
| 617 | + okEvent.response(null); | |
| 618 | + } | |
| 587 | 619 | return; |
| 588 | 620 | } |
| 589 | 621 | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; |
| 7 | 7 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 8 | 8 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 9 | 9 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 10 | +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | |
| 10 | 11 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 11 | 12 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 12 | 13 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| ... | ... | @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 17 | 18 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 18 | 19 | import com.genersoft.iot.vmp.service.IMediaService; |
| 19 | 20 | import com.genersoft.iot.vmp.service.IPlayService; |
| 21 | +import gov.nist.javax.sip.stack.SIPDialog; | |
| 20 | 22 | import org.slf4j.Logger; |
| 21 | 23 | import org.slf4j.LoggerFactory; |
| 22 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -24,6 +26,9 @@ import org.springframework.http.ResponseEntity; |
| 24 | 26 | import org.springframework.stereotype.Service; |
| 25 | 27 | import org.springframework.web.context.request.async.DeferredResult; |
| 26 | 28 | |
| 29 | +import javax.sip.ClientTransaction; | |
| 30 | +import javax.sip.Dialog; | |
| 31 | +import javax.sip.header.CallIdHeader; | |
| 27 | 32 | import javax.sip.message.Response; |
| 28 | 33 | import java.util.UUID; |
| 29 | 34 | |
| ... | ... | @@ -50,6 +55,9 @@ public class PlayServiceImpl implements IPlayService { |
| 50 | 55 | @Autowired |
| 51 | 56 | private IMediaService mediaService; |
| 52 | 57 | |
| 58 | + @Autowired | |
| 59 | + private VideoStreamSessionManager streamSession; | |
| 60 | + | |
| 53 | 61 | |
| 54 | 62 | @Override |
| 55 | 63 | public PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { |
| ... | ... | @@ -141,7 +149,14 @@ public class PlayServiceImpl implements IPlayService { |
| 141 | 149 | deviceChannel.setStreamId(streamInfo.getStreamId()); |
| 142 | 150 | storager.startPlay(deviceId, channelId, streamInfo.getStreamId()); |
| 143 | 151 | } |
| 144 | - | |
| 152 | + ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); | |
| 153 | + SIPDialog dialog = (SIPDialog)transaction.getDialog(); | |
| 154 | + StreamInfo.TransactionInfo transactionInfo = new StreamInfo.TransactionInfo(); | |
| 155 | + transactionInfo.callId = dialog.getCallId().getCallId(); | |
| 156 | + transactionInfo.localTag = dialog.getLocalTag(); | |
| 157 | + transactionInfo.remoteTag = dialog.getRemoteTag(); | |
| 158 | + transactionInfo.branch = dialog.getFirstTransactionInt().getBranchId(); | |
| 159 | + streamInfo.setTransactionInfo(transactionInfo); | |
| 145 | 160 | redisCatchStorage.startPlay(streamInfo); |
| 146 | 161 | msg.setData(JSON.toJSONString(streamInfo)); |
| 147 | 162 | resultHolder.invokeResult(msg); | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
| ... | ... | @@ -102,6 +102,7 @@ public class PlayController { |
| 102 | 102 | msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); |
| 103 | 103 | msg.setData("点播未找到"); |
| 104 | 104 | resultHolder.invokeResult(msg); |
| 105 | + storager.stopPlay(deviceId, channelId); | |
| 105 | 106 | }else { |
| 106 | 107 | redisCatchStorage.stopPlay(streamInfo); |
| 107 | 108 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | ... | ... |