panlinlin
4 years ago
21 changed files with 976 additions and 81 deletions
@ -0,0 +1,65 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
public class Codec { |
|||
|
|||
private int payloadType; |
|||
private String name; |
|||
|
|||
public int getPayloadType() { |
|||
return payloadType; |
|||
} |
|||
|
|||
public void setPayloadType(int payloadType) { |
|||
this.payloadType = payloadType; |
|||
} |
|||
|
|||
public String getName() { |
|||
return name; |
|||
} |
|||
|
|||
public void setName(String name) { |
|||
this.name = name; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object obj) { |
|||
if (!(obj instanceof Codec)) { |
|||
return false; |
|||
} |
|||
Codec codec = (Codec)obj; |
|||
if (codec.getName() == null) { |
|||
return name == null; |
|||
} |
|||
return codec.getName().equalsIgnoreCase(name); |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
StringBuffer buf = new StringBuffer(); |
|||
buf.append(RFC4566_28181.TYPE_ATTRIBUTE).append(RFC4566_28181.SEPARATOR); |
|||
buf.append(RFC4566_28181.ATTR_RTPMAP).append(RFC4566_28181.ATTR_SEPARATOR); |
|||
buf.append(payloadType).append(" ").append(name).append("/"); |
|||
buf.append(9000).append("\r\n"); |
|||
return buf.toString(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,123 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2007, 2008, 2009, 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
import java.net.Inet4Address; |
|||
import java.net.Inet6Address; |
|||
import java.net.InetAddress; |
|||
import java.util.Hashtable; |
|||
import java.util.List; |
|||
|
|||
public class MediaDescription { |
|||
|
|||
private String type; |
|||
private InetAddress ipAddress; |
|||
// attributes not codec-related
|
|||
private Hashtable<String, String> attributes; |
|||
private int port; |
|||
private List<Codec> codecs; |
|||
|
|||
public String getType() { |
|||
return type; |
|||
} |
|||
|
|||
public void setType(String type) { |
|||
this.type = type; |
|||
} |
|||
|
|||
public Hashtable<String, String> getAttributes() { |
|||
return attributes; |
|||
} |
|||
|
|||
public void setAttributes(Hashtable<String, String> attributes) { |
|||
this.attributes = attributes; |
|||
} |
|||
|
|||
public InetAddress getIpAddress() { |
|||
return ipAddress; |
|||
} |
|||
|
|||
public void setIpAddress(InetAddress ipAddress) { |
|||
this.ipAddress = ipAddress; |
|||
} |
|||
|
|||
public int getPort() { |
|||
return port; |
|||
} |
|||
|
|||
public void setPort(int port) { |
|||
this.port = port; |
|||
} |
|||
|
|||
public List<Codec> getCodecs() { |
|||
return codecs; |
|||
} |
|||
|
|||
public void setCodecs(List<Codec> codecs) { |
|||
this.codecs = codecs; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
StringBuffer buf = new StringBuffer(); |
|||
buf.append(RFC4566_28181.TYPE_MEDIA).append(RFC4566_28181.SEPARATOR); |
|||
buf.append(type).append(" ").append(port); |
|||
buf.append(" RTP/AVP"); |
|||
if (codecs != null) { |
|||
for (Codec codec: codecs) { |
|||
buf.append(" "); |
|||
buf.append(codec.getPayloadType()); |
|||
} |
|||
buf.append("\r\n"); |
|||
} |
|||
if (ipAddress != null) { |
|||
int ipVersion; |
|||
if (ipAddress instanceof Inet4Address) { |
|||
ipVersion = 4; |
|||
} else if (ipAddress instanceof Inet6Address) { |
|||
ipVersion = 6; |
|||
} else { |
|||
throw new RuntimeException("unknown ip version: " + ipAddress); |
|||
} |
|||
buf.append(RFC4566_28181.TYPE_CONNECTION).append(RFC4566_28181.SEPARATOR); |
|||
buf.append("IN IP").append(ipVersion).append(" "); |
|||
buf.append(ipAddress.getHostAddress()).append("\r\n"); |
|||
} |
|||
if (codecs != null) { |
|||
for (Codec codec: codecs) { |
|||
buf.append(codec.toString()); |
|||
} |
|||
} |
|||
|
|||
if (attributes != null) { |
|||
for (String attributeName: attributes.keySet()) { |
|||
buf.append(RFC4566_28181.TYPE_ATTRIBUTE).append(RFC4566_28181.SEPARATOR); |
|||
buf.append(attributeName); |
|||
String attributeValue = attributes.get(attributeName); |
|||
if (attributeValue != null && !"".equals(attributeValue.trim())) { |
|||
buf.append(":").append(attributeValue); |
|||
} |
|||
buf.append("\r\n"); |
|||
} |
|||
} |
|||
return buf.toString(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,51 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2007, 2008, 2009, 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
public class RFC4566_28181 { |
|||
|
|||
public static final char VERSION = '0'; |
|||
|
|||
public static final char TYPE_VERSION = 'v'; |
|||
public static final char TYPE_ORIGIN = 'o'; |
|||
public static final char TYPE_SUBJECT = 's'; |
|||
public static final char TYPE_INFO = 'i'; |
|||
public static final char TYPE_URI = 'u'; |
|||
public static final char TYPE_EMAIL = 'e'; |
|||
public static final char TYPE_PHONE = 'p'; |
|||
public static final char TYPE_CONNECTION = 'c'; |
|||
public static final char TYPE_BANDWITH = 'b'; |
|||
public static final char TYPE_TIME = 't'; |
|||
public static final char TYPE_REPEAT = 'r'; |
|||
public static final char TYPE_ZONE = 'z'; |
|||
public static final char TYPE_KEY = 'k'; |
|||
public static final char TYPE_ATTRIBUTE = 'a'; |
|||
public static final char TYPE_MEDIA = 'm'; |
|||
public static final char TYPE_SSRC = 'y'; |
|||
public static final char TYPE_MEDIA_DES = 'f'; |
|||
|
|||
public static final char SEPARATOR = '='; |
|||
public static final char ATTR_SEPARATOR = ':'; |
|||
|
|||
public static final String MEDIA_AUDIO = "audio"; |
|||
|
|||
public static final String ATTR_RTPMAP = "rtpmap"; |
|||
public static final String ATTR_SENDRECV = "sendrecv"; |
|||
} |
@ -0,0 +1,38 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2007, 2008, 2009, 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
public class SdpLine { |
|||
private char type; |
|||
private String value; |
|||
public char getType() { |
|||
return type; |
|||
} |
|||
public void setType(char type) { |
|||
this.type = type; |
|||
} |
|||
public String getValue() { |
|||
return value; |
|||
} |
|||
public void setValue(String value) { |
|||
this.value = value; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,230 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2007, 2008, 2009, 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStreamReader; |
|||
import java.net.InetAddress; |
|||
import java.util.ArrayList; |
|||
import java.util.Hashtable; |
|||
import java.util.List; |
|||
|
|||
public class SdpParser { |
|||
|
|||
public SessionDescription parse(byte[] body) throws IOException { |
|||
if (body == null || body.length == 0) { |
|||
return null; |
|||
} |
|||
ByteArrayInputStream in = new ByteArrayInputStream(body); |
|||
InputStreamReader inputStreamReader = new InputStreamReader(in); |
|||
BufferedReader reader = new BufferedReader(inputStreamReader); |
|||
SessionDescription sessionDescription = new SessionDescription(); |
|||
|
|||
//version
|
|||
|
|||
String line = reader.readLine(); |
|||
if (line.length() < 3) { |
|||
return null; |
|||
} |
|||
if (line.charAt(0) != RFC4566_28181.TYPE_VERSION |
|||
|| line.charAt(1) != RFC4566_28181.SEPARATOR |
|||
|| line.charAt(2) != RFC4566_28181.VERSION) { |
|||
return null; |
|||
} |
|||
|
|||
//origin
|
|||
|
|||
line = reader.readLine(); |
|||
if (line.length() < 3) { |
|||
return null; |
|||
} |
|||
if (line.charAt(0) != RFC4566_28181.TYPE_ORIGIN |
|||
|| line.charAt(1) != RFC4566_28181.SEPARATOR) { |
|||
return null; |
|||
} |
|||
line = line.substring(2); |
|||
String[] originArr = line.split(" "); |
|||
if (originArr == null || originArr.length != 6) { |
|||
return null; |
|||
} |
|||
sessionDescription.setUsername(originArr[0]); |
|||
sessionDescription.setId(Long.parseLong(originArr[1])); |
|||
sessionDescription.setVersion(Long.parseLong(originArr[2])); |
|||
sessionDescription.setIpAddress(InetAddress.getByName(originArr[5])); |
|||
|
|||
//name
|
|||
|
|||
line = reader.readLine(); |
|||
if (line.length() < 3) { |
|||
return null; |
|||
} |
|||
if (line.charAt(0) != RFC4566_28181.TYPE_SUBJECT |
|||
|| line.charAt(1) != RFC4566_28181.SEPARATOR) { |
|||
return null; |
|||
} |
|||
sessionDescription.setName(line.substring(2)); |
|||
|
|||
//session connection and attributes
|
|||
Hashtable<String, String> sessionAttributes = new Hashtable<String, String>(); |
|||
sessionDescription.setAttributes(sessionAttributes); |
|||
|
|||
while ((line = reader.readLine()) != null |
|||
&& line.charAt(0) != RFC4566_28181.TYPE_MEDIA) { |
|||
if (line.length() > 3 |
|||
&& line.charAt(0) == RFC4566_28181.TYPE_CONNECTION |
|||
&& line.charAt(1) == RFC4566_28181.SEPARATOR) { |
|||
String connection = parseConnection(line.substring(2)); |
|||
if (connection == null) { |
|||
continue; |
|||
} |
|||
sessionDescription.setIpAddress(InetAddress.getByName(connection)); |
|||
} else if (line.length() > 3 |
|||
&& line.charAt(0) == RFC4566_28181.TYPE_ATTRIBUTE |
|||
&& line.charAt(1) == RFC4566_28181.SEPARATOR) { |
|||
String value = line.substring(2); |
|||
int pos = value.indexOf(RFC4566_28181.ATTR_SEPARATOR); |
|||
if (pos > -1) { |
|||
sessionAttributes.put(value.substring(0, pos), |
|||
value.substring(pos + 1)); |
|||
} else { |
|||
sessionAttributes.put(value, ""); |
|||
} |
|||
} |
|||
} |
|||
if (line == null) { |
|||
return null; |
|||
} |
|||
//we are at the first media line
|
|||
|
|||
ArrayList<SdpLine> mediaLines = new ArrayList<SdpLine>(); |
|||
do { |
|||
if (line.length() < 2) { |
|||
return null; |
|||
} |
|||
if (line.charAt(1) != RFC4566_28181.SEPARATOR) { |
|||
return null; |
|||
} |
|||
if (line.charAt(0) == RFC4566_28181.TYPE_SSRC) { |
|||
sessionDescription.setSsrc(line.length() >=2 ?line.substring(2):""); |
|||
}else if (line.charAt(0) == RFC4566_28181.TYPE_MEDIA_DES) { |
|||
sessionDescription.setGbMediaDescriptions(line.length() >=2 ?line.substring(2):""); |
|||
}else { |
|||
SdpLine mediaLine = new SdpLine(); |
|||
mediaLine.setType(line.charAt(0)); |
|||
mediaLine.setValue(line.substring(2)); |
|||
mediaLines.add(mediaLine); |
|||
} |
|||
|
|||
} |
|||
while ((line = reader.readLine()) != null ); |
|||
|
|||
ArrayList<MediaDescription> mediaDescriptions = new ArrayList<MediaDescription>(); |
|||
sessionDescription.setMediaDescriptions(mediaDescriptions); |
|||
|
|||
for (SdpLine sdpLine : mediaLines) { |
|||
MediaDescription mediaDescription; |
|||
if (sdpLine.getType() == RFC4566_28181.TYPE_MEDIA) { |
|||
String[] mediaArr = sdpLine.getValue().split(" "); |
|||
if (mediaArr == null || mediaArr.length < 4) { |
|||
return null; |
|||
} |
|||
mediaDescription = new MediaDescription(); |
|||
mediaDescription.setType(mediaArr[0]); |
|||
//TODO manage port range
|
|||
mediaDescription.setPort(Integer.parseInt(mediaArr[1])); |
|||
mediaDescription.setAttributes(new Hashtable<String, String>()); |
|||
List<Codec> codecs = new ArrayList<Codec>(); |
|||
for (int i = 3; i < mediaArr.length; ++i) { |
|||
int payloadType = Integer.parseInt(mediaArr[i]); |
|||
Codec codec = new Codec(); |
|||
codec.setPayloadType(payloadType); |
|||
codec.setName("unsupported"); |
|||
codecs.add(codec); |
|||
} |
|||
mediaDescription.setCodecs(codecs); |
|||
mediaDescriptions.add(mediaDescription); |
|||
} else { |
|||
mediaDescription = mediaDescriptions.get(mediaDescriptions.size() - 1); |
|||
String sdpLineValue = sdpLine.getValue(); |
|||
if (sdpLine.getType() == RFC4566_28181.TYPE_CONNECTION) { |
|||
String ipAddress = parseConnection(sdpLineValue); |
|||
mediaDescription.setIpAddress(InetAddress.getByName(ipAddress)); |
|||
} else if (sdpLine.getType() == RFC4566_28181.TYPE_ATTRIBUTE) { |
|||
Hashtable<String, String> attributes = mediaDescription.getAttributes(); |
|||
int pos = sdpLineValue.indexOf(RFC4566_28181.ATTR_SEPARATOR); |
|||
if (pos > -1) { |
|||
String name = sdpLineValue.substring(0, pos); |
|||
String value = sdpLineValue.substring(pos + 1); |
|||
pos = value.indexOf(" "); |
|||
if (pos > -1) { |
|||
int payloadType; |
|||
try { |
|||
payloadType = Integer.parseInt(value.substring(0, pos)); |
|||
List<Codec> codecs = mediaDescription.getCodecs(); |
|||
for (Codec codec: codecs) { |
|||
if (codec.getPayloadType() == payloadType) { |
|||
value = value.substring(pos + 1); |
|||
pos = value.indexOf("/"); |
|||
if (pos > -1) { |
|||
value = value.substring(0, pos); |
|||
codec.setName(value); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} catch (NumberFormatException e) { |
|||
attributes.put(name, value); |
|||
} |
|||
} else { |
|||
attributes.put(name, value); |
|||
} |
|||
} else { |
|||
attributes.put(sdpLineValue, ""); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
sessionDescription.setMediaDescriptions(mediaDescriptions); |
|||
|
|||
for (MediaDescription description : mediaDescriptions) { |
|||
if (description.getIpAddress() == null) { |
|||
InetAddress sessionAddress = sessionDescription.getIpAddress(); |
|||
if (sessionAddress == null) { |
|||
return null; |
|||
} |
|||
description.setIpAddress(sessionAddress); |
|||
} |
|||
} |
|||
|
|||
|
|||
return sessionDescription; |
|||
} |
|||
|
|||
private String parseConnection(String line) { |
|||
String[] connectionArr = line.split(" "); |
|||
if (connectionArr == null || connectionArr.length != 3) { |
|||
return null; |
|||
} |
|||
return connectionArr[2]; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,162 @@ |
|||
/* |
|||
This file is part of Peers, a java SIP softphone. |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
|||
Copyright 2007, 2008, 2009, 2010 Yohann Martineau |
|||
*/ |
|||
|
|||
package com.genersoft.iot.vmp.gb28181.sdp; |
|||
|
|||
import java.net.Inet4Address; |
|||
import java.net.Inet6Address; |
|||
import java.net.InetAddress; |
|||
import java.util.Hashtable; |
|||
import java.util.List; |
|||
|
|||
public class SessionDescription { |
|||
|
|||
private long id; |
|||
private long version; |
|||
private String name; |
|||
private String username; |
|||
private InetAddress ipAddress; |
|||
private List<MediaDescription> mediaDescriptions; |
|||
private Hashtable<String, String> attributes; |
|||
private String ssrc; |
|||
private String gbMediaDescriptions; |
|||
|
|||
public SessionDescription() { |
|||
} |
|||
|
|||
public long getId() { |
|||
return id; |
|||
} |
|||
|
|||
public void setId(long id) { |
|||
this.id = id; |
|||
} |
|||
|
|||
public InetAddress getIpAddress() { |
|||
return ipAddress; |
|||
} |
|||
|
|||
public void setIpAddress(InetAddress ipAddress) { |
|||
this.ipAddress = ipAddress; |
|||
} |
|||
|
|||
public List<MediaDescription> getMediaDescriptions() { |
|||
return mediaDescriptions; |
|||
} |
|||
|
|||
public void setMediaDescriptions(List<MediaDescription> mediaDescriptions) { |
|||
this.mediaDescriptions = mediaDescriptions; |
|||
} |
|||
|
|||
public String getName() { |
|||
return name; |
|||
} |
|||
|
|||
public void setName(String name) { |
|||
this.name = name; |
|||
} |
|||
|
|||
public String getUsername() { |
|||
return username; |
|||
} |
|||
|
|||
public void setUsername(String username) { |
|||
this.username = username; |
|||
} |
|||
|
|||
public long getVersion() { |
|||
return version; |
|||
} |
|||
|
|||
public void setVersion(long version) { |
|||
this.version = version; |
|||
} |
|||
|
|||
public Hashtable<String, String> getAttributes() { |
|||
return attributes; |
|||
} |
|||
|
|||
public void setAttributes(Hashtable<String, String> attributes) { |
|||
this.attributes = attributes; |
|||
} |
|||
|
|||
public String getSsrc() { |
|||
return ssrc; |
|||
} |
|||
|
|||
public void setSsrc(String ssrc) { |
|||
this.ssrc = ssrc; |
|||
} |
|||
|
|||
public String getGbMediaDescriptions() { |
|||
return gbMediaDescriptions; |
|||
} |
|||
|
|||
public void setGbMediaDescriptions(String gbMediaDescriptions) { |
|||
this.gbMediaDescriptions = gbMediaDescriptions; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
StringBuilder buf = new StringBuilder(); |
|||
buf.append("v=0\r\n"); |
|||
buf.append("o=").append(username).append(" ").append(id); |
|||
buf.append(" ").append(version); |
|||
int ipVersion; |
|||
if (ipAddress instanceof Inet4Address) { |
|||
ipVersion = 4; |
|||
} else if (ipAddress instanceof Inet6Address) { |
|||
ipVersion = 6; |
|||
} else { |
|||
throw new RuntimeException("unknown ip version: " + ipAddress); |
|||
} |
|||
buf.append(" IN IP").append(ipVersion).append(" "); |
|||
String hostAddress = ipAddress.getHostAddress(); |
|||
buf.append(hostAddress).append("\r\n"); |
|||
buf.append("s=").append(name).append("\r\n"); |
|||
buf.append("c=IN IP").append(ipVersion).append(" "); |
|||
buf.append(hostAddress).append("\r\n"); |
|||
buf.append("t=0 0\r\n"); |
|||
if (attributes != null){ |
|||
for (String attributeName: attributes.keySet()) { |
|||
String attributeValue = attributes.get(attributeName); |
|||
buf.append("a=").append(attributeName); |
|||
if (attributeValue != null && !"".equals(attributeValue.trim())) { |
|||
buf.append(":"); |
|||
buf.append(attributeValue); |
|||
buf.append("\r\n"); |
|||
} |
|||
} |
|||
} |
|||
if (mediaDescriptions != null){ |
|||
for (MediaDescription mediaDescription: mediaDescriptions) { |
|||
buf.append(mediaDescription.toString()); |
|||
} |
|||
} |
|||
|
|||
if (ssrc != null){ |
|||
buf.append("y=").append(ssrc).append("\r\n"); |
|||
} |
|||
if (gbMediaDescriptions != null){ |
|||
buf.append("f=").append(gbMediaDescriptions).append("\r\n"); |
|||
} |
|||
return buf.toString(); |
|||
} |
|||
|
|||
} |
@ -1,71 +1,231 @@ |
|||
package com.genersoft.iot.vmp.gb28181.transmit.request.impl; |
|||
|
|||
import javax.sip.InvalidArgumentException; |
|||
import javax.sip.RequestEvent; |
|||
import javax.sip.SipException; |
|||
import javax.sip.address.SipURI; |
|||
import javax.sip.header.ContentTypeHeader; |
|||
import javax.sip.header.FromHeader; |
|||
import javax.sip.header.SubjectHeader; |
|||
import javax.sip.message.Request; |
|||
import javax.sip.message.Response; |
|||
|
|||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; |
|||
import com.genersoft.iot.vmp.gb28181.sdp.Codec; |
|||
import com.genersoft.iot.vmp.gb28181.sdp.MediaDescription; |
|||
import com.genersoft.iot.vmp.gb28181.sdp.SdpParser; |
|||
import com.genersoft.iot.vmp.gb28181.sdp.SessionDescription; |
|||
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.request.SIPRequestAbstractProcessor; |
|||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
|||
import gov.nist.javax.sip.address.AddressImpl; |
|||
import gov.nist.javax.sip.address.SipUri; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
import java.io.IOException; |
|||
import java.text.ParseException; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @Description:处理INVITE请求 |
|||
* @author: swwheihei |
|||
* @date: 2020年5月3日 下午4:43:52 |
|||
* @author: panll |
|||
* @date: 2021年1月14日 |
|||
*/ |
|||
public class InviteRequestProcessor extends SIPRequestAbstractProcessor { |
|||
|
|||
private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); |
|||
|
|||
private SIPCommanderFroPlatform cmderFroPlatform; |
|||
|
|||
private IVideoManagerStorager storager; |
|||
|
|||
/** |
|||
* 处理invite请求 |
|||
* |
|||
* @param request |
|||
* @param evt |
|||
* 请求消息 |
|||
*/ |
|||
@Override |
|||
public void process(RequestEvent evt) { |
|||
// TODO 优先级99 Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
|
|||
// Request request = requestEvent.getRequest();
|
|||
//
|
|||
// try {
|
|||
// // 发送100 Trying
|
|||
// ServerTransaction serverTransaction = getServerTransaction(requestEvent);
|
|||
// // 查询目标地址
|
|||
// URI reqUri = request.getRequestURI();
|
|||
// URI contactURI = currUser.get(reqUri);
|
|||
//
|
|||
// System.out.println("processInvite rqStr=" + reqUri + " contact=" + contactURI);
|
|||
//
|
|||
// // 根据Request uri来路由,后续的响应消息通过VIA来路由
|
|||
// Request cliReq = messageFactory.createRequest(request.toString());
|
|||
// cliReq.setRequestURI(contactURI);
|
|||
//
|
|||
// HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory();
|
|||
// Via callerVia = (Via) request.getHeader(Via.NAME);
|
|||
// Via via = (Via) headerFactory.createViaHeader(SIPMain.ip, SIPMain.port, "UDP",
|
|||
// callerVia.getBranch() + "sipphone");
|
|||
//
|
|||
// cliReq.removeHeader(Via.NAME);
|
|||
// cliReq.addHeader(via);
|
|||
//
|
|||
// // 更新contact的地址
|
|||
// ContactHeader contactHeader = headerFactory.createContactHeader();
|
|||
// Address address = SipFactory.getInstance().createAddressFactory()
|
|||
// .createAddress("sip:sipsoft@" + SIPMain.ip + ":" + SIPMain.port);
|
|||
// contactHeader.setAddress(address);
|
|||
// contactHeader.setExpires(3600);
|
|||
// cliReq.setHeader(contactHeader);
|
|||
//
|
|||
// clientTransactionId = sipProvider.getNewClientTransaction(cliReq);
|
|||
// clientTransactionId.sendRequest();
|
|||
//
|
|||
// System.out.println("processInvite clientTransactionId=" + clientTransactionId.toString());
|
|||
//
|
|||
// System.out.println("send invite to callee: " + cliReq);
|
|||
// } catch (TransactionUnavailableException e1) {
|
|||
// e1.printStackTrace();
|
|||
// } catch (SipException e) {
|
|||
// e.printStackTrace();
|
|||
// } catch (ParseException e) {
|
|||
// e.printStackTrace();
|
|||
// } catch (Exception e) {
|
|||
// e.printStackTrace();
|
|||
// }
|
|||
// Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
|
|||
try { |
|||
Request request = evt.getRequest(); |
|||
SipURI sipURI = (SipURI) request.getRequestURI(); |
|||
String channelId = sipURI.getUser(); |
|||
String platformId = null; |
|||
// SubjectHeader subjectHeader = (SubjectHeader)request.getHeader(SubjectHeader.NAME);
|
|||
// // 查询通道是否存在 不存在回复404
|
|||
// if (subjectHeader != null) { // 存在则从subjectHeader 获取平台信息
|
|||
// String subject = subjectHeader.getSubject();
|
|||
// if (subject != null) {
|
|||
// String[] info1 = subject.split(",");
|
|||
// if (info1 != null && info1 .length == 2) {
|
|||
// String[] info2 = info1[1].split(":");
|
|||
// if (info2 != null && info2.length == 2) {
|
|||
// platformId = info2[0];
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
|
|||
FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); |
|||
AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
|||
SipUri uri = (SipUri) address.getURI(); |
|||
platformId = uri.getUser(); |
|||
|
|||
// if (platformId == null) { // 不存在则从fromHeader 获取平台信息
|
|||
// FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
|
|||
// platformId = fromHeader.getName();
|
|||
// }
|
|||
if (platformId == null || channelId == null) { |
|||
response400Ack(evt); // 参数不全, 发400,请求错误
|
|||
return; |
|||
} |
|||
// 查询平台下是否有该通道
|
|||
DeviceChannel channel = storager.queryChannelInParentPlatform(platformId, channelId); |
|||
if (channel == null) { |
|||
response404Ack(evt); // 通道不存在,发404,资源不存在
|
|||
return; |
|||
}else { |
|||
response100Ack(evt); // 通道存在,发100,trying
|
|||
} |
|||
// 解析sdp消息
|
|||
byte[] sdpByteArray = request.getRawContent(); |
|||
SdpParser sdpParser = new SdpParser(); // TODO keng
|
|||
SessionDescription sdp = sdpParser.parse(sdpByteArray); |
|||
// 获取支持的格式
|
|||
List<MediaDescription> mediaDescriptions = sdp.getMediaDescriptions(); |
|||
// 查看是否支持PS 负载96
|
|||
String ip = null; |
|||
int port = -1; |
|||
for (MediaDescription mediaDescription : mediaDescriptions) { |
|||
|
|||
List<Codec> codecs = mediaDescription.getCodecs(); |
|||
for (Codec codec : codecs) { |
|||
if("96".equals(codec.getPayloadType()) || "PS".equals(codec.getName()) || "ps".equals(codec.getName())) { |
|||
ip = mediaDescription.getIpAddress().getHostName(); |
|||
port = mediaDescription.getPort(); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if (ip == null || port == -1) { // TODO 没有合适的视频流格式, 可配置是否使用第一个media信息
|
|||
if (mediaDescriptions.size() > 0) { |
|||
ip = mediaDescriptions.get(0).getIpAddress().getHostName(); |
|||
port = mediaDescriptions.get(0).getPort(); |
|||
} |
|||
} |
|||
|
|||
if (ip == null || port == -1) { |
|||
response488Ack(evt); |
|||
return; |
|||
} |
|||
|
|||
|
|||
String ssrc = sdp.getSsrc(); |
|||
// 通知下级推流,
|
|||
// 查找合适的端口推流,
|
|||
// 发送 200ok
|
|||
// 收到ack后调用推流接口
|
|||
|
|||
|
|||
|
|||
|
|||
} catch (SipException | InvalidArgumentException | ParseException e) { |
|||
e.printStackTrace(); |
|||
} catch (IOException e) { |
|||
logger.warn("sdp解析错误"); |
|||
e.printStackTrace(); |
|||
} |
|||
|
|||
} |
|||
|
|||
/*** |
|||
* 回复100 trying |
|||
* @param evt |
|||
* @throws SipException |
|||
* @throws InvalidArgumentException |
|||
* @throws ParseException |
|||
*/ |
|||
private void response100Ack(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { |
|||
Response response = getMessageFactory().createResponse(Response.TRYING, evt.getRequest()); |
|||
getServerTransaction(evt).sendResponse(response); |
|||
} |
|||
|
|||
/*** |
|||
* 回复404 |
|||
* @param evt |
|||
* @throws SipException |
|||
* @throws InvalidArgumentException |
|||
* @throws ParseException |
|||
*/ |
|||
private void response404Ack(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { |
|||
Response response = getMessageFactory().createResponse(Response.NOT_FOUND, evt.getRequest()); |
|||
getServerTransaction(evt).sendResponse(response); |
|||
} |
|||
|
|||
/*** |
|||
* 回复400 |
|||
* @param evt |
|||
* @throws SipException |
|||
* @throws InvalidArgumentException |
|||
* @throws ParseException |
|||
*/ |
|||
private void response400Ack(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { |
|||
Response response = getMessageFactory().createResponse(Response.BAD_REQUEST, evt.getRequest()); |
|||
getServerTransaction(evt).sendResponse(response); |
|||
} |
|||
|
|||
/*** |
|||
* 回复488 |
|||
* @param evt |
|||
* @throws SipException |
|||
* @throws InvalidArgumentException |
|||
* @throws ParseException |
|||
*/ |
|||
private void response488Ack(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException { |
|||
Response response = getMessageFactory().createResponse(Response.NOT_ACCEPTABLE_HERE, evt.getRequest()); |
|||
getServerTransaction(evt).sendResponse(response); |
|||
} |
|||
|
|||
/*** |
|||
* 回复200 OK |
|||
* @param evt |
|||
* @throws SipException |
|||
* @throws InvalidArgumentException |
|||
* @throws ParseException |
|||
*/ |
|||
private void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException { |
|||
Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); |
|||
ContentTypeHeader contentTypeHeader = getHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); |
|||
response.setContent(sdp, contentTypeHeader); |
|||
getServerTransaction(evt).sendResponse(response); |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
public SIPCommanderFroPlatform getCmderFroPlatform() { |
|||
return cmderFroPlatform; |
|||
} |
|||
|
|||
public void setCmderFroPlatform(SIPCommanderFroPlatform cmderFroPlatform) { |
|||
this.cmderFroPlatform = cmderFroPlatform; |
|||
} |
|||
|
|||
public IVideoManagerStorager getStorager() { |
|||
return storager; |
|||
} |
|||
|
|||
public void setStorager(IVideoManagerStorager storager) { |
|||
this.storager = storager; |
|||
} |
|||
} |
|||
|
Loading…
Reference in new issue