From f2c78c4b78b8aa245d41f8f884ac1f8a70f43519 Mon Sep 17 00:00:00 2001 From: shikong <919411476@qq.com> Date: Wed, 9 Aug 2023 16:09:23 +0800 Subject: [PATCH] =?UTF-8?q?sip=20=E7=9B=91=E5=90=AC=E5=88=9B=E5=BB=BA/?= =?UTF-8?q?=E9=94=80=E6=AF=81=20=E6=94=AF=E6=8C=81=20hotReload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/pom.xml | 12 ++ common/pom.xml | 5 + .../gb28181/common/json/JsonResponse.java | 56 ++++++++++ .../gb28181/common/json/JsonUtils.java | 81 ++++++++++++++ .../gb28181/common/json/ResponseStatus.java | 60 ++++++++++ gb28181-service/pom.xml | 11 ++ .../docking/gb28181/config/sip/SipConfig.java | 42 +++++++ .../docking/gb28181/core/sip/SipService.java | 6 + .../gb28181/core/sip/SipServiceImpl.java | 88 +++++++++++++++ .../core/sip/logger/ServerLoggerImpl.java | 79 +++++++++++++ .../core/sip/logger/StackLoggerImpl.java | 105 ++++++++++++++++++ .../sip/properties/DefaultProperties.java | 62 +++++++++++ .../docking/gb28181/starter/SipStarter.java | 53 +++++++++ starter/pom.xml | 6 + starter/src/test/resources/application.yml | 25 +++++ 15 files changed, 691 insertions(+) create mode 100644 common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonResponse.java create mode 100644 common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java create mode 100644 common/src/main/java/cn/skcks/docking/gb28181/common/json/ResponseStatus.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/config/sip/SipConfig.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipService.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipServiceImpl.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/ServerLoggerImpl.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/StackLoggerImpl.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/properties/DefaultProperties.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/starter/SipStarter.java create mode 100644 starter/src/test/resources/application.yml diff --git a/api/pom.xml b/api/pom.xml index 8f5d5aa..2d2c699 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,4 +18,16 @@ UTF-8 + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + org.springdoc + springdoc-openapi-starter-common + + diff --git a/common/pom.xml b/common/pom.xml index ff69ca3..8bebbf6 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -80,6 +80,11 @@ redis.clients jedis + + + org.apache.commons + commons-lang3 + diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonResponse.java b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonResponse.java new file mode 100644 index 0000000..bbb1bd2 --- /dev/null +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonResponse.java @@ -0,0 +1,56 @@ +package cn.skcks.docking.gb28181.common.json; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@SuppressWarnings("unused") +@Schema(title = "返回结果") +public class JsonResponse { + @Schema(title = "状态码") + private int code; + + @Schema(title = "响应消息") + private String msg; + + @Schema(title = "响应数据") + private T data; + + public JsonResponse(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + public static JsonResponse success(T data) { + return JsonResponse.build(data, ResponseStatus.OK); + } + + public static JsonResponse success(T data, String message) { + return JsonResponse.build(data, ResponseStatus.OK.getCode(), message); + } + + public static JsonResponse error(T data) { + return JsonResponse.build(data, ResponseStatus.INTERNAL_SERVER_ERROR); + } + + public static JsonResponse error(T data, String message) { + return JsonResponse.build(data, ResponseStatus.INTERNAL_SERVER_ERROR.getCode(), message); + } + + public static JsonResponse build(ResponseStatus status) { + return new JsonResponse<>(status.getCode(), status.getMessage(),null); + } + + public static JsonResponse build(T data, ResponseStatus status) { + return new JsonResponse<>(status.getCode(), status.getMessage(), data); + } + + public static JsonResponse build(ResponseStatus status,String message) { + return new JsonResponse<>(status.getCode(), message, null); + } + + public static JsonResponse build(T data, int status, String msg) { + return new JsonResponse<>(status, msg, data); + } +} diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java new file mode 100644 index 0000000..3859a77 --- /dev/null +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/json/JsonUtils.java @@ -0,0 +1,81 @@ +package cn.skcks.docking.gb28181.common.json; + +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +@SuppressWarnings("unused") +public class JsonUtils { + public static final ObjectMapper mapper; + public static final ObjectMapper compressMapper; + + static { + mapper = new ObjectMapper(); + compressMapper = new ObjectMapper(); + + mapper.enable(SerializationFeature.INDENT_OUTPUT); + compressMapper.disable(SerializationFeature.INDENT_OUTPUT); + + // 返回内容中 不含 值为 null 的字段 + // mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + compressMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + + // 如果json中有新增的字段并且是实体类类中不存在的,不报错 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + + compressMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + compressMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + + // 允许出现特殊字符和转义符 + mapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + compressMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + + // 允许出现单引号 + mapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true); + compressMapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true); + } + + public static T parse(String json, Class clazz) { + try { + return mapper.readValue(json, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T parse(String json, TypeReference clazz) { + try { + return mapper.readValue(json, clazz); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String toCompressJson(Object obj) { + try { + return compressMapper.writeValueAsString(obj); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String toJson(Object obj) { + try { + return mapper.writeValueAsString(obj); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static T convert(Object object, Class clazz) { + return JsonUtils.parse(JsonUtils.toJson(object), clazz); + } +} diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/json/ResponseStatus.java b/common/src/main/java/cn/skcks/docking/gb28181/common/json/ResponseStatus.java new file mode 100644 index 0000000..8465d00 --- /dev/null +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/json/ResponseStatus.java @@ -0,0 +1,60 @@ +package cn.skcks.docking.gb28181.common.json; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@SuppressWarnings("unused") +public enum ResponseStatus { + UNKNOWN(-1, "Unknown"), + UNDEFINED(0, "Undefined"), + OK(200, "OK"), + CREATED(201, "Created"), + ACCEPTED(202, "Accepted"), + NO_CONTENT(204, "No Content"), + MOVED_PERMANENTLY(301, "Moved Permanently"), + FOUND(302, "Found"), + SEE_OTHER(303, "See Other"), + NOT_MODIFIED(304, "Not Modified"), + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + BAD_REQUEST(400, "Bad Request"), + UNAUTHORIZED(401, "Unauthorized"), + FORBIDDEN(403, "Forbidden"), + NOT_FOUND(404, "Not Found"), + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + NOT_ACCEPTABLE(406, "Not Acceptable"), + REQUEST_TIMEOUT(408, "Request Timeout"), + CONFLICT(409, "Conflict"), + GONE(410, "Gone"), + LENGTH_REQUIRED(411, "Length Required"), + PRECONDITION_FAILED(412, "Precondition Failed"), + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + URI_TOO_LONG(414, "URI Too Long"), + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + RANGE_NOT_SATISFIABLE(416, "Range Not Satisfiable"), + EXPECTATION_FAILED(417, "Expectation Failed"), + TOO_MANY_REQUESTS(429, "Too Many Requests"), + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + NOT_IMPLEMENTED(501, "Not Implemented"), + BAD_GATEWAY(502, "Bad Gateway"), + SERVICE_UNAVAILABLE(503, "Service Unavailable"); + + + private final int code; + private final String message; + + public static ResponseStatus getByCode(int code) { + for (ResponseStatus status : values()) { + if (status.getCode() == code) { + return status; + } + } + return null; + } + + public static ResponseStatus valueOf(int code) { + return getByCode(code); + } +} + diff --git a/gb28181-service/pom.xml b/gb28181-service/pom.xml index d7bd25a..5e77728 100644 --- a/gb28181-service/pom.xml +++ b/gb28181-service/pom.xml @@ -19,6 +19,17 @@ + + cn.skcks.docking.gb28181 + common + ${project.version} + + + + org.springframework.boot + spring-boot + + javax.sip diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/config/sip/SipConfig.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/config/sip/SipConfig.java new file mode 100644 index 0000000..782439a --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/config/sip/SipConfig.java @@ -0,0 +1,42 @@ +package cn.skcks.docking.gb28181.config.sip; + + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@ConfigurationProperties(prefix = "gb28181.sip", ignoreInvalidFields = true) +@Order(0) +@Data +public class SipConfig { + + private List ip; + + private List showIp; + + private Integer port; + + private String domain; + + private String id; + + private String password; + + Integer ptzSpeed = 50; + + Integer registerTimeInterval = 120; + + private boolean alarm; + + public List getShowIp() { + if (this.showIp == null) { + return this.ip; + } + return showIp; + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipService.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipService.java new file mode 100644 index 0000000..3472648 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipService.java @@ -0,0 +1,6 @@ +package cn.skcks.docking.gb28181.core.sip; + +public interface SipService { + void run(); + void stop(); +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipServiceImpl.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipServiceImpl.java new file mode 100644 index 0000000..5839a45 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/SipServiceImpl.java @@ -0,0 +1,88 @@ +package cn.skcks.docking.gb28181.core.sip; + +import cn.skcks.docking.gb28181.config.sip.SipConfig; +import cn.skcks.docking.gb28181.core.sip.properties.DefaultProperties; +import gov.nist.javax.sip.SipProviderImpl; +import gov.nist.javax.sip.SipStackImpl; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.sip.*; +import java.util.ArrayList; +import java.util.List; +import java.util.TooManyListenersException; + +@Slf4j +@RequiredArgsConstructor +@Data +@Service +public class SipServiceImpl implements SipService{ + private final SipFactory sipFactory = SipFactory.getInstance(); + private final SipConfig sipConfig; + private final List pool = new ArrayList<>(2); + private SipStackImpl sipStack; + + @Override + public void run() { + sipFactory.setPathName("gov.nist"); + sipConfig.getIp().parallelStream().forEach(ip->{ + listen(ip, sipConfig.getPort()); + }); + } + + @Override + public void stop() { + if(sipStack == null){ + return; + } + + sipStack.closeAllSockets(); + pool.parallelStream().forEach(sipProvider -> { + ListeningPoint listen = sipProvider.getListeningPoint(); + log.debug("移除监听 {}://{}:{}",listen.getTransport(),listen.getIPAddress(),listen.getPort()); + sipProvider.removeListeningPoints(); + + try{ + sipStack.deleteListeningPoint(listen); + sipStack.deleteSipProvider(sipProvider); + } catch (Exception ignore){ + } + }); + pool.clear(); + } + + public void listen(String ip, int port){ + try{ + sipStack = (SipStackImpl)sipFactory.createSipStack(DefaultProperties.getProperties("GB28181_SIP_LOG",true)); + + try { + ListeningPoint tcpListen = sipStack.createListeningPoint(ip, port, "TCP"); + SipProviderImpl tcpSipProvider = (SipProviderImpl) sipStack.createSipProvider(tcpListen); + tcpSipProvider.setDialogErrorsAutomaticallyHandled(); + pool.add(tcpSipProvider); + log.info("[sip] 监听 tcp://{}:{}", ip, port); + } catch (TransportNotSupportedException + | ObjectInUseException + | InvalidArgumentException e) { + log.error("[sip] tcp://{}:{} 监听失败, 请检查端口是否被占用, 错误信息 => {}", ip, port, e.getMessage()); + } + + try { + ListeningPoint udpListen = sipStack.createListeningPoint(ip, port, "UDP"); + SipProviderImpl udpSipProvider = (SipProviderImpl) sipStack.createSipProvider(udpListen); + pool.add(udpSipProvider); + log.info("[sip] 监听 udp://{}:{}", ip, port); + } catch (TransportNotSupportedException + | ObjectInUseException + | InvalidArgumentException e) { + log.error("[sip] udp://{}:{} 监听失败, 请检查端口是否被占用, 错误信息 => {}", ip, port, e.getMessage()); + } + } catch (Exception e){ + log.error("[sip] {}:{} 监听失败, 请检查端口是否被占用, 错误信息 => {}",ip,port, e.getMessage()); + } + + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/ServerLoggerImpl.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/ServerLoggerImpl.java new file mode 100644 index 0000000..3c455ed --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/ServerLoggerImpl.java @@ -0,0 +1,79 @@ +package cn.skcks.docking.gb28181.core.sip.logger; + +import gov.nist.core.CommonLogger; +import gov.nist.core.ServerLogger; +import gov.nist.core.StackLogger; +import gov.nist.javax.sip.message.SIPMessage; +import gov.nist.javax.sip.stack.SIPTransactionStack; + +import javax.sip.SipStack; +import java.util.Properties; + +public class ServerLoggerImpl implements ServerLogger { + + private boolean showLog = true; + + protected StackLogger stackLogger; + + @Override + public void closeLogFile() { + + } + + @Override + public void logMessage(SIPMessage message, String from, String to, boolean sender, long time) { + if (!showLog) { + return; + } + String log = (sender ? "发送: 目标 =>" + from : "接收: 来自 =>" + to) + "\r\n" + message; + this.stackLogger.logInfo(log); + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender, long time) { + if (!showLog) { + return; + } + String log = (sender ? "发送: 目标 =>" + from : "接收: 来自 =>" + to) + "\r\n" + message; + this.stackLogger.logInfo(log); + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender) { + if (!showLog) { + return; + } + String log = (sender ? "发送: 目标 =>" + from : "接收: 来自 =>" + to) + "\r\n" + message; + this.stackLogger.logInfo(log); + } + + @Override + public void logException(Exception ex) { + if (!showLog) { + return; + } + this.stackLogger.logException(ex); + } + + @Override + public void setStackProperties(Properties stackProperties) { + if (!showLog) { + return; + } + String TRACE_LEVEL = stackProperties.getProperty("gov.nist.javax.sip.TRACE_LEVEL"); + if (TRACE_LEVEL != null) { + showLog = true; + } + } + + @Override + public void setSipStack(SipStack sipStack) { + if (!showLog) { + return; + } + if(sipStack instanceof SIPTransactionStack) { + SIPTransactionStack sipStack1 = (SIPTransactionStack) sipStack; + this.stackLogger = CommonLogger.getLogger(SIPTransactionStack.class); + } + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/StackLoggerImpl.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/StackLoggerImpl.java new file mode 100644 index 0000000..5b420e6 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/logger/StackLoggerImpl.java @@ -0,0 +1,105 @@ +package cn.skcks.docking.gb28181.core.sip.logger; + +import gov.nist.core.StackLogger; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.Properties; + +@Slf4j +@Component +public class StackLoggerImpl implements StackLogger { + + private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class); + + @Override + public void logStackTrace() {} + + @Override + public void logStackTrace(int traceLevel) { + System.out.println("traceLevel: " + traceLevel); + } + + @Override + public int getLineCount() { + return 0; + } + + @Override + public void logException(Throwable ex) { + + } + + @Override + public void logDebug(String message) { + } + + @Override + public void logDebug(String message, Exception ex) { + } + + @Override + public void logTrace(String message) { + logger.trace(message); + } + + @Override + public void logFatalError(String message) { + } + + @Override + public void logError(String message) { + } + + @Override + public boolean isLoggingEnabled() { + return true; + } + + @Override + public boolean isLoggingEnabled(int logLevel) { + return true; + } + + @Override + public void logError(String message, Exception ex) { +// logger.error(message); + } + + @Override + public void logWarning(String message) { + logger.warn(message); + } + + @Override + public void logInfo(String message) { + logger.info(message); + } + + @Override + public void disableLogging() { + + } + + @Override + public void enableLogging() { + + } + + @Override + public void setBuildTimeStamp(String buildTimeStamp) { + + } + + @Override + public void setStackProperties(Properties stackProperties) { + + } + + @Override + public String getLoggerName() { + return null; + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/properties/DefaultProperties.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/properties/DefaultProperties.java new file mode 100644 index 0000000..80acbee --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/properties/DefaultProperties.java @@ -0,0 +1,62 @@ +package cn.skcks.docking.gb28181.core.sip.properties; + +import cn.skcks.docking.gb28181.config.sip.SipConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Properties; + +/** + * 获取sip默认配置 + */ +public class DefaultProperties { + + public static Properties getProperties(String name, boolean sipLog) { + Properties properties = new Properties(); + properties.setProperty("javax.sip.STACK_NAME", name); +// properties.setProperty("javax.sip.IP_ADDRESS", ip); + // 关闭自动会话 + properties.setProperty("javax.sip.AUTOMATIC_DIALOG_SUPPORT", "off"); + /** + * 完整配置参考 gov.nist.javax.sip.SipStackImpl,需要下载源码 + * gov/nist/javax/sip/SipStackImpl.class + * sip消息的解析在 gov.nist.javax.sip.stack.UDPMessageChannel的processIncomingDataPacket方法 + */ + +// * gov/nist/javax/sip/SipStackImpl.class + // 接收所有notify请求,即使没有订阅 + properties.setProperty("gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY", "true"); + properties.setProperty("gov.nist.javax.sip.AUTOMATIC_DIALOG_ERROR_HANDLING", "false"); + properties.setProperty("gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true"); + // 为_NULL _对话框传递_终止的_事件 + properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true"); + // 是否自动计算content length的实际长度,默认不计算 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "true"); + // 会话清理策略 + properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal"); + // 处理由该服务器处理的基于底层TCP的保持生存超时 + properties.setProperty("gov.nist.javax.sip.RELIABLE_CONNECTION_KEEP_ALIVE_TIMEOUT", "60"); + // 获取实际内容长度,不使用header中的长度信息 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "true"); + // 线程可重入 + properties.setProperty("gov.nist.javax.sip.REENTRANT_LISTENER", "true"); + // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位)) + properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000"); + +// properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "gov.nist.javax.sip.stack.NioMessageProcessorFactory"); + + /** + * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE + */ + Logger logger = LoggerFactory.getLogger(SipConfig.class); + if (sipLog) { + properties.setProperty("gov.nist.javax.sip.STACK_LOGGER", "cn.skcks.docking.gb28181.core.sip.logger.StackLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.SERVER_LOGGER", "cn.skcks.docking.gb28181.core.sip.logger.ServerLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true"); + logger.info("[SIP日志]已开启"); + }else { + logger.info("[SIP日志]已关闭"); + } + return properties; + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/starter/SipStarter.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/starter/SipStarter.java new file mode 100644 index 0000000..a842599 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/starter/SipStarter.java @@ -0,0 +1,53 @@ +package cn.skcks.docking.gb28181.starter; + +import cn.skcks.docking.gb28181.common.json.JsonUtils; +import cn.skcks.docking.gb28181.config.sip.SipConfig; +import cn.skcks.docking.gb28181.core.sip.SipService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.SmartLifecycle; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Order(0) +@Slf4j +@RequiredArgsConstructor +@Component +public class SipStarter implements SmartLifecycle { + private final SipConfig sipConfig; + private final SipService sipService; + + private boolean isRunning; + + @Override + public void start() { + if(checkConfig()){ + isRunning = true; + log.debug("sip 服务 启动"); + sipService.run(); + } + } + + @Override + public void stop() { + log.debug("sip 服务 关闭"); + sipService.stop(); + isRunning = false; + } + + @Override + public boolean isRunning() { + return isRunning; + } + + public boolean checkConfig(){ + log.debug("sip 配置信息 => \n{}", JsonUtils.toJson(sipConfig)); + if(CollectionUtils.isEmpty(sipConfig.getIp())){ + log.error("sip ip 配置错误, 请检查配置是否正确"); + return false; + } + + return true; + } +} diff --git a/starter/pom.xml b/starter/pom.xml index 023ca0b..52fd0b5 100644 --- a/starter/pom.xml +++ b/starter/pom.xml @@ -21,6 +21,12 @@ + + cn.skcks.docking.gb28181 + gb28181-service + ${project.version} + + cn.skcks.docking.gb28181 common diff --git a/starter/src/test/resources/application.yml b/starter/src/test/resources/application.yml new file mode 100644 index 0000000..d3f1ec9 --- /dev/null +++ b/starter/src/test/resources/application.yml @@ -0,0 +1,25 @@ +server: + port: 28181 + +gb28181: + # 作为28181服务器的配置 + sip: + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 + ip: + - 10.10.10.20 + - 10.27.0.6 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4405010000 + # [可选] + id: 44050100002000000002 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: 123456 + # 是否存储alarm信息 + alarm: true