优化sdp解析,兼容带有f=的设备

This commit is contained in:
648540858 2023-06-10 00:32:42 +08:00
parent 4815af8ca0
commit 4ed7cb49be
7 changed files with 129 additions and 66 deletions

View File

@ -0,0 +1,46 @@
package com.genersoft.iot.vmp.gb28181.bean;
import javax.sdp.SessionDescription;
/**
* 28181 的SDP解析器
*/
public class Gb28181Sdp {
private SessionDescription baseSdb;
private String ssrc;
private String mediaDescription;
public static Gb28181Sdp getInstance(SessionDescription baseSdb, String ssrc, String mediaDescription) {
Gb28181Sdp gb28181Sdp = new Gb28181Sdp();
gb28181Sdp.setBaseSdb(baseSdb);
gb28181Sdp.setSsrc(ssrc);
gb28181Sdp.setMediaDescription(mediaDescription);
return gb28181Sdp;
}
public SessionDescription getBaseSdb() {
return baseSdb;
}
public void setBaseSdb(SessionDescription baseSdb) {
this.baseSdb = baseSdb;
}
public String getSsrc() {
return ssrc;
}
public void setSsrc(String ssrc) {
this.ssrc = ssrc;
}
public String getMediaDescription() {
return mediaDescription;
}
public void setMediaDescription(String mediaDescription) {
this.mediaDescription = mediaDescription;
}
}

View File

@ -54,8 +54,8 @@ public class SIPRequestHeaderPlarformProvider {
parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
//via //via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(),
parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag()); Integer.parseInt(parentPlatform.getDevicePort()), parentPlatform.getTransport(), SipUtils.getNewViaTag());
viaHeader.setRPort(); viaHeader.setRPort();
viaHeaders.add(viaHeader); viaHeaders.add(viaHeader);
//from //from

View File

@ -574,11 +574,7 @@ public class SIPCommander implements ISIPCommander {
ResponseEvent responseEvent = (ResponseEvent) event.event; ResponseEvent responseEvent = (ResponseEvent) event.event;
SIPResponse response = (SIPResponse) responseEvent.getResponse(); SIPResponse response = (SIPResponse) responseEvent.getResponse();
String contentString =new String(response.getRawContent()); String contentString =new String(response.getRawContent());
int ssrcIndex = contentString.indexOf("y="); String ssrc = SipUtils.getSsrcFromSdp(contentString);
String ssrc=ssrcInfo.getSsrc();
if (ssrcIndex >= 0) {
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
}
streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD);
okEvent.response(event); okEvent.response(event);
}); });

View File

@ -241,18 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 解析sdp消息, 使用jainsip 自带的sdp解析方式 // 解析sdp消息, 使用jainsip 自带的sdp解析方式
String contentString = new String(request.getRawContent()); String contentString = new String(request.getRawContent());
// jainSip不支持y=字段 移除以解析 Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
// 检查是否有y字段 SessionDescription sdp = gb28181Sdp.getBaseSdb();
int ssrcIndex = contentString.indexOf("y=");
SessionDescription sdp;
if (ssrcIndex >= 0) {
//ssrc规定长度为10个字节不取余下长度以避免后续还有f=字段
String substring = contentString.substring(0, ssrcIndex);
sdp = SdpFactory.getInstance().createSessionDescription(substring);
} else {
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
}
String sessionName = sdp.getSessionName().getValue(); String sessionName = sdp.getSessionName().getValue();
Long startTime = null; Long startTime = null;
@ -340,11 +330,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
String ssrc; String ssrc;
if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式 // 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
}else { }else {
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); ssrc = gb28181Sdp.getSsrc();
} }
String streamTypeStr = null; String streamTypeStr = null;
if (mediaTransmissionTCP) { if (mediaTransmissionTCP) {
@ -513,11 +503,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} else if (gbStream != null) { } else if (gbStream != null) {
String ssrc; String ssrc;
if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式 // 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId());
}else { }else {
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); ssrc = gb28181Sdp.getSsrc();
} }
if("push".equals(gbStream.getStreamType())) { if("push".equals(gbStream.getStreamType())) {
@ -891,20 +881,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} }
String contentString = new String(request.getRawContent()); String contentString = new String(request.getRawContent());
// jainSip不支持y=字段 移除移除以解析 // jainSip不支持y=字段 移除移除以解析
String substring = contentString;
String ssrc = "0000000404"; String ssrc = "0000000404";
int ssrcIndex = contentString.indexOf("y=");
if (ssrcIndex > 0) {
substring = contentString.substring(0, ssrcIndex);
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
}
ssrcIndex = substring.indexOf("f=");
if (ssrcIndex > 0) {
substring = contentString.substring(0, ssrcIndex);
}
SessionDescription sdp = null;
try { try {
sdp = SdpFactory.getInstance().createSessionDescription(substring); Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
SessionDescription sdp = gb28181Sdp.getBaseSdb();
// 获取支持的格式 // 获取支持的格式
Vector mediaDescriptions = sdp.getMediaDescriptions(true); Vector mediaDescriptions = sdp.getMediaDescriptions(true);
// 查看是否支持PS 负载96 // 查看是否支持PS 负载96

View File

@ -1,10 +1,12 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
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 org.slf4j.Logger; import org.slf4j.Logger;
@ -12,7 +14,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.sdp.SdpFactory;
import javax.sdp.SdpParseException; import javax.sdp.SdpParseException;
import javax.sdp.SessionDescription; import javax.sdp.SessionDescription;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
@ -79,18 +80,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
ResponseEventExt event = (ResponseEventExt)evt; ResponseEventExt event = (ResponseEventExt)evt;
String contentString = new String(response.getRawContent()); String contentString = new String(response.getRawContent());
// jainSip不支持y=字段 移除以解析 Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
int ssrcIndex = contentString.indexOf("y="); SessionDescription sdp = gb28181Sdp.getBaseSdb();
// 检查是否有y字段
SessionDescription sdp;
if (ssrcIndex >= 0) {
//ssrc规定长度为10字节不取余下长度以避免后续还有f=字段
String substring = contentString.substring(0, contentString.indexOf("y="));
sdp = SdpFactory.getInstance().createSessionDescription(substring);
} else {
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
}
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 = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);

View File

@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.gb28181.utils; package com.genersoft.iot.vmp.gb28181.utils;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
import com.genersoft.iot.vmp.utils.GitUtil; import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.AddressImpl;
@ -10,6 +11,9 @@ import gov.nist.javax.sip.message.SIPRequest;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
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;
@ -190,4 +194,52 @@ public class SipUtils {
} }
return deviceChannel; return deviceChannel;
} }
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 {
int baseSdpIndex = Math.min(mediaDescriptionIndex, ssrcIndex);
//ssrc规定长度为10字节不取余下长度以避免后续还有f=字段
String substring = sdpStr.substring(0, baseSdpIndex);
sdp = SdpFactory.getInstance().createSessionDescription(substring);
String lines[] = sdpStr.split("\\r?\\n");
for (String line : lines) {
if (line.trim().startsWith("y=")) {
ssrc = line.substring(2);
}else if (line.trim().startsWith("f=")) {
mediaDescription = line.substring(2);
}
if (ssrc != null && mediaDescription != null) {
break;
}
}
}
return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription);
}
public static String getSsrcFromSdp(String sdpStr) {
// jainSip不支持y= f=字段 移除以解析
int ssrcIndex = sdpStr.indexOf("y=");
if (ssrcIndex == 0) {
return null;
}
String lines[] = sdpStr.split("\\r?\\n");
for (String line : lines) {
if (line.trim().startsWith("y=")) {
return line.substring(2);
}
}
return null;
}
} }

View File

@ -18,6 +18,7 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
@ -297,17 +298,16 @@ public class PlayServiceImpl implements IPlayService {
ResponseEvent responseEvent = (ResponseEvent) event.event; ResponseEvent responseEvent = (ResponseEvent) event.event;
String contentString = new String(responseEvent.getResponse().getRawContent()); String contentString = new String(responseEvent.getResponse().getRawContent());
// 获取ssrc // 获取ssrc
int ssrcIndex = contentString.indexOf("y="); String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
// 检查是否有y字段 // 检查是否有y字段
if (ssrcIndex >= 0) { if (ssrcInResponse != null) {
//ssrc规定长度为10字节不取余下长度以避免后续还有f=字段 TODO 后续对不规范的非10位ssrc兼容
String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim();
// 查询到ssrc不一致且开启了ssrc校验则需要针对处理 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
String substring = contentString.substring(0, contentString.indexOf("y="));
try { try {
SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
SessionDescription sdp = gb28181Sdp.getBaseSdb();
int port = -1; int port = -1;
Vector mediaDescriptions = sdp.getMediaDescriptions(true); Vector mediaDescriptions = sdp.getMediaDescriptions(true);
for (Object description : mediaDescriptions) { for (Object description : mediaDescriptions) {
@ -607,17 +607,16 @@ public class PlayServiceImpl implements IPlayService {
ResponseEvent responseEvent = (ResponseEvent) eventResult.event; ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
String contentString = new String(responseEvent.getResponse().getRawContent()); String contentString = new String(responseEvent.getResponse().getRawContent());
// 获取ssrc // 获取ssrc
int ssrcIndex = contentString.indexOf("y="); String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
// 检查是否有y字段 // 检查是否有y字段
if (ssrcIndex >= 0) { if (ssrcInResponse != null) {
//ssrc规定长度为10字节不取余下长度以避免后续还有f=字段 TODO 后续对不规范的非10位ssrc兼容
String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
// 查询到ssrc不一致且开启了ssrc校验则需要针对处理 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
String substring = contentString.substring(0, contentString.indexOf("y="));
try { try {
SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
SessionDescription sdp = gb28181Sdp.getBaseSdb();
int port = -1; int port = -1;
Vector mediaDescriptions = sdp.getMediaDescriptions(true); Vector mediaDescriptions = sdp.getMediaDescriptions(true);
for (Object description : mediaDescriptions) { for (Object description : mediaDescriptions) {
@ -800,17 +799,15 @@ public class PlayServiceImpl implements IPlayService {
ResponseEvent responseEvent = (ResponseEvent) eventResult.event; ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
String contentString = new String(responseEvent.getResponse().getRawContent()); String contentString = new String(responseEvent.getResponse().getRawContent());
// 获取ssrc // 获取ssrc
int ssrcIndex = contentString.indexOf("y="); String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString);
// 检查是否有y字段 // 检查是否有y字段
if (ssrcIndex >= 0) { if (ssrcInResponse != null) {
//ssrc规定长度为10字节不取余下长度以避免后续还有f=字段 TODO 后续对不规范的非10位ssrc兼容
String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
// 查询到ssrc不一致且开启了ssrc校验则需要针对处理 // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) {
String substring = contentString.substring(0, contentString.indexOf("y="));
try { try {
SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString);
SessionDescription sdp = gb28181Sdp.getBaseSdb();
int port = -1; int port = -1;
Vector mediaDescriptions = sdp.getMediaDescriptions(true); Vector mediaDescriptions = sdp.getMediaDescriptions(true);
for (Object description : mediaDescriptions) { for (Object description : mediaDescriptions) {