sdp 完全重构

This commit is contained in:
shikong 2024-01-06 16:04:22 +08:00
parent 0ec87388b0
commit 51c4a73a76
10 changed files with 26 additions and 321 deletions

View File

@ -1,65 +0,0 @@
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.*;
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();
SessionName sessionName = sessionDescription.getSessionName();
if(sessionName != null){
gb28181Description.setSessionName(sessionName);
}
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();
}
}

View File

@ -1,16 +0,0 @@
package cn.skcks.docking.gb28181.core.sip.gb28181.sdp;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.sdp.SessionDescription;
@Builder
@Data
public class Gb28181Sdp {
private SessionDescription baseSdb;
private String ssrc;
private String mediaDescription;
}

View File

@ -1,127 +0,0 @@
package cn.skcks.docking.gb28181.core.sip.gb28181.sdp;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import gov.nist.core.Separators;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import javax.sdp.*;
import java.util.*;
@Slf4j
public class MediaSdpHelper {
public final static String SEPARATOR = "_";
public static String getStreamId(String prefix,String... ids){
return StringUtils.joinWith(SEPARATOR, (Object[]) ArrayUtils.addFirst(ids,prefix));
}
public static final Map<String, String> RTPMAP = new HashMap<>() {{
put("96", "PS/90000");
put("126", "H264/90000");
put("125", "H264S/90000");
put("99", "H265/90000");
put("98", "H264/90000");
put("97", "MPEG4/90000");
}};
public static final Map<String, String> FMTP = new HashMap<>() {{
put("126", "profile-level-id=42e01e");
put("125", "profile-level-id=42e01e");
}};
@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, String ssrc, StreamMode streamMode, TimeDescription timeDescription){
log.debug("{} {} {} {} {} {} {} {} {}",action, deviceId, channelId, netType, rtpIp, rtpPort, ssrc, streamMode, timeDescription);
GB28181Description description = new GB28181Description();
description.setSessionName(SdpFactory.getInstance().createSessionName(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(String deviceId, String channelId, String netType, String rtpIp, int rtpPort, String ssrc, StreamMode streamMode){
TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription();
return build(Action.PLAY, deviceId, channelId, netType, rtpIp, rtpPort, ssrc, streamMode, timeDescription);
}
@SneakyThrows
public static GB28181Description playback(String deviceId, String channelId, String netType, String rtpIp, int rtpPort, String ssrc, StreamMode streamMode, Date start, Date end) {
TimeField timeField = new TimeField();
timeField.setStartTime(start.toInstant().getEpochSecond());
timeField.setStopTime(end.toInstant().getEpochSecond());
TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(timeField);
GB28181Description description = build(Action.PLAY_BACK, deviceId, channelId, netType, rtpIp, rtpPort, ssrc, streamMode, timeDescription);
URIField uriField = new URIField();
uriField.setURI(StringUtils.joinWith(":", channelId, "0"));
description.setURI(uriField);
return description;
}
}

View File

@ -1,28 +0,0 @@
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 String ssrc;
@Override
public String encode() {
return SSRC_FIELD + ssrc + Separators.NEWLINE;
}
public String toString(){
return encode();
}
}

View File

@ -1,27 +0,0 @@
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 of(String mode) {
for (StreamMode m : values()) {
if (m.getMode().equalsIgnoreCase(mode)) {
return m;
}
}
return null;
}
}

View File

@ -1,13 +1,14 @@
package cn.skcks.docking.gb28181.core.sip.message.processor.message.response; package cn.skcks.docking.gb28181.core.sip.message.processor.message.response;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.Gb28181Sdp;
import cn.skcks.docking.gb28181.core.sip.listener.SipListener; import cn.skcks.docking.gb28181.core.sip.listener.SipListener;
import cn.skcks.docking.gb28181.core.sip.message.processor.MessageProcessor; import cn.skcks.docking.gb28181.core.sip.message.processor.MessageProcessor;
import cn.skcks.docking.gb28181.core.sip.message.request.SipRequestBuilder; import cn.skcks.docking.gb28181.core.sip.message.request.SipRequestBuilder;
import cn.skcks.docking.gb28181.core.sip.message.sender.SipMessageSender; import cn.skcks.docking.gb28181.core.sip.message.sender.SipMessageSender;
import cn.skcks.docking.gb28181.core.sip.message.subscribe.GenericSubscribe; import cn.skcks.docking.gb28181.core.sip.message.subscribe.GenericSubscribe;
import cn.skcks.docking.gb28181.core.sip.message.subscribe.SipSubscribe; import cn.skcks.docking.gb28181.core.sip.message.subscribe.SipSubscribe;
import cn.skcks.docking.gb28181.core.sip.utils.SipUtil; import cn.skcks.docking.gb28181.sdp.GB28181Description;
import cn.skcks.docking.gb28181.sdp.parser.GB28181DescriptionParser;
import gov.nist.javax.sip.ResponseEventExt; import gov.nist.javax.sip.ResponseEventExt;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
@ -16,7 +17,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.sdp.SdpParseException; import javax.sdp.SdpParseException;
import javax.sdp.SessionDescription;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent; import javax.sip.ResponseEvent;
import javax.sip.SipException; import javax.sip.SipException;
@ -65,8 +65,8 @@ public class InviteResponseProcessor implements MessageProcessor {
ResponseEventExt event = (ResponseEventExt) requestEvent; ResponseEventExt event = (ResponseEventExt) requestEvent;
String contentString = new String(response.getRawContent()); String contentString = new String(response.getRawContent());
Gb28181Sdp gb28181Sdp = SipUtil.parseSDP(contentString); GB28181DescriptionParser gb28181DescriptionParser = new GB28181DescriptionParser(contentString);
SessionDescription sdp = gb28181Sdp.getBaseSdb(); GB28181Description sdp = gb28181DescriptionParser.parse();
SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort()); SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
Request reqAck = SipRequestBuilder.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); Request reqAck = SipRequestBuilder.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);

View File

@ -5,7 +5,6 @@ import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.skcks.docking.gb28181.common.config.ProjectConfig; import cn.skcks.docking.gb28181.common.config.ProjectConfig;
import cn.skcks.docking.gb28181.core.sip.dto.RemoteInfo; import cn.skcks.docking.gb28181.core.sip.dto.RemoteInfo;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.Gb28181Sdp;
import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri; import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.Subject; import gov.nist.javax.sip.header.Subject;
@ -19,9 +18,6 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import javax.sdp.SdpFactory;
import javax.sdp.SdpParseException;
import javax.sdp.SessionDescription;
import javax.sip.PeerUnavailableException; import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory; import javax.sip.SipFactory;
import javax.sip.header.FromHeader; import javax.sip.header.FromHeader;
@ -166,37 +162,6 @@ public class SipUtil implements ApplicationContextAware {
return builder.toString(); return builder.toString();
} }
public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException {
// jainSip不支持y= f=字段 移除以解析
int ssrcIndex = sdpStr.indexOf("y=");
int mediaDescriptionIndex = sdpStr.indexOf("f=");
// 检查是否有y字段
SessionDescription sdp;
String ssrc = null;
String mediaDescription = null;
if (mediaDescriptionIndex == 0 && ssrcIndex == 0) {
sdp = SdpFactory.getInstance().createSessionDescription(sdpStr);
}else {
String[] lines = sdpStr.split("\\r?\\n");
StringBuilder sdpBuffer = new StringBuilder();
for (String line : lines) {
if (line.trim().startsWith("y=")) {
ssrc = line.substring(2);
}else if (line.trim().startsWith("f=")) {
mediaDescription = line.substring(2);
}else {
sdpBuffer.append(line.trim()).append("\r\n");
}
}
sdp = SdpFactory.getInstance().createSessionDescription(sdpBuffer.toString());
}
return Gb28181Sdp.builder()
.baseSdb(sdp)
.ssrc(ssrc)
.mediaDescription(mediaDescription)
.build();
}
public static String getSsrcFromSdp(String sdpStr) { public static String getSsrcFromSdp(String sdpStr) {
// jainSip不支持y= f=字段 移除以解析 // jainSip不支持y= f=字段 移除以解析

View File

@ -1,23 +1,24 @@
package cn.skcks.docking.gb28181.core.sip.message.event; package cn.skcks.docking.gb28181.core.sip.message.event;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.GB28181Description; import cn.skcks.docking.gb28181.sdp.GB28181Description;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.MediaSdpHelper; import cn.skcks.docking.gb28181.sdp.GB28181SDPBuilder;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.SsrcField; import cn.skcks.docking.gb28181.sdp.field.ssrc.SsrcField;
import cn.skcks.docking.gb28181.core.sip.gb28181.sdp.StreamMode; import cn.skcks.docking.gb28181.sdp.media.MediaStreamMode;
import gov.nist.core.NameValue;
import gov.nist.core.Separators; import gov.nist.core.Separators;
import gov.nist.javax.sdp.MediaDescriptionImpl; import gov.nist.javax.sdp.fields.AttributeField;
import gov.nist.javax.sdp.SessionDescriptionImpl; import gov.nist.javax.sdp.fields.ConnectionField;
import gov.nist.javax.sdp.fields.*; import gov.nist.javax.sdp.fields.URIField;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import javax.sdp.*; import javax.sdp.*;
import java.net.URL; import java.util.ArrayList;
import java.util.*; import java.util.List;
import java.util.Optional;
import java.util.Vector;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -124,7 +125,8 @@ public class SipEventTest {
@Test @Test
@SneakyThrows @SneakyThrows
public void sdpTest() { public void sdpTest() {
GB28181Description description = GB28181Description.Convertor.convert((SessionDescriptionImpl) SdpFactory.getInstance().createSessionDescription("Play"));
GB28181Description description = new GB28181Description(SdpFactory.getInstance().createSessionDescription("Play"));
Version version = SdpFactory.getInstance().createVersion(0); Version version = SdpFactory.getInstance().createVersion(0);
description.setVersion(version); description.setVersion(version);
@ -132,10 +134,10 @@ public class SipEventTest {
Connection connectionField = SdpFactory.getInstance().createConnection(ConnectionField.IN, Connection.IP4, "10.10.10.20"); Connection connectionField = SdpFactory.getInstance().createConnection(ConnectionField.IN, Connection.IP4, "10.10.10.20");
description.setConnection(connectionField); description.setConnection(connectionField);
MediaDescription mediaDescription = SdpFactory.getInstance().createMediaDescription("video", 6666, 0, SdpConstants.RTP_AVP, MediaSdpHelper.RTPMAP.keySet().toArray(new String[0])); MediaDescription mediaDescription = SdpFactory.getInstance().createMediaDescription("video", 6666, 0, SdpConstants.RTP_AVP, GB28181SDPBuilder.RTPMAP.keySet().toArray(new String[0]));
mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("recvonly",null)); mediaDescription.addAttribute((AttributeField)SdpFactory.getInstance().createAttribute("recvonly",null));
MediaSdpHelper.RTPMAP.forEach((k, v)->{ GB28181SDPBuilder.RTPMAP.forEach((k, v)->{
Optional.ofNullable(MediaSdpHelper.FMTP.get(k)).ifPresent((f)->{ Optional.ofNullable(GB28181SDPBuilder.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.FMTP.toLowerCase(), StringUtils.joinWith(Separators.SP,k,f)));
}); });
mediaDescription.addAttribute((AttributeField) SdpFactory.getInstance().createAttribute(SdpConstants.RTPMAP, StringUtils.joinWith(Separators.SP,k,v))); mediaDescription.addAttribute((AttributeField) SdpFactory.getInstance().createAttribute(SdpConstants.RTPMAP, StringUtils.joinWith(Separators.SP,k,v)));
@ -181,7 +183,7 @@ public class SipEventTest {
int rtpPort = 5080; int rtpPort = 5080;
String rtpIp = "10.10.10.20"; String rtpIp = "10.10.10.20";
long ssrc = RandomUtil.randomLong(10000000,100000000); long ssrc = RandomUtil.randomLong(10000000,100000000);
GB28181Description description = MediaSdpHelper.build(MediaSdpHelper.Action.PLAY, deviceId, channel, Connection.IP4, rtpIp, rtpPort, String.valueOf(ssrc), StreamMode.UDP, SdpFactory.getInstance().createTimeDescription()); GB28181Description gb28181Description = GB28181SDPBuilder.Sender.build(GB28181SDPBuilder.Action.PLAY, deviceId, channel, Connection.IP4, rtpIp, rtpPort, String.valueOf(ssrc), MediaStreamMode.UDP, SdpFactory.getInstance().createTimeDescription());
log.info("\n{}", description); log.info("\n{}", gb28181Description);
} }
} }

View File

@ -34,7 +34,7 @@ public class FormatFieldParser extends SDPParser {
log.info("{}", (Object) split); log.info("{}", (Object) split);
String video = split[0]; String video = split[0];
String[] videoParams = StringUtils.split(video,"/"); String[] videoParams = StringUtils.split(video,"/");
log.info("{}", (Object) videoParams); log.info("videoParams {}", (Object) videoParams);
if(videoParams.length > 1){ if(videoParams.length > 1){
formatField.setVideoFormat(videoParams[1]); formatField.setVideoFormat(videoParams[1]);
} }
@ -55,6 +55,7 @@ public class FormatFieldParser extends SDPParser {
} }
String audio = split[1]; String audio = split[1];
String[] audioParams = audio.split("/"); String[] audioParams = audio.split("/");
log.info("audioParams {}", (Object) audioParams);
if(audioParams.length > 0){ if(audioParams.length > 0){
formatField.setAudioFormat(audioParams[0]); formatField.setAudioFormat(audioParams[0]);
} }

View File

@ -27,7 +27,7 @@ public class NotifyRequestBuilder extends RequestBuilder implements NotifyBuilde
int expires = sipRequest.getExpires().getExpires(); int expires = sipRequest.getExpires().getExpires();
return createNotifyRequest(callId, cSeq, event, content, toTag, expires); return createNotifyRequest(callId, cSeq, event, content, toTag, expires);
} }
@SneakyThrows @SneakyThrows
public Request createNotifyRequest(String callId, long cSeq, String event, byte[] content, String toTag, int expire) { public Request createNotifyRequest(String callId, long cSeq, String event, byte[] content, String toTag, int expire) {
SIPRequest notifyRequest = (SIPRequest) createNotifyRequest(callId, cSeq, event, content, toTag); SIPRequest notifyRequest = (SIPRequest) createNotifyRequest(callId, cSeq, event, content, toTag);
SubscriptionState subscriptionState = (SubscriptionState) notifyRequest.getHeader(SubscriptionState.NAME); SubscriptionState subscriptionState = (SubscriptionState) notifyRequest.getHeader(SubscriptionState.NAME);