src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java
@@ -31,6 +31,9 @@ @JSONField(name = "general.streamNoneReaderDelayMS") private String generalStreamNoneReaderDelayMS; @JSONField(name = "general.localIP") private String localIP; @JSONField(name = "hls.fileBufSize") private String hlsFileBufSize; @@ -162,6 +165,18 @@ @JSONField(name = "rtp.videoMtuSize") private String rtpVideoMtuSize; @JSONField(name = "rtp_proxy.checkSource") private String rtpProxyCheckSource; @JSONField(name = "rtp_proxy.dumpDir") private String rtpProxyDumpDir; @JSONField(name = "rtp_proxy.port") private String rtpProxyPort; @JSONField(name = "rtp_proxy.timeoutSec") private String rtpProxyTimeoutSec; @JSONField(name = "rtsp.authBasic") private String rtspAuthBasic; @@ -664,4 +679,44 @@ public void setShellPhell(String shellPhell) { this.shellPhell = shellPhell; } public String getLocalIP() { return localIP; } public void setLocalIP(String localIP) { this.localIP = localIP; } public String getRtpProxyCheckSource() { return rtpProxyCheckSource; } public void setRtpProxyCheckSource(String rtpProxyCheckSource) { this.rtpProxyCheckSource = rtpProxyCheckSource; } public String getRtpProxyDumpDir() { return rtpProxyDumpDir; } public void setRtpProxyDumpDir(String rtpProxyDumpDir) { this.rtpProxyDumpDir = rtpProxyDumpDir; } public String getRtpProxyPort() { return rtpProxyPort; } public void setRtpProxyPort(String rtpProxyPort) { this.rtpProxyPort = rtpProxyPort; } public String getRtpProxyTimeoutSec() { return rtpProxyTimeoutSec; } public void setRtpProxyTimeoutSec(String rtpProxyTimeoutSec) { this.rtpProxyTimeoutSec = rtpProxyTimeoutSec; } } src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
@@ -16,10 +16,6 @@ String sipId; @Value("${sip.password}") String sipPassword; @Value("${media.ip}") String mediaIp; @Value("${media.port}") Integer mediaPort; @Value("${sip.ptz.speed:50}") Integer speed; @@ -54,22 +50,6 @@ public void setSipPassword(String sipPassword) { this.sipPassword = sipPassword; } public String getMediaIp() { return mediaIp; } public void setMediaIp(String mediaIp) { this.mediaIp = mediaIp; } public Integer getMediaPort() { return mediaPort; } public void setMediaPort(Integer mediaPort) { this.mediaPort = mediaPort; } public Integer getSpeed() { src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannel.java
@@ -103,9 +103,14 @@ private String password; /** * 云台控制 * 云台类型 */ private int PTZType; /** * 云台类型描述字符串 */ private String PTZTypeText; /** * 在线/离线 @@ -328,6 +333,27 @@ public void setPTZType(int PTZType) { this.PTZType = PTZType; switch (PTZType) { case 0: this.PTZTypeText = "未知"; break; case 1: this.PTZTypeText = "球机"; break; case 2: this.PTZTypeText = "半球"; break; case 3: this.PTZTypeText = "固定枪机"; break; case 4: this.PTZTypeText = "遥控枪机"; break; } } public String getPTZTypeText() { return PTZTypeText; } public String getSsrc() { src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -60,9 +60,7 @@ @Qualifier(value="udpSipProvider") private SipProvider udpSipProvider; @Value("${media.ip}") private String mediaIp; /** * 云台方向放控制,使用配置文件中的默认镜头移动速度 * @@ -207,18 +205,19 @@ String ssrc = streamSession.createPlaySsrc(); String transport = device.getTransport(); MediaServerConfig mediaInfo = storager.getMediaInfo(); // StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getLocalIP()+"\r\n"); content.append("s=Play\r\n"); content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n"); content.append("c=IN IP4 "+mediaInfo.getLocalIP()+"\r\n"); content.append("t=0 0\r\n"); if("TCP".equals(transport)) { content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n"); content.append("m=video "+mediaInfo.getRtpProxyPort()+" TCP/RTP/AVP 96 98 97\r\n"); } if("UDP".equals(transport)) { content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n"); content.append("m=video "+mediaInfo.getRtpProxyPort()+" RTP/AVP 96 98 97\r\n"); } content.append("a=recvonly\r\n"); content.append("a=rtpmap:96 PS/90000\r\n"); @@ -239,16 +238,16 @@ deviceChannel.setSsrc(ssrc); storager.updateChannel(device.getDeviceId(), deviceChannel); } MediaServerConfig mediaInfo = storager.getMediaInfo(); StreamInfo streamInfo = new StreamInfo(); streamInfo.setSsrc(ssrc); // String streamId = Integer.toHexString(Integer.parseInt(streamInfo.getSsrc())); String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); // ZLM 要求大写且首位补零 streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaIp, mediaInfo.getHttpPort(), streamId)); streamInfo.setWS_FLV(String.format("ws://%s:%s/rtp/%s.flv", mediaIp, mediaInfo.getHttpPort(), streamId)); streamInfo.setRTMP(String.format("rtmp://%s:%s/rtp/%s", mediaIp, mediaInfo.getRtmpPort(), streamId)); streamInfo.setHLS(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaIp, mediaInfo.getHttpPort(), streamId)); streamInfo.setRTSP(String.format("rtsp://%s:%s/rtp/%s", mediaIp, mediaInfo.getRtspPort(), streamId)); streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); streamInfo.setWS_FLV(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); streamInfo.setRTMP(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId)); streamInfo.setHLS(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId)); streamInfo.setRTSP(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId)); storager.startPlay(device.getDeviceId(), channelId, streamInfo); return streamInfo; @@ -269,7 +268,7 @@ @Override public String playbackStreamCmd(Device device, String channelId, String startTime, String endTime) { try { MediaServerConfig mediaInfo = storager.getMediaInfo(); String ssrc = streamSession.createPlayBackSsrc(); // StringBuffer content = new StringBuffer(200); @@ -277,13 +276,13 @@ content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n"); content.append("s=Playback\r\n"); content.append("u="+channelId+":0\r\n"); content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n"); content.append("c=IN IP4 "+mediaInfo.getLocalIP()+"\r\n"); content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); if(device.getTransport().equals("TCP")) { content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n"); content.append("m=video "+mediaInfo.getRtpProxyPort()+" TCP/RTP/AVP 96 98 97\r\n"); } if(device.getTransport().equals("UDP")) { content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n"); content.append("m=video "+mediaInfo.getRtpProxyPort()+" RTP/AVP 96 98 97\r\n"); } content.append("a=recvonly\r\n"); content.append("a=rtpmap:96 PS/90000\r\n"); @@ -300,6 +299,7 @@ ClientTransaction transaction = transmitRequest(device, request); streamSession.put(ssrc, transaction); return ssrc; } catch ( SipException | ParseException | InvalidArgumentException e) { e.printStackTrace(); return null; @@ -473,6 +473,8 @@ */ @Override public boolean catalogQuery(Device device) { // 清空通道 storager.cleanChannelsForDevice(device.getDeviceId()); try { StringBuffer catalogXml = new StringBuffer(200); catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>"); src/main/java/com/genersoft/iot/vmp/media/zlm/SolrProxyServletConfiguration.java
File was deleted src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
New file @@ -0,0 +1,58 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import org.apache.http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Enumeration; @RestController @RequestMapping("/zlm") public class ZLMHTTPProxyController { private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class); @Autowired private IVideoManagerStorager storager; @ResponseBody @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") public Object proxy(HttpServletRequest request, HttpServletResponse response){ if (storager.getMediaInfo() == null) { return "未接入流媒体"; } String requestURI = String.format("http://%s:%s%s?%s&%s", storager.getMediaInfo().getLocalIP(), storager.getMediaInfo().getHttpPort(), request.getRequestURI().replace("/zlm",""), storager.getMediaInfo().getHookAdminParams(), request.getQueryString() ); // 发送请求 RestTemplate restTemplate = new RestTemplate(); //将指定的url返回的参数自动封装到自定义好的对应类对象中 Object result = null; try { result = restTemplate.getForObject(requestURI,Object.class); }catch (HttpClientErrorException httpClientErrorException) { response.setStatus(httpClientErrorException.getStatusCode().value()); } return result; } } src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSONArray; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.utils.IpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -21,6 +22,8 @@ import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import javax.servlet.http.HttpServletRequest; /** * @Description:针对 ZLMediaServer的hook事件监听 @@ -267,7 +270,7 @@ */ @ResponseBody @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") public ResponseEntity<String> onServerStarted(@RequestBody JSONObject json){ public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){ if (logger.isDebugEnabled()) { logger.debug("ZLM HOOK on_server_started API调用,参数:" + json.toString()); src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -176,4 +176,10 @@ * 更新缓存 */ public void updateCatch(); /** * 清空通道 * @param deviceId */ void cleanChannelsForDevice(String deviceId); } src/main/java/com/genersoft/iot/vmp/storager/jdbc/VideoManagerJdbcStoragerImpl.java
@@ -171,4 +171,9 @@ public void updateCatch() { } @Override public void cleanChannelsForDevice(String deviceId) { } } src/main/java/com/genersoft/iot/vmp/storager/redis/VideoManagerRedisStoragerImpl.java
@@ -399,13 +399,13 @@ for (int i = 0; i < deviceChannelList.size(); i++) { String key = (String)deviceChannelList.get(i); String[] s = key.split("_"); String channelId = s[3]; String channelId = s[3].split(":")[0]; HashSet<String> subChannel = channelMap.get(channelId); if (subChannel == null) { subChannel = new HashSet<>(); } if (s.length > 4) { subChannel.add(s[4]); if ("null".equals(s[6])) { subChannel.add(s[6]); } channelMap.put(channelId, subChannel); System.out.println(); @@ -414,4 +414,15 @@ deviceMap.put(device.getDeviceId(),channelMap); } } @Override public void cleanChannelsForDevice(String deviceId) { List<DeviceChannel> result = new ArrayList<>(); List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*"); if (deviceChannelList != null && deviceChannelList.size() > 0 ) { for (int i = 0; i < deviceChannelList.size(); i++) { redis.del((String)deviceChannelList.get(i)); } } } } src/main/java/com/genersoft/iot/vmp/utils/IpUtil.java
New file @@ -0,0 +1,48 @@ package com.genersoft.iot.vmp.utils; import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.UnknownHostException; public class IpUtil { public static String getIpAddr(HttpServletRequest request) { String ipAddress = null; try { ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals("127.0.0.1")) { // 根据网卡取本机配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } ipAddress = inet.getHostAddress(); } } // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() // = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } } catch (Exception e) { ipAddress=""; } // ipAddress = this.getRequest().getRemoteAddr(); return ipAddress; } } src/main/java/com/genersoft/iot/vmp/vmanager/device/DeviceController.java
@@ -85,9 +85,9 @@ public DeferredResult<ResponseEntity<Device>> devicesSync(@PathVariable String deviceId){ if (logger.isDebugEnabled()) { logger.debug("设备信息同步API调用,deviceId:" + deviceId); } logger.debug("设备信息同步API调用,deviceId:" + deviceId); Device device = storager.queryVideoDevice(deviceId); cmder.catalogQuery(device); DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>(); src/main/java/com/genersoft/iot/vmp/vmanager/play/PlayController.java
@@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.vmanager.play; import com.alibaba.fastjson.JSON; import com.genersoft.iot.vmp.common.StreamInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,9 +44,7 @@ } if(streamInfo!=null) { JSONObject json = new JSONObject(); json.put("ssrc", streamInfo.getSsrc()); return new ResponseEntity<String>(json.toString(),HttpStatus.OK); return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK); } else { logger.warn("设备预览API调用失败!"); return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR); src/main/resources/application.yml
@@ -35,17 +35,6 @@ id: 34020000002000000001 # 默认设备认证密码,后续扩展使用设备单独密码 password: 12345678 media: # ip: 10.200.64.88 ip: 192.168.1.20 port: 10000 # 自定义代理相关配置 # 代理的本地路由 proxy: servlet_url: /media/* # 要代理的地址 target_url: http://127.0.0.1:10080 auth: #32位小写md5加密(默认密码为admin) username: admin src/main/resources/static/css/app.ea73b2a1.css
File was deleted src/main/resources/static/css/chunk-vendors.b5921ce8.css
File was deleted src/main/resources/static/dist/crossdomain.xml
File was deleted src/main/resources/static/dist/liveplayer-lib.min.js
File was deleted src/main/resources/static/dist/liveplayer.swfBinary files differ
src/main/resources/static/favicon.icoBinary files differ
src/main/resources/static/fonts/element-icons.535877f5.woffBinary files differ
src/main/resources/static/fonts/element-icons.732389de.ttfBinary files differ
src/main/resources/static/index.html
File was deleted src/main/resources/static/js/app.d31a42f9.js
File was deleted src/main/resources/static/js/app.d31a42f9.js.map
File was deleted src/main/resources/static/js/chunk-vendors.21c802f5.js
File was deleted src/main/resources/static/js/chunk-vendors.21c802f5.js.map
File was deleted src/main/resources/static/video/crossdomain.xml
File was deleted src/main/resources/static/video/liveplayer-element.min.js
File was deleted src/main/resources/static/video/liveplayer.swfBinary files differ
src/main/resources/static/video/video.html
File was deleted web_src/src/assets/icons.png