pom.xml
@@ -45,6 +45,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <!-- 依赖版本 --> <pagehelper.version>5.2.0</pagehelper.version> src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java
@@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; @@ -17,46 +18,21 @@ private Logger logger = LoggerFactory.getLogger("ApplicationCheckRunner"); @Value("${sip.ip}") private String sipIp; @Autowired private MediaConfig mediaConfig; @Value("${media.ip}") private String mediaIp; @Value("${media.wanIp}") private String mediaWanIp; @Value("${media.hookIp}") private String mediaHookIp; @Value("${media.port}") private int mediaPort; @Value("${media.secret}") private String mediaSecret; @Value("${media.streamNoneReaderDelayMS}") private String streamNoneReaderDelayMS; @Value("${sip.ip}") private String sipIP; @Value("${server.port}") private String serverPort; @Value("${media.autoConfig}") private boolean autoConfig; @Autowired private SipConfig sipConfig; @Override public void run(String... args) throws Exception { if (sipIP.equals("localhost") || sipIP.equals("127.0.0.1")) { logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipIP ); if (sipConfig.getSipIp().equals("localhost") || sipConfig.getSipIp().equals("127.0.0.1")) { logger.error("sip.ip不能使用 {} ,请使用类似192.168.1.44这样的来自网卡的IP!!!", sipConfig.getSipIp() ); System.exit(1); } if (mediaIp.equals("localhost") || (mediaIp.equals("127.0.0.1") && mediaWanIp == null)) { logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaIp ); if (mediaConfig.getIp().equals("localhost") || (mediaConfig.getIp().equals("127.0.0.1") && mediaConfig.getWanIp() == null)) { logger.warn("mediaIp.ip使用 {} ,将无法收到网络内其他设备的推流!!!", mediaConfig.getIp() ); } } src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
New file @@ -0,0 +1,177 @@ package com.genersoft.iot.vmp.conf; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @Configuration("mediaConfig") public class MediaConfig { @Value("${media.ip}") private String ip; @Value("${media.wanIp}") private String wanIp; @Value("${media.hookIp}") private String hookIp; @Value("${media.httpPort}") private String httpPort; @Value("${media.httpSSlPort}") private String httpSSlPort; @Value("${media.rtmpPort}") private String rtmpPort; @Value("${media.rtmpSSlPort}") private String rtmpSSlPort; @Value("${media.rtpProxyPort}") private String rtpProxyPort; @Value("${media.rtspPort}") private String rtspPort; @Value("${media.rtspSSLPort}") private String rtspSSLPort; @Value("${media.autoConfig}") private boolean autoConfig; @Value("${media.secret}") private String secret; @Value("${media.streamNoneReaderDelayMS}") private String streamNoneReaderDelayMS; @Value("${media.rtp.enable}") private boolean rtpEnable; @Value("${media.rtp.portRange}") private String rtpPortRange; public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getWanIp() { return wanIp; } public void setWanIp(String wanIp) { this.wanIp = wanIp; } public String getHookIp() { return hookIp; } public void setHookIp(String hookIp) { this.hookIp = hookIp; } public String getHttpPort() { return httpPort; } public void setHttpPort(String httpPort) { this.httpPort = httpPort; } public boolean isAutoConfig() { return autoConfig; } public boolean getAutoConfig() { return autoConfig; } public void setAutoConfig(boolean autoConfig) { this.autoConfig = autoConfig; } public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public String getStreamNoneReaderDelayMS() { return streamNoneReaderDelayMS; } public void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS) { this.streamNoneReaderDelayMS = streamNoneReaderDelayMS; } public boolean isRtpEnable() { return rtpEnable; } public void setRtpEnable(boolean rtpEnable) { this.rtpEnable = rtpEnable; } public String getRtpPortRange() { return rtpPortRange; } public void setRtpPortRange(String rtpPortRange) { this.rtpPortRange = rtpPortRange; } public String getHttpSSlPort() { return httpSSlPort; } public void setHttpSSlPort(String httpSSlPort) { this.httpSSlPort = httpSSlPort; } public String getRtmpPort() { return rtmpPort; } public void setRtmpPort(String rtmpPort) { this.rtmpPort = rtmpPort; } public String getRtmpSSlPort() { return rtmpSSlPort; } public void setRtmpSSlPort(String rtmpSSlPort) { this.rtmpSSlPort = rtmpSSlPort; } public String getRtpProxyPort() { return rtpProxyPort; } public void setRtpProxyPort(String rtpProxyPort) { this.rtpProxyPort = rtpProxyPort; } public String getRtspPort() { return rtspPort; } public void setRtspPort(String rtspPort) { this.rtspPort = rtspPort; } public String getRtspSSLPort() { return rtspSSLPort; } public void setRtspSSLPort(String rtspSSLPort) { this.rtspSSLPort = rtspSSLPort; } } src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
@@ -77,6 +77,7 @@ // 可以直接访问的静态数据 web.ignoring() .antMatchers("/") .antMatchers("/#/**") .antMatchers("/static/**") .antMatchers("/index.html") .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**" @@ -111,7 +112,7 @@ http.headers().contentTypeOptions().disable(); http.authorizeRequests() // 放行接口 .antMatchers("/#/**", "/api/user/login","/index/hook/**").permitAll() .antMatchers("/api/user/login","/index/hook/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() // 异常处理(权限拒绝、登录失效等) src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
@@ -38,7 +38,7 @@ public void onMessage(Message message, byte[] pattern) { // 获取失效的key String expiredKey = message.toString(); logger.info(expiredKey); logger.debug(expiredKey); if(!expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)){ logger.debug("收到redis过期监听,但开头不是"+VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX+",忽略"); return; src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -1,8 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; import java.text.ParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.sip.*; import javax.sip.address.SipURI; @@ -10,11 +8,11 @@ import javax.sip.header.ViaHeader; import javax.sip.message.Request; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; @@ -82,13 +80,13 @@ @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @Value("${media.rtp.enable}") private boolean rtpEnable; @Autowired private MediaConfig mediaConfig; @Value("${media.seniorSdp}") @Value("${userSettings.seniorSdp}") private boolean seniorSdp; @Value("${media.autoApplyPlay}") @Value("${userSettings.autoApplyPlay}") private boolean autoApplyPlay; @Value("${userSettings.waitTrack}") @@ -353,20 +351,20 @@ try { if (device == null) return; String ssrc = streamSession.createPlaySsrc(); if (rtpEnable) { if (mediaConfig.isRtpEnable()) { streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); }else { streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); } String streamMode = device.getStreamMode().toUpperCase(); MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); if (mediaInfo == null) { logger.warn("点播时发现ZLM尚未连接..."); return; } String mediaPort = null; // 使用动态udp端口 if (rtpEnable) { if (mediaConfig.isRtpEnable()) { mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; }else { mediaPort = mediaInfo.getRtpProxyPort(); @@ -470,7 +468,7 @@ public void playbackStreamCmd(Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event , SipSubscribe.Event errorEvent) { try { MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String ssrc = streamSession.createPlayBackSsrc(); String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); // 添加订阅 @@ -495,7 +493,7 @@ +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); String mediaPort = null; // 使用动态udp端口 if (rtpEnable) { if (mediaConfig.isRtpEnable()) { mediaPort = zlmrtpServerFactory.createRTPServer(streamId) + ""; }else { mediaPort = mediaInfo.getRtpProxyPort(); @@ -1445,7 +1443,7 @@ @Override public void closeRTPServer(Device device, String channelId) { if (rtpEnable) { if (mediaConfig.isRtpEnable()) { String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId); zlmrtpServerFactory.closeRTPServer(streamId); } src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; @@ -62,9 +63,6 @@ @Autowired @Qualifier(value="udpSipProvider") private SipProvider udpSipProvider; @Value("${media.rtp.enable}") private boolean rtpEnable; @Override public boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) { src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
@@ -11,7 +11,7 @@ import javax.sip.message.Request; import javax.sip.message.Response; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; @@ -187,7 +187,7 @@ sendRtpItem.setStatus(1); redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); @@ -246,7 +246,7 @@ sendRtpItem.setStatus(1); redisCatchStorage.updateSendRTPSever(sendRtpItem); // TODO 添加对tcp的支持 MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StringBuffer content = new StringBuffer(200); content.append("v=0\r\n"); content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n"); src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
@@ -17,7 +17,9 @@ import javax.sip.message.Response; import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate; import gov.nist.javax.sip.RequestEventExt; import gov.nist.javax.sip.header.SIPDateHeader; import gov.nist.javax.sip.message.SIPRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; @@ -59,7 +61,9 @@ @Override public void process(RequestEvent evt) { try { logger.info("收到注册请求,开始处理"); RequestEventExt evtExt = (RequestEventExt)evt; String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); logger.info("[{}] 收到注册请求,开始处理", requestAddress); Request request = evt.getRequest(); Response response = null; @@ -78,9 +82,9 @@ if (authorhead == null || !passwordCorrect) { if (authorhead == null) { logger.info("未携带授权头 回复401"); logger.info("[{}] 未携带授权头 回复401", requestAddress); } else if (!passwordCorrect) { logger.info("密码错误 回复401"); logger.info("[{}] 密码错误 回复401", requestAddress); } response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getSipDomain()); @@ -147,7 +151,7 @@ // 保存到redis // 下发catelog查询目录 if (registerFlag == 1 ) { logger.info("注册成功! deviceId:" + device.getDeviceId()); logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress); // boolean exists = storager.exists(device.getDeviceId()); device.setRegisterTimeMillis(System.currentTimeMillis()); storager.updateDevice(device); @@ -158,7 +162,7 @@ handler.onRegister(device); //} } else if (registerFlag == 2) { logger.info("注销成功! deviceId:" + device.getDeviceId()); logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress); publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); } } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java
@@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.media.zlm; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -18,14 +18,11 @@ // private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class); // @Autowired // private IVideoManagerStorager storager; @Autowired private IRedisCatchStorage redisCatchStorage; @Value("${media.port}") private int mediaHttpPort; @Autowired private MediaConfig mediaConfig; @ResponseBody @RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8") @@ -34,10 +31,10 @@ if (redisCatchStorage.getMediaInfo() == null) { return "未接入流媒体"; } MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String requestURI = String.format("http://%s:%s%s?%s&%s", mediaInfo.getLocalIP(), mediaHttpPort, mediaConfig.getHttpPort(), request.getRequestURI().replace("/zlm",""), mediaInfo.getHookAdminParams(), request.getQueryString() src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -6,7 +6,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -53,23 +53,20 @@ @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired private ZLMServerManger zlmServerManger; @Autowired private ZLMMediaListManager zlmMediaListManager; @Autowired private ZLMHttpHookSubscribe subscribe; @Value("${media.autoApplyPlay}") @Value("${userSettings.autoApplyPlay}") private boolean autoApplyPlay; @Value("${media.ip}") private String mediaIp; @Value("${media.wanIp}") private String mediaWanIp; @Value("${media.port}") private int mediaPort; @Autowired private MediaConfig mediaConfig; /** * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 @@ -388,12 +385,8 @@ subscribe.response(json); } } MediaServerConfig mediaServerConfig = JSON.toJavaObject(json, MediaServerConfig.class); mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); mediaServerConfig.setLocalIP(mediaIp); redisCatchStorage.updateMediaInfo(mediaServerConfig); ZLMServerConfig ZLMServerConfig = JSON.toJavaObject(json, ZLMServerConfig.class); zlmServerManger.updateServerCatch(ZLMServerConfig); // 重新发起代理 JSONObject ret = new JSONObject(); src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
@@ -1,9 +1,7 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.gb28181.bean.GbStream; @@ -126,7 +124,7 @@ public void clearAllSessions() { logger.info("清空所有国标相关的session"); JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); HashSet<String> allLocalPorts = new HashSet(); if (allSessionJSON.getInteger("code") == 0) { JSONArray data = allSessionJSON.getJSONArray("data"); src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -2,10 +2,13 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.MediaConfig; import okhttp3.*; import org.checkerframework.checker.units.qual.A; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -20,14 +23,8 @@ private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); @Value("${media.ip}") private String mediaIp; @Value("${media.port}") private int mediaPort; @Value("${media.secret}") private String mediaSecret; @Autowired private MediaConfig mediaConfig; public interface RequestCallback{ void run(JSONObject response); @@ -35,12 +32,12 @@ public JSONObject sendPost(String api, Map<String, Object> param, RequestCallback callback) { OkHttpClient client = new OkHttpClient(); String url = String.format("http://%s:%s/index/api/%s", mediaIp, mediaPort, api); String url = String.format("http://%s:%s/index/api/%s", mediaConfig.getIp(), mediaConfig.getHttpPort(), api); JSONObject responseJSON = null; logger.debug(url); FormBody.Builder builder = new FormBody.Builder(); builder.add("secret",mediaSecret); builder.add("secret",mediaConfig.getSecret()); if (param != null && param.keySet().size() > 0) { for (String key : param.keySet()){ if (param.get(key) != null) { src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -2,6 +2,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; import com.genersoft.iot.vmp.gb28181.session.SsrcUtil; import org.slf4j.Logger; @@ -18,8 +19,8 @@ private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); @Value("${media.rtp.portRange}") private String portRange; @Autowired private MediaConfig mediaConfig; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -103,7 +104,7 @@ private int getPortFromportRange() { if (currentPort == 0) { String[] portRangeStrArray = portRange.split(","); String[] portRangeStrArray = mediaConfig.getRtpPortRange().split(","); portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]); portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]); } src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -3,7 +3,8 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; //import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -34,35 +35,19 @@ @Autowired private IRedisCatchStorage redisCatchStorage; @Value("${media.ip}") private String mediaIp; @Autowired private MediaConfig mediaConfig; @Value("${media.wanIp}") private String mediaWanIp; @Value("${media.hookIp}") private String mediaHookIp; @Value("${media.port}") private int mediaPort; @Value("${media.secret}") private String mediaSecret; @Value("${media.streamNoneReaderDelayMS}") private String streamNoneReaderDelayMS; @Value("${sip.ip}") private String sipIP; @Autowired private SipConfig sipConfig; @Value("${server.port}") private String serverPort; @Value("${media.autoConfig}") private boolean autoConfig; @Value("${server.ssl.enabled}") private boolean sslEnabled; private boolean startGetMedia = false; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -74,32 +59,37 @@ private ZLMHttpHookSubscribe hookSubscribe; @Autowired private ZLMServerManger zlmServerManger; @Autowired private IStreamProxyService streamProxyService; @Override public void run(String... strings) throws Exception { // 订阅 zlm启动事件 hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,(response)->{ MediaServerConfig mediaServerConfig = JSONObject.toJavaObject(response, MediaServerConfig.class); zLmRunning(mediaServerConfig); ZLMServerConfig ZLMServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); zLmRunning(ZLMServerConfig); }); // 获取zlm信息 logger.info("等待zlm接入..."); MediaServerConfig mediaServerConfig = getMediaServerConfig(); startGetMedia = true; ZLMServerConfig ZLMServerConfig = getMediaServerConfig(); if (mediaServerConfig != null) { zLmRunning(mediaServerConfig); if (ZLMServerConfig != null) { zLmRunning(ZLMServerConfig); } } public MediaServerConfig getMediaServerConfig() { public ZLMServerConfig getMediaServerConfig() { if (!startGetMedia) return null; JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(); MediaServerConfig mediaServerConfig = null; ZLMServerConfig ZLMServerConfig = null; if (responseJSON != null) { JSONArray data = responseJSON.getJSONArray("data"); if (data != null && data.size() > 0) { mediaServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), MediaServerConfig.class); ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); } } else { @@ -109,20 +99,18 @@ } catch (InterruptedException e) { e.printStackTrace(); } mediaServerConfig = getMediaServerConfig(); ZLMServerConfig = getMediaServerConfig(); } return mediaServerConfig; return ZLMServerConfig; } private void saveZLMConfig() { logger.info("设置zlm..."); if (StringUtils.isEmpty(mediaHookIp)) { mediaHookIp = sipIP; } if (StringUtils.isEmpty(mediaConfig.getHookIp())) mediaConfig.setHookIp(sipConfig.getSipIp()); String protocol = sslEnabled ? "https" : "http"; String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaHookIp, serverPort); String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaConfig.getHookIp(), serverPort); Map<String, Object> param = new HashMap<>(); param.put("api.secret",mediaSecret); // -profile:v Baseline param.put("api.secret",mediaConfig.getSecret()); // -profile:v Baseline 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"); param.put("hook.enable","1"); param.put("hook.on_flow_report",""); @@ -139,7 +127,7 @@ param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); param.put("hook.timeoutSec","20"); param.put("general.streamNoneReaderDelayMS",streamNoneReaderDelayMS); param.put("general.streamNoneReaderDelayMS",mediaConfig.getStreamNoneReaderDelayMS()); JSONObject responseJSON = zlmresTfulUtils.setServerConfig(param); @@ -153,17 +141,12 @@ /** * zlm 连接成功或者zlm重启后 */ private void zLmRunning(MediaServerConfig mediaServerConfig){ logger.info( "[ id: " + mediaServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); if (autoConfig) saveZLMConfig(); MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); if (mediaInfo != null && System.currentTimeMillis() - mediaInfo.getUpdateTime() < 50){ logger.info("[ id: " + mediaServerConfig.getGeneralMediaServerId() + "]zlm刚刚更新,忽略这次更新"); return; } mediaServerConfig.setLocalIP(mediaIp); mediaServerConfig.setWanIp(StringUtils.isEmpty(mediaWanIp)? mediaIp: mediaWanIp); redisCatchStorage.updateMediaInfo(mediaServerConfig); private void zLmRunning(ZLMServerConfig zlmServerConfig){ logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); // 关闭循环获取zlm配置 startGetMedia = false; if (mediaConfig.getAutoConfig()) saveZLMConfig(); zlmServerManger.updateServerCatch(zlmServerConfig); // 清空所有session // zlmMediaListManager.clearAllSessions(); src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
File was renamed from src/main/java/com/genersoft/iot/vmp/conf/MediaServerConfig.java @@ -1,8 +1,8 @@ package com.genersoft.iot.vmp.conf; package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.annotation.JSONField; public class MediaServerConfig { public class ZLMServerConfig { @JSONField(name = "api.apiDebug") private String apiDebug; @@ -156,6 +156,9 @@ @JSONField(name = "rtmp.port") private String rtmpPort; @JSONField(name = "rtmp.sslport") private String rtmpSslPort; @JSONField(name = "rtp.audioMtuSize") private String rtpAudioMtuSize; @@ -749,4 +752,12 @@ public void setGeneralMediaServerId(String generalMediaServerId) { this.generalMediaServerId = generalMediaServerId; } public String getRtmpSslPort() { return rtmpSslPort; } public void setRtmpSslPort(String rtmpSslPort) { this.rtmpSslPort = rtmpSslPort; } } src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerManger.java
New file @@ -0,0 +1,45 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson.annotation.JSONField; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @Component public class ZLMServerManger { @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired private MediaConfig mediaConfig; public void updateServerCatch(ZLMServerConfig zlmServerConfig) { zlmServerConfig.setLocalIP(mediaConfig.getIp()); zlmServerConfig.setWanIp(StringUtils.isEmpty(mediaConfig.getWanIp())? mediaConfig.getIp(): mediaConfig.getWanIp()); zlmServerConfig.setHttpPort(mediaConfig.getHttpPort()); if(!StringUtils.isEmpty(mediaConfig.getHttpSSlPort())) zlmServerConfig.setHttpSSLport(mediaConfig.getHttpSSlPort()); if(!StringUtils.isEmpty(mediaConfig.getRtspPort())) zlmServerConfig.setRtspPort(mediaConfig.getRtspPort()); if(!StringUtils.isEmpty(mediaConfig.getRtspSSLPort())) zlmServerConfig.setRtspSSlport(mediaConfig.getRtspSSLPort()); if(!StringUtils.isEmpty(mediaConfig.getRtmpPort())) zlmServerConfig.setRtmpPort(mediaConfig.getRtmpPort()); if(!StringUtils.isEmpty(mediaConfig.getRtmpSSlPort())) zlmServerConfig.setRtmpSslPort(mediaConfig.getRtmpSSlPort()); if(!StringUtils.isEmpty(mediaConfig.getRtpProxyPort())) zlmServerConfig.setRtpProxyPort(mediaConfig.getRtpProxyPort()); redisCatchStorage.updateMediaInfo(zlmServerConfig); } } src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; @@ -28,7 +28,7 @@ @Override public StreamInfo getStreamInfoByAppAndStream(String app, String stream, JSONArray tracks) { MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); StreamInfo streamInfoResult = new StreamInfo(); streamInfoResult.setStreamId(stream); streamInfoResult.setApp(app); src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.service.impl; import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; @@ -41,7 +41,7 @@ @Override public String save(StreamProxyItem param) { MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), param.getStream() ); param.setDst_url(dstUrl); src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
@@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.storager; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; @@ -41,16 +41,16 @@ /** * 更新流媒体信息 * @param mediaServerConfig * @param ZLMServerConfig * @return */ boolean updateMediaInfo(MediaServerConfig mediaServerConfig); boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig); /** * 获取流媒体信息 * @return */ MediaServerConfig getMediaInfo(); ZLMServerConfig getMediaInfo(); Map<String, StreamInfo> queryPlayByDeviceId(String deviceId); src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -2,7 +2,7 @@ import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; @@ -86,13 +86,13 @@ /** * 更新流媒体信息 * @param mediaServerConfig * @param ZLMServerConfig * @return */ @Override public boolean updateMediaInfo(MediaServerConfig mediaServerConfig) { mediaServerConfig.setUpdateTime(System.currentTimeMillis()); return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX,mediaServerConfig); public boolean updateMediaInfo(ZLMServerConfig ZLMServerConfig) { ZLMServerConfig.setUpdateTime(System.currentTimeMillis()); return redis.set(VideoManagerConstants.MEDIA_SERVER_PREFIX, ZLMServerConfig); } /** @@ -100,8 +100,8 @@ * @return */ @Override public MediaServerConfig getMediaInfo() { return (MediaServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); public ZLMServerConfig getMediaInfo() { return (ZLMServerConfig)redis.get(VideoManagerConstants.MEDIA_SERVER_PREFIX); } @Override src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
@@ -1,7 +1,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.MediaServerConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; @@ -162,7 +162,7 @@ logger.warn("视频转码API调用失败!, 视频流已停止推流!"); return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); } else { MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), streamId ); String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); src/main/resources/application-dev.yml
@@ -67,17 +67,25 @@ # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip hookIp: # [必须修改] zlm服务器的http.port port: 80 httpPort: 80 # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 httpSSlPort: # [可选] zlm服务器的rtmp.port, 置空使用zlm配置文件配置 rtmpPort: # [可选] zlm服务器的rtmp.sslport, 置空使用zlm配置文件配置 rtmpSSlPort: # [可选] zlm服务器的 rtp_proxy.port, 置空使用zlm配置文件配置 rtpProxyPort: # [可选] zlm服务器的 rtsp.port, 置空使用zlm配置文件配置 rtspPort: # [可选] zlm服务器的 rtsp.sslport, 置空使用zlm配置文件配置 rtspSSLPort: # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 autoConfig: true # [可选] zlm服务器的hook.admin_params=secret secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc # [可选] zlm服务器的general.streamNoneReaderDelayMS streamNoneReaderDelayMS: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流 # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true autoApplyPlay: false # [可选] 部分设备需要扩展SDP,需要打开此设置 seniorSdp: false # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 rtp: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 @@ -95,9 +103,13 @@ level: com: genersoft: iot: debug iot: info # [根据业务需求配置] userSettings: # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true autoApplyPlay: false # [可选] 部分设备需要扩展SDP,需要打开此设置 seniorSdp: false # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) savePositionHistory: false # 点播等待超时时间,单位:毫秒