sdp 完全重构
This commit is contained in:
parent
0ec87388b0
commit
51c4a73a76
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
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.message.processor.MessageProcessor;
|
||||
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.subscribe.GenericSubscribe;
|
||||
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.message.SIPResponse;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@ -16,7 +17,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sdp.SdpParseException;
|
||||
import javax.sdp.SessionDescription;
|
||||
import javax.sip.InvalidArgumentException;
|
||||
import javax.sip.ResponseEvent;
|
||||
import javax.sip.SipException;
|
||||
@ -65,8 +65,8 @@ public class InviteResponseProcessor implements MessageProcessor {
|
||||
|
||||
ResponseEventExt event = (ResponseEventExt) requestEvent;
|
||||
String contentString = new String(response.getRawContent());
|
||||
Gb28181Sdp gb28181Sdp = SipUtil.parseSDP(contentString);
|
||||
SessionDescription sdp = gb28181Sdp.getBaseSdb();
|
||||
GB28181DescriptionParser gb28181DescriptionParser = new GB28181DescriptionParser(contentString);
|
||||
GB28181Description sdp = gb28181DescriptionParser.parse();
|
||||
SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
|
||||
Request reqAck = SipRequestBuilder.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
|
||||
|
||||
|
@ -5,7 +5,6 @@ import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.skcks.docking.gb28181.common.config.ProjectConfig;
|
||||
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.SipUri;
|
||||
import gov.nist.javax.sip.header.Subject;
|
||||
@ -19,9 +18,6 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sdp.SdpFactory;
|
||||
import javax.sdp.SdpParseException;
|
||||
import javax.sdp.SessionDescription;
|
||||
import javax.sip.PeerUnavailableException;
|
||||
import javax.sip.SipFactory;
|
||||
import javax.sip.header.FromHeader;
|
||||
@ -166,37 +162,6 @@ public class SipUtil implements ApplicationContextAware {
|
||||
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) {
|
||||
|
||||
// jainSip不支持y= f=字段, 移除以解析。
|
||||
|
@ -1,23 +1,24 @@
|
||||
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 cn.skcks.docking.gb28181.sdp.GB28181Description;
|
||||
import cn.skcks.docking.gb28181.sdp.GB28181SDPBuilder;
|
||||
import cn.skcks.docking.gb28181.sdp.field.ssrc.SsrcField;
|
||||
import cn.skcks.docking.gb28181.sdp.media.MediaStreamMode;
|
||||
import gov.nist.core.Separators;
|
||||
import gov.nist.javax.sdp.MediaDescriptionImpl;
|
||||
import gov.nist.javax.sdp.SessionDescriptionImpl;
|
||||
import gov.nist.javax.sdp.fields.*;
|
||||
import gov.nist.javax.sdp.fields.AttributeField;
|
||||
import gov.nist.javax.sdp.fields.ConnectionField;
|
||||
import gov.nist.javax.sdp.fields.URIField;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@ -124,7 +125,8 @@ public class SipEventTest {
|
||||
@Test
|
||||
@SneakyThrows
|
||||
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);
|
||||
description.setVersion(version);
|
||||
@ -132,10 +134,10 @@ public class SipEventTest {
|
||||
Connection connectionField = SdpFactory.getInstance().createConnection(ConnectionField.IN, Connection.IP4, "10.10.10.20");
|
||||
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));
|
||||
MediaSdpHelper.RTPMAP.forEach((k, v)->{
|
||||
Optional.ofNullable(MediaSdpHelper.FMTP.get(k)).ifPresent((f)->{
|
||||
GB28181SDPBuilder.RTPMAP.forEach((k, v)->{
|
||||
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.RTPMAP, StringUtils.joinWith(Separators.SP,k,v)));
|
||||
@ -181,7 +183,7 @@ public class SipEventTest {
|
||||
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, String.valueOf(ssrc), StreamMode.UDP, SdpFactory.getInstance().createTimeDescription());
|
||||
log.info("\n{}", description);
|
||||
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{}", gb28181Description);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class FormatFieldParser extends SDPParser {
|
||||
log.info("{}", (Object) split);
|
||||
String video = split[0];
|
||||
String[] videoParams = StringUtils.split(video,"/");
|
||||
log.info("{}", (Object) videoParams);
|
||||
log.info("videoParams {}", (Object) videoParams);
|
||||
if(videoParams.length > 1){
|
||||
formatField.setVideoFormat(videoParams[1]);
|
||||
}
|
||||
@ -55,6 +55,7 @@ public class FormatFieldParser extends SDPParser {
|
||||
}
|
||||
String audio = split[1];
|
||||
String[] audioParams = audio.split("/");
|
||||
log.info("audioParams {}", (Object) audioParams);
|
||||
if(audioParams.length > 0){
|
||||
formatField.setAudioFormat(audioParams[0]);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public class NotifyRequestBuilder extends RequestBuilder implements NotifyBuilde
|
||||
int expires = sipRequest.getExpires().getExpires();
|
||||
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) {
|
||||
SIPRequest notifyRequest = (SIPRequest) createNotifyRequest(callId, cSeq, event, content, toTag);
|
||||
SubscriptionState subscriptionState = (SubscriptionState) notifyRequest.getHeader(SubscriptionState.NAME);
|
||||
|
Loading…
Reference in New Issue
Block a user