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,6 +21,15 @@ public class StreamInfo { | ||
| 21 | private String rtc; | 21 | private String rtc; |
| 22 | private JSONArray tracks; | 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 | public String getApp() { | 33 | public String getApp() { |
| 25 | return app; | 34 | return app; |
| 26 | } | 35 | } |
| @@ -148,4 +157,12 @@ public class StreamInfo { | @@ -148,4 +157,12 @@ public class StreamInfo { | ||
| 148 | public void setRtc(String rtc) { | 157 | public void setRtc(String rtc) { |
| 149 | this.rtc = rtc; | 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,6 +135,36 @@ public class SIPRequestHeaderProvider { | ||
| 135 | return request; | 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 | public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { | 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 | Request request = null; | 169 | Request request = null; |
| 140 | // sipuri | 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,11 +10,14 @@ import javax.sip.header.CallIdHeader; | ||
| 10 | import javax.sip.header.ViaHeader; | 10 | import javax.sip.header.ViaHeader; |
| 11 | import javax.sip.message.Request; | 11 | import javax.sip.message.Request; |
| 12 | 12 | ||
| 13 | +import com.alibaba.fastjson.JSON; | ||
| 14 | +import com.alibaba.fastjson.JSONArray; | ||
| 13 | import com.alibaba.fastjson.JSONObject; | 15 | import com.alibaba.fastjson.JSONObject; |
| 14 | import com.genersoft.iot.vmp.common.StreamInfo; | 16 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 15 | import com.genersoft.iot.vmp.conf.MediaServerConfig; | 17 | import com.genersoft.iot.vmp.conf.MediaServerConfig; |
| 16 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 18 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 17 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; | 19 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| 20 | +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; | ||
| 18 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; | 21 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| 19 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 22 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 20 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 23 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| @@ -75,6 +78,9 @@ public class SIPCommander implements ISIPCommander { | @@ -75,6 +78,9 @@ public class SIPCommander implements ISIPCommander { | ||
| 75 | @Autowired | 78 | @Autowired |
| 76 | private ZLMRTPServerFactory zlmrtpServerFactory; | 79 | private ZLMRTPServerFactory zlmrtpServerFactory; |
| 77 | 80 | ||
| 81 | + @Autowired | ||
| 82 | + private ZLMRESTfulUtils zlmresTfulUtils; | ||
| 83 | + | ||
| 78 | @Value("${media.rtp.enable}") | 84 | @Value("${media.rtp.enable}") |
| 79 | private boolean rtpEnable; | 85 | private boolean rtpEnable; |
| 80 | 86 | ||
| @@ -577,13 +583,39 @@ public class SIPCommander implements ISIPCommander { | @@ -577,13 +583,39 @@ public class SIPCommander implements ISIPCommander { | ||
| 577 | 583 | ||
| 578 | try { | 584 | try { |
| 579 | ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); | 585 | ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); |
| 580 | - // 服务重启后 | 586 | + // 服务重启后, 无法直接发送bye, 通过手动构建发送 |
| 581 | if (transaction == null) { | 587 | if (transaction == null) { |
| 588 | + | ||
| 582 | StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); | 589 | StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); |
| 583 | if (streamInfo != null) { | 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 | redisCatchStorage.stopPlay(streamInfo); | 613 | redisCatchStorage.stopPlay(streamInfo); |
| 585 | } | 614 | } |
| 586 | - okEvent.response(null); | 615 | + |
| 616 | + if (okEvent != null) { | ||
| 617 | + okEvent.response(null); | ||
| 618 | + } | ||
| 587 | return; | 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,6 +7,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; | ||
| 7 | import com.genersoft.iot.vmp.gb28181.bean.Device; | 7 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 8 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; | 8 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 9 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; | 9 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 10 | +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | ||
| 10 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | 11 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 11 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | 12 | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| 12 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | 13 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | ||
| 17 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; | 18 | import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; |
| 18 | import com.genersoft.iot.vmp.service.IMediaService; | 19 | import com.genersoft.iot.vmp.service.IMediaService; |
| 19 | import com.genersoft.iot.vmp.service.IPlayService; | 20 | import com.genersoft.iot.vmp.service.IPlayService; |
| 21 | +import gov.nist.javax.sip.stack.SIPDialog; | ||
| 20 | import org.slf4j.Logger; | 22 | import org.slf4j.Logger; |
| 21 | import org.slf4j.LoggerFactory; | 23 | import org.slf4j.LoggerFactory; |
| 22 | import org.springframework.beans.factory.annotation.Autowired; | 24 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -24,6 +26,9 @@ import org.springframework.http.ResponseEntity; | @@ -24,6 +26,9 @@ import org.springframework.http.ResponseEntity; | ||
| 24 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
| 25 | import org.springframework.web.context.request.async.DeferredResult; | 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 | import javax.sip.message.Response; | 32 | import javax.sip.message.Response; |
| 28 | import java.util.UUID; | 33 | import java.util.UUID; |
| 29 | 34 | ||
| @@ -50,6 +55,9 @@ public class PlayServiceImpl implements IPlayService { | @@ -50,6 +55,9 @@ public class PlayServiceImpl implements IPlayService { | ||
| 50 | @Autowired | 55 | @Autowired |
| 51 | private IMediaService mediaService; | 56 | private IMediaService mediaService; |
| 52 | 57 | ||
| 58 | + @Autowired | ||
| 59 | + private VideoStreamSessionManager streamSession; | ||
| 60 | + | ||
| 53 | 61 | ||
| 54 | @Override | 62 | @Override |
| 55 | public PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { | 63 | public PlayResult play(String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { |
| @@ -141,7 +149,14 @@ public class PlayServiceImpl implements IPlayService { | @@ -141,7 +149,14 @@ public class PlayServiceImpl implements IPlayService { | ||
| 141 | deviceChannel.setStreamId(streamInfo.getStreamId()); | 149 | deviceChannel.setStreamId(streamInfo.getStreamId()); |
| 142 | storager.startPlay(deviceId, channelId, streamInfo.getStreamId()); | 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 | redisCatchStorage.startPlay(streamInfo); | 160 | redisCatchStorage.startPlay(streamInfo); |
| 146 | msg.setData(JSON.toJSONString(streamInfo)); | 161 | msg.setData(JSON.toJSONString(streamInfo)); |
| 147 | resultHolder.invokeResult(msg); | 162 | resultHolder.invokeResult(msg); |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
| @@ -102,6 +102,7 @@ public class PlayController { | @@ -102,6 +102,7 @@ public class PlayController { | ||
| 102 | msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); | 102 | msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid); |
| 103 | msg.setData("点播未找到"); | 103 | msg.setData("点播未找到"); |
| 104 | resultHolder.invokeResult(msg); | 104 | resultHolder.invokeResult(msg); |
| 105 | + storager.stopPlay(deviceId, channelId); | ||
| 105 | }else { | 106 | }else { |
| 106 | redisCatchStorage.stopPlay(streamInfo); | 107 | redisCatchStorage.stopPlay(streamInfo); |
| 107 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | 108 | storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); |