Commit 805fa42a2c010d92b1c8fc29a60d32fd99fb4ea3
1 parent
1b6f0137
修复回放、停止预览bug,增加ZLM hook监听,长时间无人观看,停止摄像头推流。需开启ZLM的hook并配置url
Showing
7 changed files
with
251 additions
and
100 deletions
pom.xml
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java
| ... | ... | @@ -22,7 +22,7 @@ public class SsrcUtil { |
| 22 | 22 | |
| 23 | 23 | private static void init() { |
| 24 | 24 | SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig"); |
| 25 | - ssrcPrefix = sipConfig.getSipDomain().substring(4, 9); | |
| 25 | + ssrcPrefix = sipConfig.getSipDomain().substring(3, 8); | |
| 26 | 26 | isUsed = new ArrayList<String>(); |
| 27 | 27 | notUsed = new ArrayList<String>(); |
| 28 | 28 | for (int i = 1; i < 10000; i++) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -24,7 +24,7 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; |
| 24 | 24 | import com.genersoft.iot.vmp.gb28181.bean.Host; |
| 25 | 25 | |
| 26 | 26 | /** |
| 27 | - * @Description:TODO(这里用一句话描述这个类的作用) | |
| 27 | + * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化 | |
| 28 | 28 | * @author: songww |
| 29 | 29 | * @date: 2020年5月6日 上午9:29:02 |
| 30 | 30 | */ |
| ... | ... | @@ -72,50 +72,50 @@ public class SIPRequestHeaderProvider { |
| 72 | 72 | return request; |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | -// public Request createInviteRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException { | |
| 76 | -// Request request = null; | |
| 77 | -// Host host = device.getHost(); | |
| 78 | -// //请求行 | |
| 79 | -// SipURI requestLine = layer.getAddressFactory().createSipURI(device.getDeviceId(), host.getAddress()); | |
| 80 | -// //via | |
| 81 | -// ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); | |
| 82 | -// ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag); | |
| 83 | -// viaHeader.setRPort(); | |
| 84 | -// viaHeaders.add(viaHeader); | |
| 85 | -// //from | |
| 86 | -// SipURI fromSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),sipConfig.getSipIp()+":"+sipConfig.getSipPort()); | |
| 87 | -// Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI); | |
| 88 | -// FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack | |
| 89 | -// //to | |
| 90 | -// SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),host.getAddress()); | |
| 91 | -// Address toAddress = layer.getAddressFactory().createAddress(toSipURI); | |
| 92 | -// ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null); | |
| 93 | -// | |
| 94 | -// //callid | |
| 95 | -// CallIdHeader callIdHeader = null; | |
| 96 | -// if(device.getTransport().equals("TCP")) { | |
| 97 | -// callIdHeader = layer.getTcpSipProvider().getNewCallId(); | |
| 98 | -// } | |
| 99 | -// if(device.getTransport().equals("UDP")) { | |
| 100 | -// callIdHeader = layer.getUdpSipProvider().getNewCallId(); | |
| 101 | -// } | |
| 102 | -// | |
| 103 | -// //Forwards | |
| 104 | -// MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70); | |
| 105 | -// | |
| 106 | -// //ceq | |
| 107 | -// CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.INVITE); | |
| 108 | -// request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); | |
| 109 | -// | |
| 110 | -// Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); | |
| 111 | -// request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress)); | |
| 112 | -// | |
| 113 | -// ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP"); | |
| 114 | -// request.setContent(content, contentTypeHeader); | |
| 115 | -// return request; | |
| 116 | -// } | |
| 75 | + public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException { | |
| 76 | + Request request = null; | |
| 77 | + Host host = device.getHost(); | |
| 78 | + //请求行 | |
| 79 | + SipURI requestLine = layer.getAddressFactory().createSipURI(channelId, host.getAddress()); | |
| 80 | + //via | |
| 81 | + ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); | |
| 82 | + ViaHeader viaHeader = layer.getHeaderFactory().createViaHeader(sipConfig.getSipIp(), sipConfig.getSipPort(), device.getTransport(), viaTag); | |
| 83 | + viaHeader.setRPort(); | |
| 84 | + viaHeaders.add(viaHeader); | |
| 85 | + //from | |
| 86 | + SipURI fromSipURI = layer.getAddressFactory().createSipURI(sipConfig.getSipId(),sipConfig.getSipDomain()); | |
| 87 | + Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI); | |
| 88 | + FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack | |
| 89 | + //to | |
| 90 | + SipURI toSipURI = layer.getAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); | |
| 91 | + Address toAddress = layer.getAddressFactory().createAddress(toSipURI); | |
| 92 | + ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null); | |
| 93 | + | |
| 94 | + //callid | |
| 95 | + CallIdHeader callIdHeader = null; | |
| 96 | + if(device.getTransport().equals("TCP")) { | |
| 97 | + callIdHeader = layer.getTcpSipProvider().getNewCallId(); | |
| 98 | + } | |
| 99 | + if(device.getTransport().equals("UDP")) { | |
| 100 | + callIdHeader = layer.getUdpSipProvider().getNewCallId(); | |
| 101 | + } | |
| 102 | + | |
| 103 | + //Forwards | |
| 104 | + MaxForwardsHeader maxForwards = layer.getHeaderFactory().createMaxForwardsHeader(70); | |
| 105 | + | |
| 106 | + //ceq | |
| 107 | + CSeqHeader cSeqHeader = layer.getHeaderFactory().createCSeqHeader(1L, Request.INVITE); | |
| 108 | + request = layer.getMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); | |
| 109 | + | |
| 110 | + Address concatAddress = layer.getAddressFactory().createAddress(layer.getAddressFactory().createSipURI(sipConfig.getSipId(), sipConfig.getSipIp()+":"+sipConfig.getSipPort())); | |
| 111 | + request.addHeader(layer.getHeaderFactory().createContactHeader(concatAddress)); | |
| 112 | + | |
| 113 | + ContentTypeHeader contentTypeHeader = layer.getHeaderFactory().createContentTypeHeader("Application", "SDP"); | |
| 114 | + request.setContent(content, contentTypeHeader); | |
| 115 | + return request; | |
| 116 | + } | |
| 117 | 117 | |
| 118 | - public Request createInviteRequest(Device device, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException { | |
| 118 | + public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException { | |
| 119 | 119 | Request request = null; |
| 120 | 120 | Host host = device.getHost(); |
| 121 | 121 | //请求行 |
| ... | ... | @@ -130,7 +130,7 @@ public class SIPRequestHeaderProvider { |
| 130 | 130 | Address fromAddress = layer.getAddressFactory().createAddress(fromSipURI); |
| 131 | 131 | FromHeader fromHeader = layer.getHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack |
| 132 | 132 | //to |
| 133 | - SipURI toSipURI = layer.getAddressFactory().createSipURI(device.getDeviceId(),sipConfig.getSipDomain()); | |
| 133 | + SipURI toSipURI = layer.getAddressFactory().createSipURI(channelId,sipConfig.getSipDomain()); | |
| 134 | 134 | Address toAddress = layer.getAddressFactory().createAddress(toSipURI); |
| 135 | 135 | ToHeader toHeader = layer.getHeaderFactory().createToHeader(toAddress,null); |
| 136 | 136 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -142,30 +142,31 @@ public class SIPCommander implements ISIPCommander { |
| 142 | 142 | try { |
| 143 | 143 | |
| 144 | 144 | String ssrc = streamSession.createPlaySsrc(); |
| 145 | + String transport = device.getTransport(); | |
| 145 | 146 | // |
| 146 | 147 | StringBuffer content = new StringBuffer(200); |
| 147 | 148 | content.append("v=0\r\n"); |
| 148 | - content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getMediaIp()+"\r\n"); | |
| 149 | + content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); | |
| 149 | 150 | content.append("s=Play\r\n"); |
| 150 | 151 | content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n"); |
| 151 | 152 | content.append("t=0 0\r\n"); |
| 152 | - if(device.getTransport().equals("TCP")) { | |
| 153 | + if("TCP".equals(transport)) { | |
| 153 | 154 | content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n"); |
| 154 | 155 | } |
| 155 | - if(device.getTransport().equals("UDP")) { | |
| 156 | + if("UDP".equals(transport)) { | |
| 156 | 157 | content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n"); |
| 157 | 158 | } |
| 158 | - content.append("a=sendrecv\r\n"); | |
| 159 | + content.append("a=recvonly\r\n"); | |
| 159 | 160 | content.append("a=rtpmap:96 PS/90000\r\n"); |
| 160 | 161 | content.append("a=rtpmap:98 H264/90000\r\n"); |
| 161 | 162 | content.append("a=rtpmap:97 MPEG4/90000\r\n"); |
| 162 | - if(device.getTransport().equals("TCP")){ | |
| 163 | + if("TCP".equals(transport)){ | |
| 163 | 164 | content.append("a=setup:passive\r\n"); |
| 164 | 165 | content.append("a=connection:new\r\n"); |
| 165 | 166 | } |
| 166 | 167 | content.append("y="+ssrc+"\r\n");//ssrc |
| 167 | 168 | |
| 168 | - Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null); | |
| 169 | + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "live", null); | |
| 169 | 170 | |
| 170 | 171 | ClientTransaction transaction = transmitRequest(device, request); |
| 171 | 172 | streamSession.put(ssrc, transaction); |
| ... | ... | @@ -192,9 +193,9 @@ public class SIPCommander implements ISIPCommander { |
| 192 | 193 | // |
| 193 | 194 | StringBuffer content = new StringBuffer(200); |
| 194 | 195 | content.append("v=0\r\n"); |
| 195 | - content.append("o="+device.getDeviceId()+" 0 0 IN IP4 "+sipConfig.getMediaIp()+"\r\n"); | |
| 196 | + content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); | |
| 196 | 197 | content.append("s=Playback\r\n"); |
| 197 | - content.append("u="+channelId+":3\r\n"); | |
| 198 | + content.append("u="+channelId+":0\r\n"); | |
| 198 | 199 | content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n"); |
| 199 | 200 | content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); |
| 200 | 201 | if(device.getTransport().equals("TCP")) { |
| ... | ... | @@ -213,7 +214,7 @@ public class SIPCommander implements ISIPCommander { |
| 213 | 214 | } |
| 214 | 215 | content.append("y="+ssrc+"\r\n");//ssrc |
| 215 | 216 | |
| 216 | - Request request = headerProvider.createInviteRequest(device, content.toString(), null, "live", null); | |
| 217 | + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "playback", null); | |
| 217 | 218 | |
| 218 | 219 | ClientTransaction transaction = transmitRequest(device, request); |
| 219 | 220 | streamSession.put(ssrc, transaction); |
| ... | ... | @@ -245,7 +246,7 @@ public class SIPCommander implements ISIPCommander { |
| 245 | 246 | } |
| 246 | 247 | Request byeRequest = dialog.createRequest(Request.BYE); |
| 247 | 248 | ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); |
| 248 | - String protocol = viaHeader.getTransport(); | |
| 249 | + String protocol = viaHeader.getTransport().toUpperCase(); | |
| 249 | 250 | ClientTransaction clientTransaction = null; |
| 250 | 251 | if("TCP".equals(protocol)) { |
| 251 | 252 | clientTransaction = sipLayer.getTcpSipProvider().getNewClientTransaction(byeRequest); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
| 1 | 1 | package com.genersoft.iot.vmp.media.zlm; |
| 2 | 2 | |
| 3 | +import java.math.BigInteger; | |
| 4 | + | |
| 3 | 5 | import org.slf4j.Logger; |
| 4 | 6 | import org.slf4j.LoggerFactory; |
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 8 | +import org.springframework.http.HttpStatus; | |
| 5 | 9 | import org.springframework.http.ResponseEntity; |
| 6 | 10 | import org.springframework.web.bind.annotation.PostMapping; |
| 11 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 7 | 12 | import org.springframework.web.bind.annotation.RequestMapping; |
| 13 | +import org.springframework.web.bind.annotation.ResponseBody; | |
| 8 | 14 | import org.springframework.web.bind.annotation.RestController; |
| 9 | 15 | |
| 16 | +import com.alibaba.fastjson.JSONObject; | |
| 17 | +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; | |
| 18 | + | |
| 10 | 19 | /** |
| 11 | 20 | * @Description:针对 ZLMediaServer的hook事件监听 |
| 12 | 21 | * @author: songww |
| ... | ... | @@ -18,135 +27,247 @@ public class ZLMHttpHookListener { |
| 18 | 27 | |
| 19 | 28 | private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); |
| 20 | 29 | |
| 30 | + @Autowired | |
| 31 | + private SIPCommander cmder; | |
| 32 | + | |
| 21 | 33 | /** |
| 22 | 34 | * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 |
| 23 | 35 | * |
| 24 | 36 | */ |
| 25 | - @PostMapping("/on_flow_report") | |
| 26 | - public ResponseEntity onFlowReport(){ | |
| 37 | + @ResponseBody | |
| 38 | + @PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") | |
| 39 | + public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){ | |
| 40 | + | |
| 41 | + if (logger.isDebugEnabled()) { | |
| 42 | + logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString()); | |
| 43 | + } | |
| 27 | 44 | // TODO Auto-generated method stub |
| 28 | 45 | |
| 29 | - return null; | |
| 46 | + | |
| 47 | + JSONObject ret = new JSONObject(); | |
| 48 | + json.put("code", 0); | |
| 49 | + json.put("msg", "success"); | |
| 50 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 30 | 51 | } |
| 31 | 52 | |
| 32 | 53 | /** |
| 33 | 54 | * 访问http文件服务器上hls之外的文件时触发。 |
| 34 | 55 | * |
| 35 | 56 | */ |
| 36 | - @PostMapping("/on_http_access") | |
| 37 | - public ResponseEntity onHttpAccess(){ | |
| 57 | + @ResponseBody | |
| 58 | + @PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") | |
| 59 | + public ResponseEntity<String> onHttpAccess(@RequestBody JSONObject json){ | |
| 60 | + | |
| 61 | + if (logger.isDebugEnabled()) { | |
| 62 | + logger.debug("ZLM HOOK on_http_access API 调用,参数:" + json.toString()); | |
| 63 | + } | |
| 38 | 64 | // TODO Auto-generated method stub |
| 39 | 65 | |
| 40 | - return null; | |
| 66 | + JSONObject ret = new JSONObject(); | |
| 67 | + json.put("code", 0); | |
| 68 | + json.put("err", ""); | |
| 69 | + json.put("path", ""); | |
| 70 | + json.put("second", 600); | |
| 71 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 41 | 72 | } |
| 42 | 73 | |
| 43 | 74 | /** |
| 44 | 75 | * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 |
| 45 | 76 | * |
| 46 | 77 | */ |
| 47 | - @PostMapping("/on_play") | |
| 48 | - public ResponseEntity onPlay(){ | |
| 78 | + @ResponseBody | |
| 79 | + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") | |
| 80 | + public ResponseEntity<String> onPlay(@RequestBody JSONObject json){ | |
| 81 | + | |
| 82 | + if (logger.isDebugEnabled()) { | |
| 83 | + logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString()); | |
| 84 | + } | |
| 49 | 85 | // TODO Auto-generated method stub |
| 50 | 86 | |
| 51 | - return null; | |
| 87 | + JSONObject ret = new JSONObject(); | |
| 88 | + json.put("code", 0); | |
| 89 | + json.put("msg", "success"); | |
| 90 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 52 | 91 | } |
| 53 | 92 | |
| 54 | 93 | /** |
| 55 | 94 | * rtsp/rtmp/rtp推流鉴权事件。 |
| 56 | 95 | * |
| 57 | 96 | */ |
| 58 | - @PostMapping("/on_publish") | |
| 59 | - public ResponseEntity onPublish(){ | |
| 97 | + @ResponseBody | |
| 98 | + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") | |
| 99 | + public ResponseEntity<String> onPublish(@RequestBody JSONObject json){ | |
| 100 | + | |
| 101 | + if (logger.isDebugEnabled()) { | |
| 102 | + logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString()); | |
| 103 | + } | |
| 60 | 104 | // TODO Auto-generated method stub |
| 61 | 105 | |
| 62 | - return null; | |
| 106 | + JSONObject ret = new JSONObject(); | |
| 107 | + json.put("code", 0); | |
| 108 | + json.put("msg", "success"); | |
| 109 | + json.put("enableHls", true); | |
| 110 | + json.put("enableMP4", false); | |
| 111 | + json.put("enableRtxp", true); | |
| 112 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 63 | 113 | } |
| 64 | 114 | |
| 65 | 115 | /** |
| 66 | 116 | * 录制mp4完成后通知事件;此事件对回复不敏感。 |
| 67 | 117 | * |
| 68 | 118 | */ |
| 69 | - @PostMapping("/on_record_mp4") | |
| 70 | - public ResponseEntity onRecordMp4(){ | |
| 119 | + @ResponseBody | |
| 120 | + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") | |
| 121 | + public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json){ | |
| 122 | + | |
| 123 | + if (logger.isDebugEnabled()) { | |
| 124 | + logger.debug("ZLM HOOK on_record_mp4 API调用,参数:" + json.toString()); | |
| 125 | + } | |
| 71 | 126 | // TODO Auto-generated method stub |
| 72 | 127 | |
| 73 | - return null; | |
| 128 | + JSONObject ret = new JSONObject(); | |
| 129 | + json.put("code", 0); | |
| 130 | + json.put("msg", "success"); | |
| 131 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 74 | 132 | } |
| 75 | 133 | |
| 76 | 134 | /** |
| 77 | - * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 | |
| 135 | + * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 | |
| 78 | 136 | * |
| 79 | 137 | */ |
| 80 | - @PostMapping("/on_rtsp_auth") | |
| 81 | - public ResponseEntity onRtspAuth(){ | |
| 138 | + @ResponseBody | |
| 139 | + @PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") | |
| 140 | + public ResponseEntity<String> onRtspRealm(@RequestBody JSONObject json){ | |
| 141 | + | |
| 142 | + if (logger.isDebugEnabled()) { | |
| 143 | + logger.debug("ZLM HOOK on_rtsp_realm API调用,参数:" + json.toString()); | |
| 144 | + } | |
| 82 | 145 | // TODO Auto-generated method stub |
| 83 | 146 | |
| 84 | - return null; | |
| 147 | + JSONObject ret = new JSONObject(); | |
| 148 | + json.put("code", 0); | |
| 149 | + json.put("realm", ""); | |
| 150 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 85 | 151 | } |
| 86 | 152 | |
| 153 | + | |
| 87 | 154 | /** |
| 88 | - * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 | |
| 155 | + * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 | |
| 89 | 156 | * |
| 90 | 157 | */ |
| 91 | - @PostMapping("/on_rtsp_realm") | |
| 92 | - public ResponseEntity onRtspRealm(){ | |
| 158 | + @ResponseBody | |
| 159 | + @PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") | |
| 160 | + public ResponseEntity<String> onRtspAuth(@RequestBody JSONObject json){ | |
| 161 | + | |
| 162 | + if (logger.isDebugEnabled()) { | |
| 163 | + logger.debug("ZLM HOOK on_rtsp_auth API调用,参数:" + json.toString()); | |
| 164 | + } | |
| 93 | 165 | // TODO Auto-generated method stub |
| 94 | 166 | |
| 95 | - return null; | |
| 167 | + JSONObject ret = new JSONObject(); | |
| 168 | + json.put("code", 0); | |
| 169 | + json.put("encrypted", false); | |
| 170 | + json.put("passwd", "test"); | |
| 171 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 96 | 172 | } |
| 97 | 173 | |
| 98 | 174 | /** |
| 99 | 175 | * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 |
| 100 | 176 | * |
| 101 | 177 | */ |
| 102 | - @PostMapping("/on_shell_login") | |
| 103 | - public ResponseEntity onShellLogin(){ | |
| 178 | + @ResponseBody | |
| 179 | + @PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") | |
| 180 | + public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){ | |
| 181 | + | |
| 182 | + if (logger.isDebugEnabled()) { | |
| 183 | + logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString()); | |
| 184 | + } | |
| 104 | 185 | // TODO Auto-generated method stub |
| 105 | 186 | |
| 106 | - return null; | |
| 187 | + JSONObject ret = new JSONObject(); | |
| 188 | + json.put("code", 0); | |
| 189 | + json.put("msg", "success"); | |
| 190 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 107 | 191 | } |
| 108 | 192 | |
| 109 | 193 | /** |
| 110 | 194 | * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 |
| 111 | 195 | * |
| 112 | 196 | */ |
| 113 | - @PostMapping("/on_stream_changed") | |
| 114 | - public ResponseEntity onStreamChanged(){ | |
| 197 | + @ResponseBody | |
| 198 | + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") | |
| 199 | + public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){ | |
| 200 | + | |
| 201 | + if (logger.isDebugEnabled()) { | |
| 202 | + logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString()); | |
| 203 | + } | |
| 115 | 204 | // TODO Auto-generated method stub |
| 116 | 205 | |
| 117 | - return null; | |
| 206 | + JSONObject ret = new JSONObject(); | |
| 207 | + json.put("code", 0); | |
| 208 | + json.put("msg", "success"); | |
| 209 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 118 | 210 | } |
| 119 | 211 | |
| 120 | 212 | /** |
| 121 | 213 | * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 |
| 122 | 214 | * |
| 123 | 215 | */ |
| 124 | - @PostMapping("/on_stream_none_reader") | |
| 125 | - public ResponseEntity onStreamNoneReader(){ | |
| 126 | - // TODO Auto-generated method stub | |
| 216 | + @ResponseBody | |
| 217 | + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") | |
| 218 | + public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json){ | |
| 219 | + | |
| 220 | + if (logger.isDebugEnabled()) { | |
| 221 | + logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString()); | |
| 222 | + } | |
| 223 | + | |
| 224 | + BigInteger bigint=new BigInteger(json.getString("stream"), 16); | |
| 225 | + int numb=bigint.intValue(); | |
| 226 | + String ssrc = String.format("%010d", numb); | |
| 127 | 227 | |
| 128 | - return null; | |
| 228 | + cmder.streamByeCmd(ssrc); | |
| 229 | + | |
| 230 | + JSONObject ret = new JSONObject(); | |
| 231 | + json.put("code", 0); | |
| 232 | + json.put("close", true); | |
| 233 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 129 | 234 | } |
| 130 | 235 | |
| 131 | 236 | /** |
| 132 | 237 | * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 |
| 133 | 238 | * |
| 134 | 239 | */ |
| 135 | - @PostMapping("/on_stream_not_found") | |
| 136 | - public ResponseEntity onStreamNotFound(){ | |
| 240 | + @ResponseBody | |
| 241 | + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") | |
| 242 | + public ResponseEntity<String> onStreamNotFound(@RequestBody JSONObject json){ | |
| 243 | + | |
| 244 | + if (logger.isDebugEnabled()) { | |
| 245 | + logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString()); | |
| 246 | + } | |
| 137 | 247 | // TODO Auto-generated method stub |
| 138 | 248 | |
| 139 | - return null; | |
| 249 | + JSONObject ret = new JSONObject(); | |
| 250 | + json.put("code", 0); | |
| 251 | + json.put("msg", "success"); | |
| 252 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 140 | 253 | } |
| 141 | 254 | |
| 142 | 255 | /** |
| 143 | 256 | * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 |
| 144 | 257 | * |
| 145 | 258 | */ |
| 146 | - @PostMapping("/on_server_started") | |
| 147 | - public ResponseEntity onServerStarted(){ | |
| 259 | + @ResponseBody | |
| 260 | + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") | |
| 261 | + public ResponseEntity<String> onServerStarted(@RequestBody JSONObject json){ | |
| 262 | + | |
| 263 | + if (logger.isDebugEnabled()) { | |
| 264 | + logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); | |
| 265 | + } | |
| 148 | 266 | // TODO Auto-generated method stub |
| 149 | 267 | |
| 150 | - return null; | |
| 268 | + JSONObject ret = new JSONObject(); | |
| 269 | + json.put("code", 0); | |
| 270 | + json.put("msg", "success"); | |
| 271 | + return new ResponseEntity<String>(ret.toString(),HttpStatus.OK); | |
| 151 | 272 | } |
| 152 | 273 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/playback/PlaybackController.java
| ... | ... | @@ -5,8 +5,10 @@ import org.slf4j.LoggerFactory; |
| 5 | 5 | import org.springframework.beans.factory.annotation.Autowired; |
| 6 | 6 | import org.springframework.http.HttpStatus; |
| 7 | 7 | import org.springframework.http.ResponseEntity; |
| 8 | +import org.springframework.util.StringUtils; | |
| 8 | 9 | import org.springframework.web.bind.annotation.GetMapping; |
| 9 | 10 | import org.springframework.web.bind.annotation.PathVariable; |
| 11 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 10 | 12 | import org.springframework.web.bind.annotation.RequestMapping; |
| 11 | 13 | import org.springframework.web.bind.annotation.RestController; |
| 12 | 14 | |
| ... | ... | @@ -30,12 +32,40 @@ public class PlaybackController { |
| 30 | 32 | @GetMapping("/playback/{deviceId}/{channelId}") |
| 31 | 33 | public ResponseEntity<String> play(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ |
| 32 | 34 | |
| 35 | + if (logger.isDebugEnabled()) { | |
| 36 | + logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s",deviceId, channelId)); | |
| 37 | + } | |
| 38 | + | |
| 39 | + if (StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(channelId)) { | |
| 40 | + String log = String.format("设备回放 API调用失败,deviceId:%s ,channelId:%s",deviceId, channelId); | |
| 41 | + logger.warn(log); | |
| 42 | + return new ResponseEntity<String>(log,HttpStatus.BAD_REQUEST); | |
| 43 | + } | |
| 44 | + | |
| 33 | 45 | Device device = storager.queryVideoDevice(deviceId); |
| 34 | 46 | String ssrc = cmder.playbackStreamCmd(device, channelId, startTime, endTime); |
| 35 | 47 | |
| 36 | 48 | if (logger.isDebugEnabled()) { |
| 37 | - logger.debug(String.format("设备预览 API调用,deviceId:%s ,channelId:%s",deviceId, channelId)); | |
| 38 | - logger.debug("设备预览 API调用,ssrc:"+ssrc+",ZLMedia streamId:"+Integer.toHexString(Integer.parseInt(ssrc))); | |
| 49 | + logger.debug("设备回放 API调用,ssrc:"+ssrc+",ZLMedia streamId:"+Integer.toHexString(Integer.parseInt(ssrc))); | |
| 50 | + } | |
| 51 | + | |
| 52 | + if(ssrc!=null) { | |
| 53 | + JSONObject json = new JSONObject(); | |
| 54 | + json.put("ssrc", ssrc); | |
| 55 | + return new ResponseEntity<String>(json.toString(),HttpStatus.OK); | |
| 56 | + } else { | |
| 57 | + logger.warn("设备回放API调用失败!"); | |
| 58 | + return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); | |
| 59 | + } | |
| 60 | + } | |
| 61 | + | |
| 62 | + @PostMapping("/playback/{ssrc}/stop") | |
| 63 | + public ResponseEntity<String> playStop(@PathVariable String ssrc){ | |
| 64 | + | |
| 65 | + cmder.streamByeCmd(ssrc); | |
| 66 | + | |
| 67 | + if (logger.isDebugEnabled()) { | |
| 68 | + logger.debug(String.format("设备录像回放停止 API调用,ssrc:%s", ssrc)); | |
| 39 | 69 | } |
| 40 | 70 | |
| 41 | 71 | if(ssrc!=null) { |
| ... | ... | @@ -43,7 +73,7 @@ public class PlaybackController { |
| 43 | 73 | json.put("ssrc", ssrc); |
| 44 | 74 | return new ResponseEntity<String>(json.toString(),HttpStatus.OK); |
| 45 | 75 | } else { |
| 46 | - logger.warn("设备预览API调用失败!"); | |
| 76 | + logger.warn("设备录像回放停止API调用失败!"); | |
| 47 | 77 | return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); |
| 48 | 78 | } |
| 49 | 79 | } | ... | ... |