Commit 446f729e559730b813291f072a6e33a012923018
1 parent
03f35986
优化sdp解析,兼容带有f=的设备
Showing
7 changed files
with
129 additions
and
66 deletions
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Gb28181Sdp.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.gb28181.bean; | |
| 2 | + | |
| 3 | +import javax.sdp.SessionDescription; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 28181 的SDP解析器 | |
| 7 | + */ | |
| 8 | +public class Gb28181Sdp { | |
| 9 | + private SessionDescription baseSdb; | |
| 10 | + private String ssrc; | |
| 11 | + | |
| 12 | + private String mediaDescription; | |
| 13 | + | |
| 14 | + public static Gb28181Sdp getInstance(SessionDescription baseSdb, String ssrc, String mediaDescription) { | |
| 15 | + Gb28181Sdp gb28181Sdp = new Gb28181Sdp(); | |
| 16 | + gb28181Sdp.setBaseSdb(baseSdb); | |
| 17 | + gb28181Sdp.setSsrc(ssrc); | |
| 18 | + gb28181Sdp.setMediaDescription(mediaDescription); | |
| 19 | + return gb28181Sdp; | |
| 20 | + } | |
| 21 | + | |
| 22 | + | |
| 23 | + public SessionDescription getBaseSdb() { | |
| 24 | + return baseSdb; | |
| 25 | + } | |
| 26 | + | |
| 27 | + public void setBaseSdb(SessionDescription baseSdb) { | |
| 28 | + this.baseSdb = baseSdb; | |
| 29 | + } | |
| 30 | + | |
| 31 | + public String getSsrc() { | |
| 32 | + return ssrc; | |
| 33 | + } | |
| 34 | + | |
| 35 | + public void setSsrc(String ssrc) { | |
| 36 | + this.ssrc = ssrc; | |
| 37 | + } | |
| 38 | + | |
| 39 | + public String getMediaDescription() { | |
| 40 | + return mediaDescription; | |
| 41 | + } | |
| 42 | + | |
| 43 | + public void setMediaDescription(String mediaDescription) { | |
| 44 | + this.mediaDescription = mediaDescription; | |
| 45 | + } | |
| 46 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
| ... | ... | @@ -54,8 +54,8 @@ public class SIPRequestHeaderPlarformProvider { |
| 54 | 54 | parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); |
| 55 | 55 | //via |
| 56 | 56 | ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); |
| 57 | - ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), | |
| 58 | - parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag()); | |
| 57 | + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), | |
| 58 | + Integer.parseInt(parentPlatform.getDevicePort()), parentPlatform.getTransport(), SipUtils.getNewViaTag()); | |
| 59 | 59 | viaHeader.setRPort(); |
| 60 | 60 | viaHeaders.add(viaHeader); |
| 61 | 61 | //from | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -574,11 +574,7 @@ public class SIPCommander implements ISIPCommander { |
| 574 | 574 | ResponseEvent responseEvent = (ResponseEvent) event.event; |
| 575 | 575 | SIPResponse response = (SIPResponse) responseEvent.getResponse(); |
| 576 | 576 | String contentString =new String(response.getRawContent()); |
| 577 | - int ssrcIndex = contentString.indexOf("y="); | |
| 578 | - String ssrc=ssrcInfo.getSsrc(); | |
| 579 | - if (ssrcIndex >= 0) { | |
| 580 | - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 581 | - } | |
| 577 | + String ssrc = SipUtils.getSsrcFromSdp(contentString); | |
| 582 | 578 | streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, InviteSessionType.DOWNLOAD); |
| 583 | 579 | okEvent.response(event); |
| 584 | 580 | }); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
| ... | ... | @@ -241,18 +241,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 241 | 241 | // 解析sdp消息, 使用jainsip 自带的sdp解析方式 |
| 242 | 242 | String contentString = new String(request.getRawContent()); |
| 243 | 243 | |
| 244 | - // jainSip不支持y=字段, 移除以解析。 | |
| 245 | - // 检查是否有y字段 | |
| 246 | - int ssrcIndex = contentString.indexOf("y="); | |
| 247 | - | |
| 248 | - SessionDescription sdp; | |
| 249 | - if (ssrcIndex >= 0) { | |
| 250 | - //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段 | |
| 251 | - String substring = contentString.substring(0, ssrcIndex); | |
| 252 | - sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 253 | - } else { | |
| 254 | - sdp = SdpFactory.getInstance().createSessionDescription(contentString); | |
| 255 | - } | |
| 244 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 245 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 256 | 246 | String sessionName = sdp.getSessionName().getValue(); |
| 257 | 247 | |
| 258 | 248 | Long startTime = null; |
| ... | ... | @@ -340,11 +330,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 340 | 330 | } |
| 341 | 331 | |
| 342 | 332 | String ssrc; |
| 343 | - if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { | |
| 333 | + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { | |
| 344 | 334 | // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 |
| 345 | 335 | ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); |
| 346 | 336 | }else { |
| 347 | - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 337 | + ssrc = gb28181Sdp.getSsrc(); | |
| 348 | 338 | } |
| 349 | 339 | String streamTypeStr = null; |
| 350 | 340 | if (mediaTransmissionTCP) { |
| ... | ... | @@ -513,11 +503,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 513 | 503 | } else if (gbStream != null) { |
| 514 | 504 | |
| 515 | 505 | String ssrc; |
| 516 | - if (userSetting.getUseCustomSsrcForParentInvite() || ssrcIndex < 0) { | |
| 506 | + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { | |
| 517 | 507 | // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 |
| 518 | 508 | ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); |
| 519 | 509 | }else { |
| 520 | - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 510 | + ssrc = gb28181Sdp.getSsrc(); | |
| 521 | 511 | } |
| 522 | 512 | |
| 523 | 513 | if("push".equals(gbStream.getStreamType())) { |
| ... | ... | @@ -891,20 +881,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements |
| 891 | 881 | } |
| 892 | 882 | String contentString = new String(request.getRawContent()); |
| 893 | 883 | // jainSip不支持y=字段, 移除移除以解析。 |
| 894 | - String substring = contentString; | |
| 895 | 884 | String ssrc = "0000000404"; |
| 896 | - int ssrcIndex = contentString.indexOf("y="); | |
| 897 | - if (ssrcIndex > 0) { | |
| 898 | - substring = contentString.substring(0, ssrcIndex); | |
| 899 | - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 900 | - } | |
| 901 | - ssrcIndex = substring.indexOf("f="); | |
| 902 | - if (ssrcIndex > 0) { | |
| 903 | - substring = contentString.substring(0, ssrcIndex); | |
| 904 | - } | |
| 905 | - SessionDescription sdp = null; | |
| 885 | + | |
| 906 | 886 | try { |
| 907 | - sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 887 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 888 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 908 | 889 | // 获取支持的格式 |
| 909 | 890 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| 910 | 891 | // 查看是否支持PS 负载96 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.gb28181.SipLayer; |
| 4 | +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; | |
| 4 | 5 | import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; |
| 5 | 6 | import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; |
| 6 | 7 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider; |
| 7 | 8 | import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; |
| 9 | +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; | |
| 8 | 10 | import gov.nist.javax.sip.ResponseEventExt; |
| 9 | 11 | import gov.nist.javax.sip.message.SIPResponse; |
| 10 | 12 | import org.slf4j.Logger; |
| ... | ... | @@ -12,7 +14,6 @@ import org.slf4j.LoggerFactory; |
| 12 | 14 | import org.springframework.beans.factory.annotation.Autowired; |
| 13 | 15 | import org.springframework.stereotype.Component; |
| 14 | 16 | |
| 15 | -import javax.sdp.SdpFactory; | |
| 16 | 17 | import javax.sdp.SdpParseException; |
| 17 | 18 | import javax.sdp.SessionDescription; |
| 18 | 19 | import javax.sip.InvalidArgumentException; |
| ... | ... | @@ -79,18 +80,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { |
| 79 | 80 | ResponseEventExt event = (ResponseEventExt)evt; |
| 80 | 81 | |
| 81 | 82 | String contentString = new String(response.getRawContent()); |
| 82 | - // jainSip不支持y=字段, 移除以解析。 | |
| 83 | - int ssrcIndex = contentString.indexOf("y="); | |
| 84 | - // 检查是否有y字段 | |
| 85 | - SessionDescription sdp; | |
| 86 | - if (ssrcIndex >= 0) { | |
| 87 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 | |
| 88 | - String substring = contentString.substring(0, contentString.indexOf("y=")); | |
| 89 | - sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 90 | - } else { | |
| 91 | - sdp = SdpFactory.getInstance().createSessionDescription(contentString); | |
| 92 | - } | |
| 93 | - | |
| 83 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 84 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 94 | 85 | SipURI requestUri = SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort()); |
| 95 | 86 | Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response); |
| 96 | 87 | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
| 1 | 1 | package com.genersoft.iot.vmp.gb28181.utils; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
| 4 | +import com.genersoft.iot.vmp.gb28181.bean.Gb28181Sdp; | |
| 4 | 5 | import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; |
| 5 | 6 | import com.genersoft.iot.vmp.utils.GitUtil; |
| 6 | 7 | import gov.nist.javax.sip.address.AddressImpl; |
| ... | ... | @@ -10,6 +11,9 @@ import gov.nist.javax.sip.message.SIPRequest; |
| 10 | 11 | import org.apache.commons.lang3.RandomStringUtils; |
| 11 | 12 | import org.springframework.util.ObjectUtils; |
| 12 | 13 | |
| 14 | +import javax.sdp.SdpFactory; | |
| 15 | +import javax.sdp.SdpParseException; | |
| 16 | +import javax.sdp.SessionDescription; | |
| 13 | 17 | import javax.sip.PeerUnavailableException; |
| 14 | 18 | import javax.sip.SipFactory; |
| 15 | 19 | import javax.sip.header.FromHeader; |
| ... | ... | @@ -190,4 +194,52 @@ public class SipUtils { |
| 190 | 194 | } |
| 191 | 195 | return deviceChannel; |
| 192 | 196 | } |
| 197 | + | |
| 198 | + public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException { | |
| 199 | + | |
| 200 | + // jainSip不支持y= f=字段, 移除以解析。 | |
| 201 | + int ssrcIndex = sdpStr.indexOf("y="); | |
| 202 | + int mediaDescriptionIndex = sdpStr.indexOf("f="); | |
| 203 | + // 检查是否有y字段 | |
| 204 | + SessionDescription sdp; | |
| 205 | + String ssrc = null; | |
| 206 | + String mediaDescription = null; | |
| 207 | + if (mediaDescriptionIndex == 0 && ssrcIndex == 0) { | |
| 208 | + sdp = SdpFactory.getInstance().createSessionDescription(sdpStr); | |
| 209 | + }else { | |
| 210 | + int baseSdpIndex = Math.min(mediaDescriptionIndex, ssrcIndex); | |
| 211 | + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 | |
| 212 | + String substring = sdpStr.substring(0, baseSdpIndex); | |
| 213 | + sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 214 | + | |
| 215 | + String lines[] = sdpStr.split("\\r?\\n"); | |
| 216 | + for (String line : lines) { | |
| 217 | + if (line.trim().startsWith("y=")) { | |
| 218 | + ssrc = line.substring(2); | |
| 219 | + }else if (line.trim().startsWith("f=")) { | |
| 220 | + mediaDescription = line.substring(2); | |
| 221 | + } | |
| 222 | + if (ssrc != null && mediaDescription != null) { | |
| 223 | + break; | |
| 224 | + } | |
| 225 | + } | |
| 226 | + } | |
| 227 | + return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription); | |
| 228 | + } | |
| 229 | + | |
| 230 | + public static String getSsrcFromSdp(String sdpStr) { | |
| 231 | + | |
| 232 | + // jainSip不支持y= f=字段, 移除以解析。 | |
| 233 | + int ssrcIndex = sdpStr.indexOf("y="); | |
| 234 | + if (ssrcIndex == 0) { | |
| 235 | + return null; | |
| 236 | + } | |
| 237 | + String lines[] = sdpStr.split("\\r?\\n"); | |
| 238 | + for (String line : lines) { | |
| 239 | + if (line.trim().startsWith("y=")) { | |
| 240 | + return line.substring(2); | |
| 241 | + } | |
| 242 | + } | |
| 243 | + return null; | |
| 244 | + } | |
| 193 | 245 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
| ... | ... | @@ -18,6 +18,7 @@ import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 18 | 18 | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| 19 | 19 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 20 | 20 | import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; |
| 21 | +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; | |
| 21 | 22 | import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; |
| 22 | 23 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 23 | 24 | import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; |
| ... | ... | @@ -297,17 +298,16 @@ public class PlayServiceImpl implements IPlayService { |
| 297 | 298 | ResponseEvent responseEvent = (ResponseEvent) event.event; |
| 298 | 299 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| 299 | 300 | // 获取ssrc |
| 300 | - int ssrcIndex = contentString.indexOf("y="); | |
| 301 | + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); | |
| 302 | + | |
| 301 | 303 | // 检查是否有y字段 |
| 302 | - if (ssrcIndex >= 0) { | |
| 303 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
| 304 | - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); | |
| 304 | + if (ssrcInResponse != null) { | |
| 305 | 305 | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| 306 | 306 | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| 307 | 307 | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| 308 | - String substring = contentString.substring(0, contentString.indexOf("y=")); | |
| 309 | 308 | try { |
| 310 | - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 309 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 310 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 311 | 311 | int port = -1; |
| 312 | 312 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| 313 | 313 | for (Object description : mediaDescriptions) { |
| ... | ... | @@ -607,17 +607,16 @@ public class PlayServiceImpl implements IPlayService { |
| 607 | 607 | ResponseEvent responseEvent = (ResponseEvent) eventResult.event; |
| 608 | 608 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| 609 | 609 | // 获取ssrc |
| 610 | - int ssrcIndex = contentString.indexOf("y="); | |
| 610 | + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); | |
| 611 | + | |
| 611 | 612 | // 检查是否有y字段 |
| 612 | - if (ssrcIndex >= 0) { | |
| 613 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
| 614 | - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 613 | + if (ssrcInResponse != null) { | |
| 615 | 614 | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| 616 | 615 | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| 617 | 616 | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| 618 | - String substring = contentString.substring(0, contentString.indexOf("y=")); | |
| 619 | 617 | try { |
| 620 | - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 618 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 619 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 621 | 620 | int port = -1; |
| 622 | 621 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| 623 | 622 | for (Object description : mediaDescriptions) { |
| ... | ... | @@ -800,17 +799,15 @@ public class PlayServiceImpl implements IPlayService { |
| 800 | 799 | ResponseEvent responseEvent = (ResponseEvent) eventResult.event; |
| 801 | 800 | String contentString = new String(responseEvent.getResponse().getRawContent()); |
| 802 | 801 | // 获取ssrc |
| 803 | - int ssrcIndex = contentString.indexOf("y="); | |
| 802 | + String ssrcInResponse = SipUtils.getSsrcFromSdp(contentString); | |
| 804 | 803 | // 检查是否有y字段 |
| 805 | - if (ssrcIndex >= 0) { | |
| 806 | - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
| 807 | - String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); | |
| 804 | + if (ssrcInResponse != null) { | |
| 808 | 805 | // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 |
| 809 | 806 | if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { |
| 810 | 807 | if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { |
| 811 | - String substring = contentString.substring(0, contentString.indexOf("y=")); | |
| 812 | 808 | try { |
| 813 | - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
| 809 | + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); | |
| 810 | + SessionDescription sdp = gb28181Sdp.getBaseSdb(); | |
| 814 | 811 | int port = -1; |
| 815 | 812 | Vector mediaDescriptions = sdp.getMediaDescriptions(true); |
| 816 | 813 | for (Object description : mediaDescriptions) { | ... | ... |