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