From 7b008248d42ff5be85d0d52a8da543597c7eaeea Mon Sep 17 00:00:00 2001 From: shikong <919411476@qq.com> Date: Thu, 24 Aug 2023 01:27:40 +0800 Subject: [PATCH] =?UTF-8?q?GB28181Description=20sdp=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sip/gb28181/sdp/GB28181Description.java | 64 +++++++++++ .../core/sip/gb28181/sdp/MediaSdpHelper.java | 103 +++++++++++++++++- .../core/sip/gb28181/sdp/SsrcField.java | 28 +++++ .../core/sip/gb28181/sdp/StreamMode.java | 27 +++++ .../core/sip/message/event/SipEventTest.java | 39 ++++++- 5 files changed, 250 insertions(+), 11 deletions(-) create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/GB28181Description.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/SsrcField.java create mode 100644 gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/StreamMode.java diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/GB28181Description.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/GB28181Description.java new file mode 100644 index 0000000..bb61e52 --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/GB28181Description.java @@ -0,0 +1,64 @@ +package cn.skcks.docking.gb28181.core.sip.gb28181.sdp; + +import gov.nist.javax.sdp.SessionDescriptionImpl; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.SneakyThrows; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import javax.sdp.Connection; +import javax.sdp.Origin; +import javax.sdp.SessionDescription; +import javax.sdp.URI; +import java.util.Optional; + +@EqualsAndHashCode(callSuper = false) +@Data +public class GB28181Description extends SessionDescriptionImpl implements SessionDescription { + + + public static class Convertor { + @SneakyThrows + public static GB28181Description convert(SessionDescriptionImpl sessionDescription){ + GB28181Description gb28181Description = new GB28181Description(); + gb28181Description.setMediaDescriptions(sessionDescription.getMediaDescriptions(true)); + gb28181Description.setBandwidths(sessionDescription.getBandwidths(true)); + + Connection connection = sessionDescription.getConnection(); + if (connection != null){ + gb28181Description.setConnection(connection); + } + + gb28181Description.setEmails(sessionDescription.getEmails(true)); + + gb28181Description.setTimeDescriptions(sessionDescription.getTimeDescriptions(true)); + + Origin origin = sessionDescription.getOrigin(); + if(origin != null){ + gb28181Description.setOrigin(origin); + } + + gb28181Description.setAttributes(sessionDescription.getAttributes(true)); + + URI uri = sessionDescription.getURI(); + if(uri != null){ + gb28181Description.setURI(uri); + } + return gb28181Description; + } + } + + private SsrcField ssrcField; + + GB28181Description(){ + super(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(super.toString()); + sb.append(getSsrcField() == null ? "" : getSsrcField().toString()); + return sb.toString(); + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/MediaSdpHelper.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/MediaSdpHelper.java index eb19f53..2bef0ea 100644 --- a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/MediaSdpHelper.java +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/MediaSdpHelper.java @@ -1,8 +1,20 @@ package cn.skcks.docking.gb28181.core.sip.gb28181.sdp; -import javax.sdp.SessionDescription; -import java.util.HashMap; -import java.util.Map; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import gov.nist.core.Separators; +import gov.nist.javax.sdp.SessionDescriptionImpl; +import gov.nist.javax.sdp.fields.AttributeField; +import gov.nist.javax.sdp.fields.ConnectionField; +import gov.nist.javax.sdp.fields.TimeField; +import gov.nist.javax.sdp.fields.URIField; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; + +import javax.sdp.*; +import java.util.*; public class MediaSdpHelper { public static final Map RTPMAP = new HashMap<>() {{ @@ -18,7 +30,88 @@ public class MediaSdpHelper { put("125", "profile-level-id=42e01e"); }}; - public SessionDescription build(String deviceId, String channelId, String rtpIp, int rtpPort, String streamMode){ - return null; + @AllArgsConstructor + @Getter + public enum Action { + PLAY("Play"), + PLAY_BACK("Playback"), + DOWNLOAD("Download"); + + @JsonValue + private final String action; + + @JsonCreator + public static Action fromCode(String action) { + for (Action a : values()) { + if (a.getAction().equalsIgnoreCase(action)) { + return a; + } + } + return null; + } + } + + @SneakyThrows + public static GB28181Description build(Action action, String deviceId, String channelId, String netType, String rtpIp, int rtpPort, long ssrc, StreamMode streamMode, TimeDescription timeDescription){ + GB28181Description description = GB28181Description.Convertor.convert((SessionDescriptionImpl) SdpFactory.getInstance().createSessionDescription(action.getAction())); + Version version = SdpFactory.getInstance().createVersion(0); + description.setVersion(version); + + Connection connectionField = SdpFactory.getInstance().createConnection(ConnectionField.IN, netType, rtpIp); + description.setConnection(connectionField); + + MediaDescription mediaDescription = SdpFactory.getInstance().createMediaDescription("video", rtpPort, 0, SdpConstants.RTP_AVP, MediaSdpHelper.RTPMAP.keySet().toArray(new String[0])); + mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("recvonly",null)); + MediaSdpHelper.RTPMAP.forEach((k, v)->{ + Optional.ofNullable(MediaSdpHelper.FMTP.get(k)).ifPresent((f)->{ + mediaDescription.addAttribute((AttributeField) SdpFactory.getInstance().createAttribute(SdpConstants.FMTP.toLowerCase(), StringUtils.joinWith(Separators.SP,k,f))); + }); + mediaDescription.addAttribute((AttributeField) SdpFactory.getInstance().createAttribute(SdpConstants.RTPMAP, StringUtils.joinWith(Separators.SP,k,v))); + }); + + if(streamMode == StreamMode.TCP_PASSIVE){ + // TCP-PASSIVE + mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("setup","passive")); + mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("connection","new")); + } else if(streamMode == StreamMode.TCP_ACTIVE){ + // TCP-ACTIVE + mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("setup","active")); + mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("connection","new")); + } + + description.setMediaDescriptions(new Vector<>() {{ + add(mediaDescription); + }}); + + description.setTimeDescriptions(new Vector<>(){{ + add(timeDescription); + }}); + + Origin origin = SdpFactory.getInstance().createOrigin(channelId, 0, 0, ConnectionField.IN, netType, rtpIp); + description.setOrigin(origin); + + description.setSsrcField(new SsrcField(ssrc)); + return description; + } + + @SneakyThrows + public static GB28181Description play(Action action, String deviceId, String channelId, String netType, String rtpIp, int rtpPort, long ssrc, StreamMode streamMode){ + TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(); + return build(action, deviceId, channelId, netType, rtpIp, rtpPort, ssrc, streamMode, timeDescription); + } + + @SneakyThrows + public static GB28181Description playback(Action action, String deviceId, String channelId, String netType, String rtpIp, int rtpPort, long ssrc, StreamMode streamMode, Date start, Date end) { + TimeField timeField = new TimeField(); + timeField.setStart(start); + timeField.setStop(end); + TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(timeField); + + GB28181Description description = build(action, deviceId, channelId, netType, rtpIp, rtpPort, ssrc, streamMode, timeDescription); + + URIField uriField = new URIField(); + uriField.setURI(StringUtils.joinWith(":", channelId, "0")); + description.setURI(uriField); + return description; } } diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/SsrcField.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/SsrcField.java new file mode 100644 index 0000000..f421f0e --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/SsrcField.java @@ -0,0 +1,28 @@ +package cn.skcks.docking.gb28181.core.sip.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 long ssrc; + + @Override + public String encode() { + return SSRC_FIELD + ssrc + Separators.NEWLINE; + } + + public String toString(){ + return encode(); + } +} diff --git a/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/StreamMode.java b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/StreamMode.java new file mode 100644 index 0000000..282bc8b --- /dev/null +++ b/gb28181-service/src/main/java/cn/skcks/docking/gb28181/core/sip/gb28181/sdp/StreamMode.java @@ -0,0 +1,27 @@ +package cn.skcks.docking.gb28181.core.sip.gb28181.sdp; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum StreamMode { + UDP("UDP"), + TCP_ACTIVE("TCP-ACTIVE"), + TCP_PASSIVE("TCP-PASSIVE"); + + @JsonValue + private final String mode; + + @JsonCreator + public static StreamMode fromCode(String mode) { + for (StreamMode m : values()) { + if (m.getMode().equalsIgnoreCase(mode)) { + return m; + } + } + return null; + } +} diff --git a/gb28181-service/src/test/java/cn/skcks/docking/gb28181/core/sip/message/event/SipEventTest.java b/gb28181-service/src/test/java/cn/skcks/docking/gb28181/core/sip/message/event/SipEventTest.java index e641cfc..de107ab 100644 --- a/gb28181-service/src/test/java/cn/skcks/docking/gb28181/core/sip/message/event/SipEventTest.java +++ b/gb28181-service/src/test/java/cn/skcks/docking/gb28181/core/sip/message/event/SipEventTest.java @@ -1,6 +1,10 @@ package cn.skcks.docking.gb28181.core.sip.message.event; +import cn.hutool.core.util.RandomUtil; +import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.GB28181Description; import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.MediaSdpHelper; +import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.SsrcField; +import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.StreamMode; import gov.nist.core.NameValue; import gov.nist.core.Separators; import gov.nist.javax.sdp.MediaDescriptionImpl; @@ -12,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import javax.sdp.*; +import java.net.URL; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; @@ -119,12 +124,13 @@ public class SipEventTest { @Test @SneakyThrows public void sdpTest() { - SessionDescription sessionDescription = SdpFactory.getInstance().createSessionDescription("Play"); + GB28181Description description = GB28181Description.Convertor.convert((SessionDescriptionImpl) SdpFactory.getInstance().createSessionDescription("Play")); + Version version = SdpFactory.getInstance().createVersion(0); - sessionDescription.setVersion(version); + description.setVersion(version); Connection connectionField = SdpFactory.getInstance().createConnection(ConnectionField.IN, Connection.IP4, "10.10.10.20"); - sessionDescription.setConnection(connectionField); + description.setConnection(connectionField); MediaDescription mediaDescription = SdpFactory.getInstance().createMediaDescription("video", 6666, 0, SdpConstants.RTP_AVP, MediaSdpHelper.RTPMAP.keySet().toArray(new String[0])); mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("recvonly",null)); @@ -143,18 +149,39 @@ public class SipEventTest { mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("setup","active")); mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("connection","new")); - sessionDescription.setMediaDescriptions(new Vector<>() {{ + description.setMediaDescriptions(new Vector<>() {{ add(mediaDescription); }}); TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(); - sessionDescription.setTimeDescriptions(new Vector<>(){{add(timeDescription);}}); + description.setTimeDescriptions(new Vector<>(){{add(timeDescription);}}); // channelId Origin origin = SdpFactory.getInstance().createOrigin("44050100001310000006", 0, 0, ConnectionField.IN, Connection.IP4, "10.10.10.20"); - sessionDescription.setOrigin(origin); + description.setOrigin(origin); // mediaDescription.setPreconditionFields(); + URIField uriField = new URIField(); + uriField.setURI("44050100001310000006:0"); + description.setURI(uriField); + + + // GB28181Description description = (GB28181Description) description; + description.setSsrcField(new SsrcField(12345678)); + SessionDescription sessionDescription = description; + sessionDescription.setSessionName(SdpFactory.getInstance().createSessionName("PlayBack")); log.info("\n{}", sessionDescription); } + + @Test + @SneakyThrows + void mediaSdpHelperTest(){ + String deviceId = "44050100001110000006"; + String channel = "44050100001310000006"; + int rtpPort = 5080; + String rtpIp = "10.10.10.20"; + long ssrc = RandomUtil.randomLong(10000000,100000000); + GB28181Description description = MediaSdpHelper.build(MediaSdpHelper.Action.PLAY, deviceId, channel, Connection.IP4, rtpIp, rtpPort, ssrc, StreamMode.UDP, SdpFactory.getInstance().createTimeDescription()); + log.info("\n{}", description); + } }