Commit ad93be12fb3705e995cf0dab933760edaef3dd7a

Authored by 64850858
1 parent d881cd7e

添加云端录像功能

Showing 23 changed files with 541 additions and 93 deletions

Too many changes to show.

To preserve performance only 23 of 34 files are displayed.

README.md
... ... @@ -92,9 +92,6 @@ https://gitee.com/18010473990/wvp-GB28181.git
92 92 - [ ] 添加系统配置
93 93 - [ ] 添加用户管理
94 94  
95   -# 待实现:
96   -web界面系统设置
97   -
98 95  
99 96 # 项目部署
100 97 参考:[WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki)
... ... @@ -104,7 +101,7 @@ https://gitee.com/18010473990/wvp-GB28181.git
104 101  
105 102 # 使用帮助
106 103 QQ群: 901799015, 542509000(ZLM大群)
107   -QQ私信一般不回, 精力有限.欢迎大家在群里讨论.
  104 +QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对你有帮助,欢迎star和提交pr。
108 105  
109 106 # 致谢
110 107 感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架
... ...
... ... @@ -184,6 +184,12 @@
184 184 <version>2.3.0</version>
185 185 </dependency>
186 186  
  187 + <!--反向代理-->
  188 + <dependency>
  189 + <groupId>org.mitre.dsmiley.httpproxy</groupId>
  190 + <artifactId>smiley-http-proxy-servlet</artifactId>
  191 + <version>1.12</version>
  192 + </dependency>
187 193  
188 194 <!-- onvif协议栈 -->
189 195 <dependency>
... ...
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
... ... @@ -51,6 +51,9 @@ public class MediaConfig {
51 51 @Value("${media.rtp.portRange}")
52 52 private String rtpPortRange;
53 53  
  54 + @Value("${media.recordAssistPort}")
  55 + private int recordAssistPort;
  56 +
54 57 public String getIp() {
55 58 return ip;
56 59 }
... ... @@ -174,4 +177,12 @@ public class MediaConfig {
174 177 public void setRtspSSLPort(String rtspSSLPort) {
175 178 this.rtspSSLPort = rtspSSLPort;
176 179 }
  180 +
  181 + public int getRecordAssistPort() {
  182 + return recordAssistPort;
  183 + }
  184 +
  185 + public void setRecordAssistPort(int recordAssistPort) {
  186 + this.recordAssistPort = recordAssistPort;
  187 + }
177 188 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf;
  2 +
  3 +import org.apache.http.HttpRequest;
  4 +import org.apache.http.HttpResponse;
  5 +import org.mitre.dsmiley.httpproxy.ProxyServlet;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.boot.web.servlet.ServletRegistrationBean;
  10 +import org.springframework.context.annotation.Bean;
  11 +import org.springframework.context.annotation.Configuration;
  12 +import org.springframework.util.StringUtils;
  13 +
  14 +import javax.servlet.ServletException;
  15 +import java.io.IOException;
  16 +import java.net.ConnectException;
  17 +import java.util.Locale;
  18 +
  19 +
  20 +@Configuration
  21 +public class ProxyServletConfig {
  22 +
  23 + private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
  24 +
  25 + @Autowired
  26 + private MediaConfig mediaConfig;
  27 +
  28 + @Bean
  29 + public ServletRegistrationBean zlmServletRegistrationBean(){
  30 + String ip = StringUtils.isEmpty(mediaConfig.getWanIp())? mediaConfig.getIp(): mediaConfig.getWanIp();
  31 + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*");
  32 + servletRegistrationBean.setName("zlm_Proxy");
  33 + servletRegistrationBean.addInitParameter("targetUri", String.format("http://%s:%s", ip, mediaConfig.getHttpPort()));
  34 + if (logger.isDebugEnabled()) {
  35 + servletRegistrationBean.addInitParameter("log", "true");
  36 + }
  37 + return servletRegistrationBean;
  38 + }
  39 +
  40 + class ZLMProxySerlet extends ProxyServlet{
  41 + @Override
  42 + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
  43 + System.out.println(e.getMessage());
  44 + try {
  45 + super.handleRequestException(proxyRequest, proxyResonse, e);
  46 + } catch (ServletException servletException) {
  47 + logger.error("zlm 代理失败: ", e);
  48 + } catch (IOException ioException) {
  49 + if (ioException instanceof ConnectException) {
  50 + logger.error("zlm 连接失败");
  51 + }else {
  52 + logger.error("zlm 代理失败: ", e);
  53 + }
  54 + } catch (RuntimeException exception){
  55 + logger.error("zlm 代理失败: ", e);
  56 + }
  57 + }
  58 + }
  59 +
  60 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
... ... @@ -43,7 +43,6 @@ public class RedisConfig extends CachingConfigurerSupport {
43 43 * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
44 44 *
45 45 * @param connectionFactory
46   - * @param listenerAdapter
47 46 * @return
48 47 */
49 48 @Bean
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java
... ... @@ -8,11 +8,53 @@ public class UserSetup {
8 8 @Value("${userSettings.savePositionHistory}")
9 9 boolean savePositionHistory;
10 10  
  11 + @Value("${userSettings.autoApplyPlay}")
  12 + private boolean autoApplyPlay;
  13 +
  14 + @Value("${userSettings.seniorSdp}")
  15 + private boolean seniorSdp;
  16 +
  17 + @Value("${userSettings.playTimeout}")
  18 + private long playTimeout;
  19 +
  20 + @Value("${userSettings.waitTrack}")
  21 + private boolean waitTrack;
  22 +
  23 + @Value("${userSettings.interfaceAuthentication}")
  24 + private boolean interfaceAuthentication;
  25 +
  26 + @Value("${userSettings.recordPushLive}")
  27 + private boolean recordPushLive;
  28 +
11 29 public boolean getSavePositionHistory() {
12 30 return savePositionHistory;
13 31 }
14 32  
15   - public void setSavePositionHistory(boolean savePositionHistory) {
16   - this.savePositionHistory = savePositionHistory;
  33 + public boolean isSavePositionHistory() {
  34 + return savePositionHistory;
  35 + }
  36 +
  37 + public boolean isAutoApplyPlay() {
  38 + return autoApplyPlay;
  39 + }
  40 +
  41 + public boolean isSeniorSdp() {
  42 + return seniorSdp;
  43 + }
  44 +
  45 + public long getPlayTimeout() {
  46 + return playTimeout;
  47 + }
  48 +
  49 + public boolean isWaitTrack() {
  50 + return waitTrack;
  51 + }
  52 +
  53 + public boolean isInterfaceAuthentication() {
  54 + return interfaceAuthentication;
  55 + }
  56 +
  57 + public boolean isRecordPushLive() {
  58 + return recordPushLive;
17 59 }
18 60 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
1 1 package com.genersoft.iot.vmp.conf.security;
2 2  
  3 +import com.genersoft.iot.vmp.conf.UserSetup;
3 4 import org.springframework.beans.factory.annotation.Autowired;
4 5 import org.springframework.beans.factory.annotation.Value;
5 6 import org.springframework.context.annotation.Bean;
... ... @@ -22,8 +23,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
22 23 @EnableGlobalMethodSecurity(prePostEnabled = true)
23 24 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
24 25  
25   - @Value("${userSettings.interfaceAuthentication}")
26   - private boolean interfaceAuthentication;
  26 + @Autowired
  27 + private UserSetup userSetup;
27 28  
28 29 @Autowired
29 30 private DefaultUserDetailsServiceImpl userDetailsService;
... ... @@ -71,7 +72,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
71 72 @Override
72 73 public void configure(WebSecurity web) {
73 74  
74   - if (!interfaceAuthentication) {
  75 + if (!userSetup.isInterfaceAuthentication()) {
75 76 web.ignoring().antMatchers("**");
76 77 }else {
77 78 // 可以直接访问的静态数据
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
... ... @@ -32,7 +32,7 @@ public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessa
32 32 /**
33 33 * 监听失效的key
34 34 * @param message
35   - * @param bytes
  35 + * @param pattern
36 36 */
37 37 @Override
38 38 public void onMessage(Message message, byte[] pattern) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -12,6 +12,7 @@ import com.alibaba.fastjson.JSONArray;
12 12 import com.alibaba.fastjson.JSONObject;
13 13 import com.genersoft.iot.vmp.common.StreamInfo;
14 14 import com.genersoft.iot.vmp.conf.MediaConfig;
  15 +import com.genersoft.iot.vmp.conf.UserSetup;
15 16 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
16 17 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
17 18 import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
... ... @@ -24,7 +25,6 @@ import org.slf4j.Logger;
24 25 import org.slf4j.LoggerFactory;
25 26 import org.springframework.beans.factory.annotation.Autowired;
26 27 import org.springframework.beans.factory.annotation.Qualifier;
27   -import org.springframework.beans.factory.annotation.Value;
28 28 import org.springframework.context.annotation.DependsOn;
29 29 import org.springframework.context.annotation.Lazy;
30 30 import org.springframework.stereotype.Component;
... ... @@ -83,14 +83,8 @@ public class SIPCommander implements ISIPCommander {
83 83 @Autowired
84 84 private MediaConfig mediaConfig;
85 85  
86   - @Value("${userSettings.seniorSdp}")
87   - private boolean seniorSdp;
88   -
89   - @Value("${userSettings.autoApplyPlay}")
90   - private boolean autoApplyPlay;
91   -
92   - @Value("${userSettings.waitTrack}")
93   - private boolean waitTrack;
  86 + @Autowired
  87 + private UserSetup userSetup;
94 88  
95 89 @Autowired
96 90 private ZLMHttpHookSubscribe subscribe;
... ... @@ -377,7 +371,7 @@ public class SIPCommander implements ISIPCommander {
377 371 subscribeKey.put("regist", true);
378 372  
379 373 subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{
380   - if (waitTrack && json.getJSONArray("tracks") == null) return;
  374 + if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
381 375 event.response(json);
382 376 subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
383 377 });
... ... @@ -390,7 +384,7 @@ public class SIPCommander implements ISIPCommander {
390 384 content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
391 385 content.append("t=0 0\r\n");
392 386  
393   - if (seniorSdp) {
  387 + if (userSetup.isSeniorSdp()) {
394 388 if("TCP-PASSIVE".equals(streamMode)) {
395 389 content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
396 390 }else if ("TCP-ACTIVE".equals(streamMode)) {
... ... @@ -478,7 +472,7 @@ public class SIPCommander implements ISIPCommander {
478 472 subscribeKey.put("regist", true);
479 473 logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
480 474 subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, json->{
481   - if (waitTrack && json.getJSONArray("tracks") == null) return;
  475 + if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
482 476 event.response(json);
483 477 subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
484 478 });
... ... @@ -500,7 +494,7 @@ public class SIPCommander implements ISIPCommander {
500 494 }
501 495 String streamMode = device.getStreamMode().toUpperCase();
502 496  
503   - if (seniorSdp) {
  497 + if (userSetup.isSeniorSdp()) {
504 498 if("TCP-PASSIVE".equals(streamMode)) {
505 499 content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
506 500 }else if ("TCP-ACTIVE".equals(streamMode)) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
1   -package com.genersoft.iot.vmp.media.zlm;
2   -
3   -import com.genersoft.iot.vmp.conf.MediaConfig;
4   -import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
5   -import org.springframework.beans.factory.annotation.Autowired;
6   -import org.springframework.beans.factory.annotation.Value;
7   -import org.springframework.web.bind.annotation.*;
8   -import org.springframework.web.client.HttpClientErrorException;
9   -import org.springframework.web.client.RestTemplate;
10   -
11   -import javax.servlet.http.HttpServletRequest;
12   -import javax.servlet.http.HttpServletResponse;
13   -
14   -@RestController
15   -@RequestMapping("/zlm")
16   -public class ZLMHTTPProxyController {
17   -
18   -
19   - // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
20   -
21   - @Autowired
22   - private IRedisCatchStorage redisCatchStorage;
23   -
24   - @Autowired
25   - private MediaConfig mediaConfig;
26   -
27   - @ResponseBody
28   - @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8")
29   - public Object proxy(HttpServletRequest request, HttpServletResponse response){
30   -
31   - if (redisCatchStorage.getMediaInfo() == null) {
32   - return "未接入流媒体";
33   - }
34   - ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
35   - String requestURI = String.format("http://%s:%s%s?%s&%s",
36   - mediaInfo.getLocalIP(),
37   - mediaConfig.getHttpPort(),
38   - request.getRequestURI().replace("/zlm",""),
39   - mediaInfo.getHookAdminParams(),
40   - request.getQueryString()
41   - );
42   - // 发送请求
43   - RestTemplate restTemplate = new RestTemplate();
44   - //将指定的url返回的参数自动封装到自定义好的对应类对象中
45   - Object result = null;
46   - try {
47   - result = restTemplate.getForObject(requestURI,Object.class);
48   -
49   - }catch (HttpClientErrorException httpClientErrorException) {
50   - response.setStatus(httpClientErrorException.getStatusCode().value());
51   - }
52   - return result;
53   - }
54   -}
  1 +//package com.genersoft.iot.vmp.media.zlm;
  2 +//
  3 +//import com.genersoft.iot.vmp.conf.MediaConfig;
  4 +//import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  5 +//import org.springframework.beans.factory.annotation.Autowired;
  6 +//import org.springframework.beans.factory.annotation.Value;
  7 +//import org.springframework.web.bind.annotation.*;
  8 +//import org.springframework.web.client.HttpClientErrorException;
  9 +//import org.springframework.web.client.RestTemplate;
  10 +//
  11 +//import javax.servlet.http.HttpServletRequest;
  12 +//import javax.servlet.http.HttpServletResponse;
  13 +//
  14 +//@RestController
  15 +//@RequestMapping("/zlm")
  16 +//public class ZLMHTTPProxyController {
  17 +//
  18 +//
  19 +// // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
  20 +//
  21 +// @Autowired
  22 +// private IRedisCatchStorage redisCatchStorage;
  23 +//
  24 +// @Autowired
  25 +// private MediaConfig mediaConfig;
  26 +//
  27 +// @ResponseBody
  28 +// @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8")
  29 +// public Object proxy(HttpServletRequest request, HttpServletResponse response){
  30 +//
  31 +// if (redisCatchStorage.getMediaInfo() == null) {
  32 +// return "未接入流媒体";
  33 +// }
  34 +// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
  35 +// String requestURI = String.format("http://%s:%s%s?%s&%s",
  36 +// mediaInfo.getLocalIP(),
  37 +// mediaConfig.getHttpPort(),
  38 +// request.getRequestURI().replace("/zlm",""),
  39 +// mediaInfo.getHookAdminParams(),
  40 +// request.getQueryString()
  41 +// );
  42 +// // 发送请求
  43 +// RestTemplate restTemplate = new RestTemplate();
  44 +// //将指定的url返回的参数自动封装到自定义好的对应类对象中
  45 +// Object result = null;
  46 +// try {
  47 +// result = restTemplate.getForObject(requestURI,Object.class);
  48 +//
  49 +// }catch (HttpClientErrorException httpClientErrorException) {
  50 +// response.setStatus(httpClientErrorException.getStatusCode().value());
  51 +// }
  52 +// return result;
  53 +// }
  54 +//}
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -7,6 +7,7 @@ import com.alibaba.fastjson.JSON;
7 7 import com.alibaba.fastjson.JSONArray;
8 8 import com.genersoft.iot.vmp.common.StreamInfo;
9 9 import com.genersoft.iot.vmp.conf.MediaConfig;
  10 +import com.genersoft.iot.vmp.conf.UserSetup;
10 11 import com.genersoft.iot.vmp.gb28181.bean.Device;
11 12 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
12 13 import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
... ... @@ -62,8 +63,8 @@ public class ZLMHttpHookListener {
62 63 @Autowired
63 64 private ZLMHttpHookSubscribe subscribe;
64 65  
65   - @Value("${userSettings.autoApplyPlay}")
66   - private boolean autoApplyPlay;
  66 + @Autowired
  67 + private UserSetup userSetup;
67 68  
68 69 @Autowired
69 70 private MediaConfig mediaConfig;
... ... @@ -132,10 +133,8 @@ public class ZLMHttpHookListener {
132 133 @ResponseBody
133 134 @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
134 135 public ResponseEntity<String> onPublish(@RequestBody JSONObject json){
135   -
136   - if (logger.isDebugEnabled()) {
137   - logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
138   - }
  136 +
  137 + logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
139 138  
140 139 ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
141 140 if (subscribe != null) subscribe.response(json);
... ... @@ -144,7 +143,7 @@ public class ZLMHttpHookListener {
144 143 ret.put("code", 0);
145 144 ret.put("msg", "success");
146 145 ret.put("enableHls", true);
147   - ret.put("enableMP4", false);
  146 + ret.put("enableMP4", userSetup.isRecordPushLive());
148 147 ret.put("enableRtxp", true);
149 148 return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
150 149 }
... ... @@ -333,7 +332,7 @@ public class ZLMHttpHookListener {
333 332 if (logger.isDebugEnabled()) {
334 333 logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
335 334 }
336   - if (autoApplyPlay) {
  335 + if (userSetup.isAutoApplyPlay()) {
337 336 String app = json.getString("app");
338 337 String streamId = json.getString("stream");
339 338 StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
... ... @@ -109,6 +109,10 @@ public class ZLMRunner implements CommandLineRunner {
109 109 if (StringUtils.isEmpty(mediaConfig.getHookIp())) mediaConfig.setHookIp(sipConfig.getSipIp());
110 110 String protocol = sslEnabled ? "https" : "http";
111 111 String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaConfig.getHookIp(), serverPort);
  112 + String recordHookPrex = null;
  113 + if (mediaConfig.getRecordAssistPort() != 0) {
  114 + recordHookPrex = String.format("http://127.0.0.1:%s/api/record", mediaConfig.getRecordAssistPort());
  115 + }
112 116 Map<String, Object> param = new HashMap<>();
113 117 param.put("api.secret",mediaConfig.getSecret()); // -profile:v Baseline
114 118 param.put("ffmpeg.cmd","%s -fflags nobuffer -rtsp_transport tcp -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s");
... ... @@ -116,8 +120,8 @@ public class ZLMRunner implements CommandLineRunner {
116 120 param.put("hook.on_flow_report","");
117 121 param.put("hook.on_play",String.format("%s/on_play", hookPrex));
118 122 param.put("hook.on_http_access","");
119   - param.put("hook.on_publish",String.format("%s/on_publish", hookPrex));
120   - param.put("hook.on_record_mp4","");
  123 + param.put("hook.on_publish", String.format("%s/on_publish", hookPrex));
  124 + param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): "");
121 125 param.put("hook.on_record_ts","");
122 126 param.put("hook.on_rtsp_auth","");
123 127 param.put("hook.on_rtsp_realm","");
... ...
src/main/java/com/genersoft/iot/vmp/service/IRecordInfoServer.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service;
  2 +
  3 +import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
  4 +import com.github.pagehelper.PageInfo;
  5 +
  6 +public interface IRecordInfoServer {
  7 + PageInfo<RecordInfo> getRecordList(int page, int count);
  8 +}
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import com.genersoft.iot.vmp.common.StreamInfo;
  7 +import com.genersoft.iot.vmp.conf.UserSetup;
7 8 import com.genersoft.iot.vmp.gb28181.bean.Device;
8 9 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
9 10 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
... ... @@ -64,8 +65,8 @@ public class PlayServiceImpl implements IPlayService {
64 65 @Autowired
65 66 private VideoStreamSessionManager streamSession;
66 67  
67   - @Value("${userSettings.playTimeout}")
68   - private long playTimeout;
  68 + @Autowired
  69 + private UserSetup userSetup;
69 70  
70 71  
71 72 @Override
... ... @@ -76,7 +77,7 @@ public class PlayServiceImpl implements IPlayService {
76 77 playResult.setDevice(device);
77 78 UUID uuid = UUID.randomUUID();
78 79 playResult.setUuid(uuid.toString());
79   - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(playTimeout);
  80 + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(userSetup.getPlayTimeout());
80 81 playResult.setResult(result);
81 82 // 录像查询以channelId作为deviceId查询
82 83 resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/RecordInfoServerImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.service.impl;
  2 +
  3 +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
  4 +import com.genersoft.iot.vmp.service.IRecordInfoServer;
  5 +import com.genersoft.iot.vmp.storager.dao.RecordInfoDao;
  6 +import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
  7 +import com.github.pagehelper.PageHelper;
  8 +import com.github.pagehelper.PageInfo;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.List;
  13 +
  14 +@Service
  15 +public class RecordInfoServerImpl implements IRecordInfoServer {
  16 +
  17 + @Autowired
  18 + private RecordInfoDao recordInfoDao;
  19 +
  20 + @Override
  21 + public PageInfo<RecordInfo> getRecordList(int page, int count) {
  22 + PageHelper.startPage(page, count);
  23 + List<RecordInfo> all = recordInfoDao.selectAll();
  24 + return new PageInfo<>(all);
  25 + }
  26 +}
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/RecordInfoDao.java 0 → 100644
  1 +package com.genersoft.iot.vmp.storager.dao;
  2 +
  3 +import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
  4 +import com.genersoft.iot.vmp.storager.dao.dto.User;
  5 +import org.apache.ibatis.annotations.Delete;
  6 +import org.apache.ibatis.annotations.Insert;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import org.apache.ibatis.annotations.Select;
  9 +import org.springframework.stereotype.Repository;
  10 +
  11 +import java.util.List;
  12 +
  13 +@Mapper
  14 +@Repository
  15 +public interface RecordInfoDao {
  16 +
  17 + @Insert("INSERT INTO recordInfo (app, stream, mediaServerId, createTime, type, deviceId, channelId, name) VALUES" +
  18 + "('${app}', '${stream}', '${mediaServerId}', datetime('now','localtime')), '${type}', '${deviceId}', '${channelId}', '${name}'")
  19 + int add(RecordInfo recordInfo);
  20 +
  21 + @Delete("DELETE FROM user WHERE createTime < '${beforeTime}'")
  22 + int deleteBefore(String beforeTime);
  23 +
  24 + @Select("select * FROM recordInfo")
  25 + List<RecordInfo> selectAll();
  26 +}
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/dto/RecordInfo.java 0 → 100644
  1 +package com.genersoft.iot.vmp.storager.dao.dto;
  2 +
  3 +/**
  4 + * 录像记录
  5 + */
  6 +public class RecordInfo {
  7 +
  8 + /**
  9 + * ID
  10 + */
  11 + private int id;
  12 +
  13 + /**
  14 + * 应用名
  15 + */
  16 + private String app;
  17 +
  18 + /**
  19 + * 流ID
  20 + */
  21 + private String stream;
  22 +
  23 + /**
  24 + * 对应的zlm流媒体的ID
  25 + */
  26 + private String mediaServerId;
  27 +
  28 + /**
  29 + * 创建时间
  30 + */
  31 + private String createTime;
  32 +
  33 + /**
  34 + * 类型 对应zlm的 originType
  35 + * unknown = 0,
  36 + * rtmp_push=1,
  37 + * rtsp_push=2,
  38 + * rtp_push=3,
  39 + * pull=4,
  40 + * ffmpeg_pull=5,
  41 + * mp4_vod=6,
  42 + * device_chn=7,
  43 + * rtc_push=8
  44 + */
  45 + private int type;
  46 +
  47 + /**
  48 + * 国标录像时的设备ID
  49 + */
  50 + private String deviceId;
  51 +
  52 + /**
  53 + * 国标录像时的通道ID
  54 + */
  55 + private String channelId;
  56 +
  57 + /**
  58 + * 拉流代理录像时的名称
  59 + */
  60 + private String name;
  61 +
  62 + public int getId() {
  63 + return id;
  64 + }
  65 +
  66 + public void setId(int id) {
  67 + this.id = id;
  68 + }
  69 +
  70 + public String getApp() {
  71 + return app;
  72 + }
  73 +
  74 + public void setApp(String app) {
  75 + this.app = app;
  76 + }
  77 +
  78 + public String getStream() {
  79 + return stream;
  80 + }
  81 +
  82 + public void setStream(String stream) {
  83 + this.stream = stream;
  84 + }
  85 +
  86 + public String getMediaServerId() {
  87 + return mediaServerId;
  88 + }
  89 +
  90 + public void setMediaServerId(String mediaServerId) {
  91 + this.mediaServerId = mediaServerId;
  92 + }
  93 +
  94 + public String getCreateTime() {
  95 + return createTime;
  96 + }
  97 +
  98 + public void setCreateTime(String createTime) {
  99 + this.createTime = createTime;
  100 + }
  101 +
  102 + public int getType() {
  103 + return type;
  104 + }
  105 +
  106 + public void setType(int type) {
  107 + this.type = type;
  108 + }
  109 +
  110 + public String getDeviceId() {
  111 + return deviceId;
  112 + }
  113 +
  114 + public void setDeviceId(String deviceId) {
  115 + this.deviceId = deviceId;
  116 + }
  117 +
  118 + public String getChannelId() {
  119 + return channelId;
  120 + }
  121 +
  122 + public void setChannelId(String channelId) {
  123 + this.channelId = channelId;
  124 + }
  125 +
  126 + public String getName() {
  127 + return name;
  128 + }
  129 +
  130 + public void setName(String name) {
  131 + this.name = name;
  132 + }
  133 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/RecordController.java renamed to src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
... ... @@ -26,9 +26,9 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
26 26 @CrossOrigin
27 27 @RestController
28 28 @RequestMapping("/api/gb_record")
29   -public class RecordController {
  29 +public class GBRecordController {
30 30  
31   - private final static Logger logger = LoggerFactory.getLogger(RecordController.class);
  31 + private final static Logger logger = LoggerFactory.getLogger(GBRecordController.class);
32 32  
33 33 @Autowired
34 34 private SIPCommander cmder;
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecoderProxyController.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.record;
  2 +
  3 +import com.genersoft.iot.vmp.conf.MediaConfig;
  4 +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
  5 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  6 +import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.http.HttpStatus;
  8 +import org.springframework.util.StringUtils;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.ResponseBody;
  11 +import org.springframework.web.bind.annotation.RestController;
  12 +import org.springframework.web.client.HttpClientErrorException;
  13 +import org.springframework.web.client.RestTemplate;
  14 +
  15 +import javax.servlet.http.HttpServletRequest;
  16 +import javax.servlet.http.HttpServletResponse;
  17 +import java.net.URLDecoder;
  18 +
  19 +@RestController
  20 +@RequestMapping("/record_proxy")
  21 +public class RecoderProxyController {
  22 +
  23 +
  24 + // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
  25 +
  26 + @Autowired
  27 + private IRedisCatchStorage redisCatchStorage;
  28 +
  29 + @Autowired
  30 + private MediaConfig mediaConfig;
  31 +
  32 + @ResponseBody
  33 + @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8")
  34 + public Object proxy(HttpServletRequest request, HttpServletResponse response){
  35 +
  36 +
  37 + String baseRequestURI = request.getRequestURI();
  38 + String[] split = baseRequestURI.split("/");
  39 + if (split.length <= 2) {
  40 + response.setStatus(HttpStatus.NOT_FOUND.value());
  41 + return null;
  42 + }
  43 + String mediaId = split[2];
  44 + if (StringUtils.isEmpty(mediaId)){
  45 + response.setStatus(HttpStatus.BAD_REQUEST.value());
  46 + return null;
  47 + }
  48 + // 后续改为根据Id获取对应的ZLM
  49 + ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
  50 + String requestURI = String.format("http://%s:%s%s?%s",
  51 + mediaInfo.getLocalIP(),
  52 + mediaConfig.getRecordAssistPort(),
  53 + baseRequestURI.substring(baseRequestURI.indexOf(mediaId) + mediaId.length()),
  54 + URLDecoder.decode(request.getQueryString())
  55 + );
  56 + // 发送请求
  57 + RestTemplate restTemplate = new RestTemplate();
  58 + //将指定的url返回的参数自动封装到自定义好的对应类对象中
  59 + Object result = null;
  60 + try {
  61 + result = restTemplate.getForObject(requestURI,Object.class);
  62 +
  63 + }catch (HttpClientErrorException httpClientErrorException) {
  64 + response.setStatus(httpClientErrorException.getStatusCode().value());
  65 + }
  66 + return result;
  67 + }
  68 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java 0 → 100644
  1 +//package com.genersoft.iot.vmp.vmanager.record;
  2 +//
  3 +//import com.alibaba.fastjson.JSONObject;
  4 +//import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
  5 +//import com.genersoft.iot.vmp.service.IRecordInfoServer;
  6 +//import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo;
  7 +//import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  8 +//import com.github.pagehelper.PageInfo;
  9 +//import io.swagger.annotations.Api;
  10 +//import io.swagger.annotations.ApiImplicitParam;
  11 +//import io.swagger.annotations.ApiImplicitParams;
  12 +//import io.swagger.annotations.ApiOperation;
  13 +//import org.springframework.beans.factory.annotation.Autowired;
  14 +//import org.springframework.web.bind.annotation.*;
  15 +//
  16 +//@Api(tags = "云端录像")
  17 +//@CrossOrigin
  18 +//@RestController
  19 +//@RequestMapping("/api/record")
  20 +//public class RecordController {
  21 +//
  22 +// @Autowired
  23 +// private IRecordInfoServer recordInfoServer;
  24 +//
  25 +// @ApiOperation("录像列表查询")
  26 +// @ApiImplicitParams({
  27 +// @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class),
  28 +// @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class),
  29 +// @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class),
  30 +// })
  31 +// @GetMapping(value = "/app/list")
  32 +// @ResponseBody
  33 +// public Object list(@RequestParam(required = false)Integer page,
  34 +// @RequestParam(required = false)Integer count ){
  35 +//
  36 +// PageInfo<RecordInfo> recordList = recordInfoServer.getRecordList(page - 1, page - 1 + count);
  37 +// return recordList;
  38 +// }
  39 +//
  40 +// @ApiOperation("获取录像详情")
  41 +// @ApiImplicitParams({
  42 +// @ApiImplicitParam(name="recordInfo", value = "录像记录", required = true, dataTypeClass = RecordInfo.class)
  43 +// })
  44 +// @GetMapping(value = "/detail")
  45 +// @ResponseBody
  46 +// public JSONObject list(RecordInfo recordInfo, String time ){
  47 +//
  48 +//
  49 +// return null;
  50 +// }
  51 +//}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
1 1 package com.genersoft.iot.vmp.vmanager.server;
2 2  
3 3 import com.genersoft.iot.vmp.VManageBootstrap;
  4 +import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
  5 +import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  6 +import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl;
4 7 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
5 8 import gov.nist.javax.sip.SipStackImpl;
6 9 import io.swagger.annotations.Api;
... ... @@ -12,6 +15,7 @@ import org.springframework.web.bind.annotation.*;
12 15 import javax.sip.ListeningPoint;
13 16 import javax.sip.ObjectInUseException;
14 17 import javax.sip.SipProvider;
  18 +import java.util.ArrayList;
15 19 import java.util.Iterator;
16 20  
17 21 @SuppressWarnings("rawtypes")
... ... @@ -24,6 +28,20 @@ public class ServerController {
24 28 @Autowired
25 29 private ConfigurableApplicationContext context;
26 30  
  31 + @Autowired
  32 + private IRedisCatchStorage redisCatchStorage;
  33 +
  34 +
  35 + @ApiOperation("流媒体服务列表")
  36 + @GetMapping(value = "/media_server/list")
  37 + @ResponseBody
  38 + public Object getMediaServerList(){
  39 + // TODO 为后续多个zlm支持准备
  40 + ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
  41 + ArrayList<ZLMServerConfig> result = new ArrayList<>();
  42 + result.add(mediaInfo);
  43 + return result;
  44 + }
27 45  
28 46 @ApiOperation("重启服务")
29 47 @GetMapping(value = "/restart")
... ...
src/main/resources/application-dev.yml
... ... @@ -92,6 +92,8 @@ media:
92 92 enable: true
93 93 # [可选] 在此范围内选择端口用于媒体流传输,
94 94 portRange: 30000,30500 # 端口范围
  95 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载
  96 + recordAssistPort: 18081
95 97  
96 98 # [可选] 日志配置, 一般不需要改
97 99 logging:
... ... @@ -118,6 +120,8 @@ userSettings:
118 120 waitTrack: false
119 121 # 是否开启接口鉴权
120 122 interfaceAuthentication: true
  123 + # 推流直播是否录制
  124 + recordPushLive: true
121 125  
122 126 # 在线文档: swagger-ui(生产环境建议关闭)
123 127 springfox:
... ...
src/main/resources/wvp.sqlite
No preview for this file type