Commit 6ed563e4b4e8387fc397d53c0533009d5a8866a2

Authored by 648540858
Committed by GitHub
2 parents dda68ac0 4142601a

Merge pull request #243 from chenparty/wvp-28181-2.0

增加回放控制功能(回放暂停、回放恢复、回放拖动播放、回放倍速播放)
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
... ...