diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java index be64677..2190cb5 100644 --- a/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java +++ b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java @@ -28,7 +28,7 @@ public class XmlUtils { // 大驼峰 (首字母大写) mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy()); // 添加 xml 头部声明 - mapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true); + mapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, false); } public static String toXml(Object obj) { diff --git a/gb28181-service/pom.xml b/gb28181-service/pom.xml index e748177..3ff1ba9 100644 --- a/gb28181-service/pom.xml +++ b/gb28181-service/pom.xml @@ -46,13 +46,6 @@ 1.3.0-91 - - - org.dom4j - dom4j - 2.1.4 - - org.mapstruct diff --git a/gb28181-sip/.gitignore b/gb28181-sip/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/gb28181-sip/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/gb28181-sip/pom.xml b/gb28181-sip/pom.xml new file mode 100644 index 0000000..aed1a83 --- /dev/null +++ b/gb28181-sip/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + cn.skcks.docking + gb28181 + 0.1.0-SNAPSHOT + + + cn.skcks.docking.gb28181 + gb28181-sip + + + 17 + 17 + UTF-8 + + + + + cn.skcks.docking.gb28181 + common + + + + + javax.sip + jain-sip-ri + + + + org.projectlombok + lombok + + + + org.junit.jupiter + junit-jupiter + test + + + diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java new file mode 100644 index 0000000..350425e --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java @@ -0,0 +1,39 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.javax.sdp.SessionDescriptionImpl; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import javax.sdp.SdpException; +import javax.sdp.SessionDescription; + +@Slf4j +@Setter +@Getter +public class GB28181Description extends SessionDescriptionImpl implements SessionDescription { + private SsrcField ssrcField; + private SessionDescriptionImpl sessionDescription; + + public GB28181Description(){ + super(); + } + + public GB28181Description(SessionDescription sessionDescription) throws SdpException { + super(sessionDescription); + } + + @SneakyThrows + public static GB28181Description build(SessionDescription sessionDescription){ + return new GB28181Description(sessionDescription); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(super.toString()); + sb.append(getSsrcField() == null ? "" : getSsrcField().toString()); + // return "+"; + return sb.toString(); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java new file mode 100644 index 0000000..d384e2e --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java @@ -0,0 +1,88 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.core.ParserCore; +import gov.nist.javax.sdp.SessionDescriptionImpl; +import gov.nist.javax.sdp.fields.SDPField; +import gov.nist.javax.sdp.parser.Lexer; +import gov.nist.javax.sdp.parser.SDPParser; + +import java.text.ParseException; +import java.util.Vector; + +@SuppressWarnings("all") +public class GB28181DescriptionParser extends ParserCore { + protected Lexer lexer; + protected Vector sdpMessage; + + public GB28181DescriptionParser(Vector sdpMessage) { + this.sdpMessage = sdpMessage; + } + + public GB28181DescriptionParser(String message) { + int start = 0; + String line = null; + // Return trivially if there is no sdp announce message + // to be parsed. Bruno Konik noticed this bug. + if (message == null) return; + sdpMessage = new Vector(); + // Strip off leading and trailing junk. + String sdpAnnounce = message.trim() + "\r\n"; + // Bug fix by Andreas Bystrom. + while (start < sdpAnnounce.length()) { + // Major re-write by Ricardo Borba. + int lfPos = sdpAnnounce.indexOf("\n", start); + int crPos = sdpAnnounce.indexOf("\r", start); + + if (lfPos >= 0 && crPos < 0) { + // there are only "\n" separators + line = sdpAnnounce.substring(start, lfPos); + start = lfPos + 1; + } else if (lfPos < 0 && crPos >= 0) { + //bug fix: there are only "\r" separators + line = sdpAnnounce.substring(start, crPos); + start = crPos + 1; + } else if (lfPos >= 0 && crPos >= 0) { + // there are "\r\n" or "\n\r" (if exists) separators + if (lfPos > crPos) { + // assume "\r\n" for now + line = sdpAnnounce.substring(start, crPos); + // Check if the "\r" and "\n" are close together + if (lfPos == crPos + 1) { + start = lfPos + 1; // "\r\n" + } else { + start = crPos + 1; // "\r" followed by the next record and a "\n" further away + } + } else { + // assume "\n\r" for now + line = sdpAnnounce.substring(start, lfPos); + // Check if the "\n" and "\r" are close together + if (crPos == lfPos + 1) { + start = crPos + 1; // "\n\r" + } else { + start = lfPos + 1; // "\n" followed by the next record and a "\r" further away + } + } + } else if (lfPos < 0 && crPos < 0) { // end + break; + } + sdpMessage.addElement(line); + } + } + + public GB28181Description parse() throws ParseException { + GB28181Description retval = new GB28181Description(); + for (int i = 0; i < sdpMessage.size(); i++) { + String field = (String) sdpMessage.elementAt(i); + SDPParser sdpParser = GB28181DescriptionParserFactory.createParser(field); + SDPField sdpField = null; + if (sdpParser != null) { + sdpField = sdpParser.parse(); + } + retval.addField(sdpField); + if (sdpField instanceof SsrcField ssrc) { + retval.setSsrcField(ssrc); + } + } + return retval; + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java new file mode 100644 index 0000000..70c85c6 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java @@ -0,0 +1,17 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.javax.sdp.parser.Lexer; +import gov.nist.javax.sdp.parser.ParserFactory; +import gov.nist.javax.sdp.parser.SDPParser; + +import java.text.ParseException; + +public class GB28181DescriptionParserFactory { + public static SDPParser createParser(String field) throws ParseException { + String fieldName = Lexer.getFieldName(field); + if(fieldName.equalsIgnoreCase("y")){ + return new SsrcFieldParser(field); + } + return ParserFactory.createParser(field); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java new file mode 100644 index 0000000..48a7d3f --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java @@ -0,0 +1,14 @@ +package cn.skcks.docking.gb28181.sdp; + +import lombok.Builder; +import lombok.Data; + +import javax.sdp.SessionDescription; + +@Builder +@Data +public class Gb28181Sdp { + private SessionDescription baseSdb; + private String ssrc; + private String mediaDescription; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java new file mode 100644 index 0000000..c9045dc --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java @@ -0,0 +1,28 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.core.Separators; +import gov.nist.javax.sdp.fields.SDPField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@AllArgsConstructor +public class SsrcField extends SDPField { + private static final String SSRC_FIELD = "y="; + public SsrcField() { + super(SSRC_FIELD); + } + + private String ssrc; + + @Override + public String encode() { + return SSRC_FIELD + ssrc + Separators.NEWLINE; + } + + public String toString(){ + return encode(); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java new file mode 100644 index 0000000..f6134c9 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java @@ -0,0 +1,33 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.javax.sdp.fields.SDPField; +import gov.nist.javax.sdp.parser.Lexer; +import gov.nist.javax.sdp.parser.SDPParser; + +import java.text.ParseException; + +public class SsrcFieldParser extends SDPParser { + public SsrcFieldParser(String ssrcField) { + this.lexer = new Lexer("charLexer", ssrcField); + } + + public SsrcField ssrcField() throws ParseException { + try { + this.lexer.match('y'); + this.lexer.SPorHT(); + this.lexer.match('='); + this.lexer.SPorHT(); + + SsrcField ssrcField = new SsrcField(); + String rest = lexer.getRest().trim(); + ssrcField.setSsrc(rest); + return ssrcField; + } catch (Exception e) { + throw lexer.createParseException(); + } + } + + public SDPField parse() throws ParseException { + return this.ssrcField(); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java new file mode 100644 index 0000000..eb23b33 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java @@ -0,0 +1,109 @@ +package cn.skcks.docking.gb28181.sip.generic; + +import cn.skcks.docking.gb28181.sip.header.XGBVerHeader; +import cn.skcks.docking.gb28181.sip.header.impl.XGBVerHeaderImpl; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; + +import javax.sip.SipFactory; +import javax.sip.address.Address; +import javax.sip.address.AddressFactory; +import javax.sip.address.SipURI; +import javax.sip.header.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class SipBuilder { + public static SipFactory getSipFactory(){ + return SipFactory.getInstance(); + } + + @SneakyThrows + public static AddressFactory createAddressFactory() { + return getSipFactory().createAddressFactory(); + } + + @SneakyThrows + public static HeaderFactory createHeaderFactory() { + return getSipFactory().createHeaderFactory(); + } + + @SneakyThrows + public static String createHostAddress(String ip, int port) { + return StringUtils.joinWith(":", ip, port); + } + + @SneakyThrows + public static SipURI createSipURI(String id, String address) { + return createAddressFactory().createSipURI(id, address); + } + + @SneakyThrows + public static Address createAddress(SipURI uri) { + return createAddressFactory().createAddress(uri); + } + + @SneakyThrows + public static ToHeader createToHeader(Address toAddress, String toTag) { + return createHeaderFactory().createToHeader(toAddress, toTag); + } + + @SneakyThrows + public static FromHeader createFromHeader(Address fromAddress, String fromTag) { + return createHeaderFactory().createFromHeader(fromAddress, fromTag); + } + + @SneakyThrows + public static CSeqHeader createCSeqHeader(long cSeq, String method){ + return createHeaderFactory().createCSeqHeader(cSeq, method); + } + + + @SneakyThrows + public static MaxForwardsHeader createMaxForwardsHeader(int maxForwards) { + return createHeaderFactory().createMaxForwardsHeader(maxForwards); + } + + @SneakyThrows + public static ViaHeader createViaHeader(String ip, int port, String transport, String viaTag){ + ViaHeader viaHeader = createHeaderFactory().createViaHeader(ip, port, transport, viaTag); + viaHeader.setRPort(); + return viaHeader; + } + + @SneakyThrows + public static List createViaHeaders(String ip, int port, String transport, String viaTag) { + return Collections.singletonList(createViaHeader(ip, port, transport, viaTag)); + } + + + @SneakyThrows + public static ContactHeader createContactHeader(Address address){ + return createHeaderFactory().createContactHeader(address); + }; + + @SneakyThrows + public static ContentTypeHeader createContentTypeHeader(String contentType, String subType){ + return createHeaderFactory().createContentTypeHeader(contentType, subType); + } + + @SneakyThrows + public static ContentTypeHeader createSDPContentTypeHeader(){ + return createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + } + + public static UserAgentHeader createUserAgentHeader(String userAgent){ + return createUserAgentHeader(Arrays.stream(StringUtils.split(userAgent, StringUtils.SPACE)).toList()); + } + + @SneakyThrows + public static UserAgentHeader createUserAgentHeader(List product){ + return createHeaderFactory().createUserAgentHeader(product); + } + + @SneakyThrows + public static XGBVerHeader createUserAgentHeader(int m,int n){ + return new XGBVerHeaderImpl(m,n); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java new file mode 100644 index 0000000..544b070 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java @@ -0,0 +1,11 @@ +package cn.skcks.docking.gb28181.sip.header; + +import javax.sip.header.Header; + +public interface XGBVerHeader extends Header { + public void setVersion(int m, int n); + + public String getVersion(); + + public final static String NAME = "X-GB-Ver"; +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java new file mode 100644 index 0000000..c76e83d --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java @@ -0,0 +1,31 @@ +package cn.skcks.docking.gb28181.sip.header.impl; + +import cn.skcks.docking.gb28181.sip.header.XGBVerHeader; +import gov.nist.javax.sip.header.SIPHeader; + +public class XGBVerHeaderImpl extends SIPHeader implements XGBVerHeader { + private String version; + + @Override + public void setVersion(int m, int n) { + this.version = String.format("%d.%d", m, n); + } + + @Override + public String getVersion() { + return version; + } + + public XGBVerHeaderImpl(int m, int n) { + super(NAME); + setVersion(m, n); + } + + public XGBVerHeaderImpl() { + super(NAME); + } + + protected StringBuilder encodeBody(StringBuilder buffer) { + return buffer.append(version); + } +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java new file mode 100644 index 0000000..2caf882 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java @@ -0,0 +1,7 @@ +package cn.skcks.docking.gb28181.sip.request; + +import javax.sip.SipFactory; + +public class SipRequestBuilder { + +} diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java new file mode 100644 index 0000000..83d8ec1 --- /dev/null +++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java @@ -0,0 +1,7 @@ +package cn.skcks.docking.gb28181.sip.response; + +import javax.sip.SipFactory; + +public class SipResponseBuilder { + +} diff --git a/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java new file mode 100644 index 0000000..9ebd8e1 --- /dev/null +++ b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java @@ -0,0 +1,46 @@ +package cn.skcks.docking.gb28181.sdp; + +import gov.nist.javax.sdp.SessionDescriptionImpl; +import gov.nist.javax.sdp.fields.TimeField; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import javax.sdp.SdpFactory; +import javax.sdp.TimeDescription; +import java.util.Vector; + +@Slf4j +public class SdpTest { + @Test + @SneakyThrows + public void test(){ + String domain = "4405010000"; + TimeField timeField = new TimeField(); + timeField.setZero(); + TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(timeField); + GB28181Description gb28181Description = new GB28181Description(); + gb28181Description.setTimeDescriptions(new Vector<>() {{ + add(timeDescription); + }}); + gb28181Description.setSsrcField(new SsrcField(String.format("%s%04d", domain.substring(3, 8), 1))); + log.info("gb28181 sdp \n{}", gb28181Description); + + GB28181DescriptionParser gb28181DescriptionParser = new GB28181DescriptionParser(gb28181Description.toString()); + GB28181Description parse = gb28181DescriptionParser.parse(); + log.info("gb28181 sdp 解析\n{}",parse); + + SessionDescriptionImpl sessionDescription = gb28181Description; + sessionDescription.setTimeDescriptions(new Vector<>() {{ + add(timeDescription); + }}); + gb28181Description = new GB28181Description(sessionDescription); + gb28181Description.setTimeDescriptions(new Vector<>() {{ + add(timeDescription); + }}); + log.info("从 SessionDescriptionImpl 转换为 gb28181 sdp \n{}", gb28181Description); + gb28181DescriptionParser = new GB28181DescriptionParser(gb28181Description.toString()); + parse = gb28181DescriptionParser.parse(); + log.info("从 SessionDescriptionImpl 转换为 gb28181 sdp 解析\n{}",parse); + } +} diff --git a/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java new file mode 100644 index 0000000..0edc093 --- /dev/null +++ b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java @@ -0,0 +1,23 @@ +package cn.skcks.docking.gb28181.sip; + +import cn.skcks.docking.gb28181.sip.generic.SipBuilder; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import javax.sip.address.Address; +import javax.sip.address.SipURI; + +@Slf4j +public class SipTest { + @Test + public void test(){ + String localIp = "127.0.0.1"; + int localPort = 5060; + String localId = "12345678901234567890"; + String localHostAddress = SipBuilder.createHostAddress(localIp,localPort); + SipURI localSipUri = SipBuilder.createSipURI(localId, localHostAddress); + + Address localAddress = SipBuilder.createAddress(localSipUri); + log.info("{}", localAddress); + } +} diff --git a/pom.xml b/pom.xml index 8d4f551..b34f256 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ gb28181-service orm zlmediakit-service + gb28181-sip @@ -59,6 +60,8 @@ 1.4.13 + + 1.3.0-91 @@ -97,6 +100,12 @@ ${project.version} + + cn.skcks.docking.gb28181 + gb28181-sip + ${project.version} + + cn.skcks.docking.gb28181 annotation @@ -121,6 +130,13 @@ ${project.version} + + + javax.sip + jain-sip-ri + ${jain-sip.version} + + org.mapstruct