panlinlin
2021-04-28 10ae1bba597e562a20f44f777ae5afdefb5da6af
增加对zlm使用docker容器的支持
20个文件已修改
2个文件已添加
1 文件已重命名
542 ■■■■■ 已修改文件
pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/ApplicationCheckRunner.java 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHTTPProxyController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerManger.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
    # 点播等待超时时间,单位:毫秒