Commit dbcd050c66388bd9c8d9fdcf843458e38273b785
Merge remote-tracking branch 'origin/wvp-28181-2.0' into wvp-28181-2.0
Showing
5 changed files
with
291 additions
and
0 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.common.StreamInfo; | |
| 3 | 4 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 4 | 5 | import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; |
| 5 | 6 | import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; |
| ... | ... | @@ -122,6 +123,26 @@ public interface ISIPCommander { |
| 122 | 123 | void streamByeCmd(String deviceId, String channelId); |
| 123 | 124 | |
| 124 | 125 | /** |
| 126 | + * 回放暂停 | |
| 127 | + */ | |
| 128 | + void playPauseCmd(Device device, StreamInfo streamInfo); | |
| 129 | + | |
| 130 | + /** | |
| 131 | + * 回放恢复 | |
| 132 | + */ | |
| 133 | + void playResumeCmd(Device device, StreamInfo streamInfo); | |
| 134 | + | |
| 135 | + /** | |
| 136 | + * 回放拖动播放 | |
| 137 | + */ | |
| 138 | + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime); | |
| 139 | + | |
| 140 | + /** | |
| 141 | + * 回放倍速播放 | |
| 142 | + */ | |
| 143 | + void playSpeedCmd(Device device, StreamInfo streamInfo, String speed); | |
| 144 | + | |
| 145 | + /** | |
| 125 | 146 | * 语音广播 |
| 126 | 147 | * |
| 127 | 148 | * @param device 视频设备 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; |
| 3 | 3 | import java.text.ParseException; |
| 4 | 4 | import java.util.ArrayList; |
| 5 | 5 | |
| 6 | +import javax.sip.Dialog; | |
| 6 | 7 | import javax.sip.InvalidArgumentException; |
| 7 | 8 | import javax.sip.PeerUnavailableException; |
| 8 | 9 | import javax.sip.SipFactory; |
| ... | ... | @@ -11,6 +12,9 @@ import javax.sip.address.SipURI; |
| 11 | 12 | import javax.sip.header.*; |
| 12 | 13 | import javax.sip.message.Request; |
| 13 | 14 | |
| 15 | +import com.genersoft.iot.vmp.common.StreamInfo; | |
| 16 | +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; | |
| 17 | +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 14 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
| 15 | 19 | import org.springframework.stereotype.Component; |
| 16 | 20 | |
| ... | ... | @@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider { |
| 30 | 34 | |
| 31 | 35 | @Autowired |
| 32 | 36 | private SipFactory sipFactory; |
| 37 | + | |
| 38 | + @Autowired | |
| 39 | + private VideoStreamSessionManager streamSession; | |
| 33 | 40 | |
| 34 | 41 | public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| 35 | 42 | Request request = null; |
| ... | ... | @@ -210,4 +217,50 @@ public class SIPRequestHeaderProvider { |
| 210 | 217 | request.setContent(content, contentTypeHeader); |
| 211 | 218 | return request; |
| 212 | 219 | } |
| 220 | + | |
| 221 | + public Request createInfoRequest(Device device, StreamInfo streamInfo, String content) | |
| 222 | + throws PeerUnavailableException, ParseException, InvalidArgumentException { | |
| 223 | + Request request = null; | |
| 224 | + Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
| 225 | + | |
| 226 | + SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), | |
| 227 | + device.getHostAddress()); | |
| 228 | + // via | |
| 229 | + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); | |
| 230 | + ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), | |
| 231 | + device.getTransport(), null); | |
| 232 | + viaHeader.setRPort(); | |
| 233 | + viaHeaders.add(viaHeader); | |
| 234 | + // from | |
| 235 | + SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), | |
| 236 | + sipConfig.getDomain()); | |
| 237 | + Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); | |
| 238 | + FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, dialog.getLocalTag()); | |
| 239 | + // to | |
| 240 | + SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(), | |
| 241 | + sipConfig.getDomain()); | |
| 242 | + Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); | |
| 243 | + ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag()); | |
| 244 | + | |
| 245 | + // callid | |
| 246 | + CallIdHeader callIdHeader = dialog.getCallId(); | |
| 247 | + | |
| 248 | + // Forwards | |
| 249 | + MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); | |
| 250 | + | |
| 251 | + // ceq | |
| 252 | + CSeqHeader cSeqHeader = sipFactory.createHeaderFactory() | |
| 253 | + .createCSeqHeader(InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()), Request.INFO); | |
| 254 | + | |
| 255 | + request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader, | |
| 256 | + fromHeader, toHeader, viaHeaders, maxForwards); | |
| 257 | + Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory() | |
| 258 | + .createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort())); | |
| 259 | + request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); | |
| 260 | + | |
| 261 | + ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", | |
| 262 | + "MANSRTSP"); | |
| 263 | + request.setContent(content, contentTypeHeader); | |
| 264 | + return request; | |
| 265 | + } | |
| 213 | 266 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.common.StreamInfo; | |
| 4 | 5 | import com.genersoft.iot.vmp.conf.SipConfig; |
| 5 | 6 | import com.genersoft.iot.vmp.conf.UserSetup; |
| 6 | 7 | import com.genersoft.iot.vmp.gb28181.bean.Device; |
| ... | ... | @@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService; |
| 17 | 18 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 18 | 19 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 19 | 20 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 21 | +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 20 | 22 | import gov.nist.javax.sip.SipProviderImpl; |
| 21 | 23 | import gov.nist.javax.sip.SipStackImpl; |
| 22 | 24 | import gov.nist.javax.sip.message.SIPRequest; |
| ... | ... | @@ -1543,4 +1545,110 @@ public class SIPCommander implements ISIPCommander { |
| 1543 | 1545 | clientTransaction.sendRequest(); |
| 1544 | 1546 | return clientTransaction; |
| 1545 | 1547 | } |
| 1548 | + | |
| 1549 | + /** | |
| 1550 | + * 回放暂停 | |
| 1551 | + */ | |
| 1552 | + @Override | |
| 1553 | + public void playPauseCmd(Device device, StreamInfo streamInfo) { | |
| 1554 | + try { | |
| 1555 | + | |
| 1556 | + StringBuffer content = new StringBuffer(200); | |
| 1557 | + content.append("PAUSE RTSP/1.0\r\n"); | |
| 1558 | + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1559 | + content.append("PauseTime: now\r\n"); | |
| 1560 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1561 | + logger.info(request.toString()); | |
| 1562 | + ClientTransaction clientTransaction = null; | |
| 1563 | + if ("TCP".equals(device.getTransport())) { | |
| 1564 | + clientTransaction = tcpSipProvider.getNewClientTransaction(request); | |
| 1565 | + } else if ("UDP".equals(device.getTransport())) { | |
| 1566 | + clientTransaction = udpSipProvider.getNewClientTransaction(request); | |
| 1567 | + } | |
| 1568 | + if (clientTransaction != null) { | |
| 1569 | + clientTransaction.sendRequest(); | |
| 1570 | + } | |
| 1571 | + | |
| 1572 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 1573 | + e.printStackTrace(); | |
| 1574 | + } | |
| 1575 | + } | |
| 1576 | + | |
| 1577 | + /** | |
| 1578 | + * 回放恢复 | |
| 1579 | + */ | |
| 1580 | + @Override | |
| 1581 | + public void playResumeCmd(Device device, StreamInfo streamInfo) { | |
| 1582 | + try { | |
| 1583 | + StringBuffer content = new StringBuffer(200); | |
| 1584 | + content.append("PLAY RTSP/1.0\r\n"); | |
| 1585 | + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1586 | + content.append("Range: npt=now-\r\n"); | |
| 1587 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1588 | + logger.info(request.toString()); | |
| 1589 | + ClientTransaction clientTransaction = null; | |
| 1590 | + if ("TCP".equals(device.getTransport())) { | |
| 1591 | + clientTransaction = tcpSipProvider.getNewClientTransaction(request); | |
| 1592 | + } else if ("UDP".equals(device.getTransport())) { | |
| 1593 | + clientTransaction = udpSipProvider.getNewClientTransaction(request); | |
| 1594 | + } | |
| 1595 | + | |
| 1596 | + clientTransaction.sendRequest(); | |
| 1597 | + | |
| 1598 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 1599 | + e.printStackTrace(); | |
| 1600 | + } | |
| 1601 | + } | |
| 1602 | + | |
| 1603 | + /** | |
| 1604 | + * 回放拖动播放 | |
| 1605 | + */ | |
| 1606 | + @Override | |
| 1607 | + public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) { | |
| 1608 | + try { | |
| 1609 | + StringBuffer content = new StringBuffer(200); | |
| 1610 | + content.append("PLAY RTSP/1.0\r\n"); | |
| 1611 | + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1612 | + content.append("Range: npt=" + seekTime + "-\r\n"); | |
| 1613 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1614 | + logger.info(request.toString()); | |
| 1615 | + ClientTransaction clientTransaction = null; | |
| 1616 | + if ("TCP".equals(device.getTransport())) { | |
| 1617 | + clientTransaction = tcpSipProvider.getNewClientTransaction(request); | |
| 1618 | + } else if ("UDP".equals(device.getTransport())) { | |
| 1619 | + clientTransaction = udpSipProvider.getNewClientTransaction(request); | |
| 1620 | + } | |
| 1621 | + | |
| 1622 | + clientTransaction.sendRequest(); | |
| 1623 | + | |
| 1624 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 1625 | + e.printStackTrace(); | |
| 1626 | + } | |
| 1627 | + } | |
| 1628 | + | |
| 1629 | + /** | |
| 1630 | + * 回放倍速播放 | |
| 1631 | + */ | |
| 1632 | + @Override | |
| 1633 | + public void playSpeedCmd(Device device, StreamInfo streamInfo, String speed) { | |
| 1634 | + try { | |
| 1635 | + StringBuffer content = new StringBuffer(200); | |
| 1636 | + content.append("PLAY RTSP/1.0\r\n"); | |
| 1637 | + content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1638 | + content.append("Scale: " + speed + ".000000\r\n"); | |
| 1639 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1640 | + logger.info(request.toString()); | |
| 1641 | + ClientTransaction clientTransaction = null; | |
| 1642 | + if ("TCP".equals(device.getTransport())) { | |
| 1643 | + clientTransaction = tcpSipProvider.getNewClientTransaction(request); | |
| 1644 | + } else if ("UDP".equals(device.getTransport())) { | |
| 1645 | + clientTransaction = udpSipProvider.getNewClientTransaction(request); | |
| 1646 | + } | |
| 1647 | + | |
| 1648 | + clientTransaction.sendRequest(); | |
| 1649 | + | |
| 1650 | + } catch (SipException | ParseException | InvalidArgumentException e) { | |
| 1651 | + e.printStackTrace(); | |
| 1652 | + } | |
| 1653 | + } | |
| 1546 | 1654 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
| ... | ... | @@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService; |
| 9 | 9 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 10 | 10 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 11 | 11 | import com.genersoft.iot.vmp.service.IPlayService; |
| 12 | +import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 12 | 13 | import io.swagger.annotations.Api; |
| 13 | 14 | import io.swagger.annotations.ApiImplicitParam; |
| 14 | 15 | import io.swagger.annotations.ApiImplicitParams; |
| ... | ... | @@ -152,4 +153,98 @@ public class PlaybackController { |
| 152 | 153 | return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
| 153 | 154 | } |
| 154 | 155 | } |
| 156 | + | |
| 157 | + @ApiOperation("回放暂停") | |
| 158 | + @ApiImplicitParams({ | |
| 159 | + @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), | |
| 160 | + }) | |
| 161 | + @GetMapping("/pause/{streamId}") | |
| 162 | + public ResponseEntity<String> playPause(@PathVariable String streamId) { | |
| 163 | + logger.info("playPause: "+streamId); | |
| 164 | + JSONObject json = new JSONObject(); | |
| 165 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 166 | + if (null == streamInfo) { | |
| 167 | + json.put("msg", "streamId不存在"); | |
| 168 | + logger.warn("streamId不存在!"); | |
| 169 | + return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); | |
| 170 | + } | |
| 171 | + setCseq(streamId); | |
| 172 | + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); | |
| 173 | + cmder.playPauseCmd(device, streamInfo); | |
| 174 | + json.put("msg", "ok"); | |
| 175 | + return new ResponseEntity<String>(json.toString(), HttpStatus.OK); | |
| 176 | + } | |
| 177 | + | |
| 178 | + @ApiOperation("回放恢复") | |
| 179 | + @ApiImplicitParams({ | |
| 180 | + @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), | |
| 181 | + }) | |
| 182 | + @GetMapping("/resume/{streamId}") | |
| 183 | + public ResponseEntity<String> playResume(@PathVariable String streamId) { | |
| 184 | + logger.info("playResume: "+streamId); | |
| 185 | + JSONObject json = new JSONObject(); | |
| 186 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 187 | + if (null == streamInfo) { | |
| 188 | + json.put("msg", "streamId不存在"); | |
| 189 | + logger.warn("streamId不存在!"); | |
| 190 | + return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); | |
| 191 | + } | |
| 192 | + setCseq(streamId); | |
| 193 | + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); | |
| 194 | + cmder.playResumeCmd(device, streamInfo); | |
| 195 | + json.put("msg", "ok"); | |
| 196 | + return new ResponseEntity<String>(json.toString(), HttpStatus.OK); | |
| 197 | + } | |
| 198 | + | |
| 199 | + @ApiOperation("回放拖动播放") | |
| 200 | + @ApiImplicitParams({ | |
| 201 | + @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), | |
| 202 | + @ApiImplicitParam(name = "seekTime", value = "拖动偏移量,单位s", dataTypeClass = Long.class), | |
| 203 | + }) | |
| 204 | + @GetMapping("/seek/{streamId}/{seekTime}") | |
| 205 | + public ResponseEntity<String> playSeek(@PathVariable String streamId, @PathVariable long seekTime) { | |
| 206 | + logger.info("playSeek: "+streamId+", "+seekTime); | |
| 207 | + JSONObject json = new JSONObject(); | |
| 208 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 209 | + if (null == streamInfo) { | |
| 210 | + json.put("msg", "streamId不存在"); | |
| 211 | + logger.warn("streamId不存在!"); | |
| 212 | + return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); | |
| 213 | + } | |
| 214 | + setCseq(streamId); | |
| 215 | + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); | |
| 216 | + cmder.playSeekCmd(device, streamInfo, seekTime); | |
| 217 | + json.put("msg", "ok"); | |
| 218 | + return new ResponseEntity<String>(json.toString(), HttpStatus.OK); | |
| 219 | + } | |
| 220 | + | |
| 221 | + @ApiOperation("回放倍速播放") | |
| 222 | + @ApiImplicitParams({ | |
| 223 | + @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), | |
| 224 | + @ApiImplicitParam(name = "speed", value = "倍速 1、2、4", dataTypeClass = String.class), | |
| 225 | + }) | |
| 226 | + @GetMapping("/speed/{streamId}/{speed}") | |
| 227 | + public ResponseEntity<String> playSpeed(@PathVariable String streamId, @PathVariable String speed) { | |
| 228 | + logger.info("playSpeed: "+streamId+", "+speed); | |
| 229 | + JSONObject json = new JSONObject(); | |
| 230 | + StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); | |
| 231 | + if (null == streamInfo) { | |
| 232 | + json.put("msg", "streamId不存在"); | |
| 233 | + logger.warn("streamId不存在!"); | |
| 234 | + return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); | |
| 235 | + } | |
| 236 | + setCseq(streamId); | |
| 237 | + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); | |
| 238 | + cmder.playSpeedCmd(device, streamInfo, speed); | |
| 239 | + json.put("msg", "ok"); | |
| 240 | + return new ResponseEntity<String>(json.toString(), HttpStatus.OK); | |
| 241 | + } | |
| 242 | + | |
| 243 | + public void setCseq(String streamId) { | |
| 244 | + if (InfoCseqCache.CSEQCACHE.containsKey(streamId)) { | |
| 245 | + InfoCseqCache.CSEQCACHE.put(streamId, InfoCseqCache.CSEQCACHE.get(streamId) + 1); | |
| 246 | + } else { | |
| 247 | + InfoCseqCache.CSEQCACHE.put(streamId, 2L); | |
| 248 | + } | |
| 249 | + } | |
| 155 | 250 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/InfoCseqCache.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.vmanager.gb28181.session; | |
| 2 | + | |
| 3 | +import java.util.Map; | |
| 4 | +import java.util.concurrent.ConcurrentHashMap; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * @ClassName: InfoCseqCache | |
| 8 | + * @Description: INFO类型的Sip中cseq的缓存 | |
| 9 | + */ | |
| 10 | +public class InfoCseqCache { | |
| 11 | + | |
| 12 | + public static Map<String, Long> CSEQCACHE = new ConcurrentHashMap<>(); | |
| 13 | + | |
| 14 | +} | |
| 0 | 15 | \ No newline at end of file | ... | ... |