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; |
package com.genersoft.iot.vmp.gb28181.transmit.request.impl; |
||||
|
|
||||
|
import javax.sip.InvalidArgumentException; |
||||
import javax.sip.RequestEvent; |
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.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请求 |
* @Description:处理INVITE请求 |
||||
* @author: swwheihei |
* @author: panll |
||||
* @date: 2020年5月3日 下午4:43:52 |
* @date: 2021年1月14日 |
||||
*/ |
*/ |
||||
public class InviteRequestProcessor extends SIPRequestAbstractProcessor { |
public class InviteRequestProcessor extends SIPRequestAbstractProcessor { |
||||
|
|
||||
|
private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); |
||||
|
|
||||
|
private SIPCommanderFroPlatform cmderFroPlatform; |
||||
|
|
||||
|
private IVideoManagerStorager storager; |
||||
|
|
||||
/** |
/** |
||||
* 处理invite请求 |
* 处理invite请求 |
||||
* |
* |
||||
* @param request |
* @param evt |
||||
* 请求消息 |
* 请求消息 |
||||
*/ |
*/ |
||||
@Override |
@Override |
||||
public void process(RequestEvent evt) { |
public void process(RequestEvent evt) { |
||||
// TODO 优先级99 Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
|
// Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
|
||||
// Request request = requestEvent.getRequest();
|
try { |
||||
//
|
Request request = evt.getRequest(); |
||||
// try {
|
SipURI sipURI = (SipURI) request.getRequestURI(); |
||||
// // 发送100 Trying
|
String channelId = sipURI.getUser(); |
||||
// ServerTransaction serverTransaction = getServerTransaction(requestEvent);
|
String platformId = null; |
||||
// // 查询目标地址
|
// SubjectHeader subjectHeader = (SubjectHeader)request.getHeader(SubjectHeader.NAME);
|
||||
// URI reqUri = request.getRequestURI();
|
// // 查询通道是否存在 不存在回复404
|
||||
// URI contactURI = currUser.get(reqUri);
|
// if (subjectHeader != null) { // 存在则从subjectHeader 获取平台信息
|
||||
//
|
// String subject = subjectHeader.getSubject();
|
||||
// System.out.println("processInvite rqStr=" + reqUri + " contact=" + contactURI);
|
// if (subject != null) {
|
||||
//
|
// String[] info1 = subject.split(",");
|
||||
// // 根据Request uri来路由,后续的响应消息通过VIA来路由
|
// if (info1 != null && info1 .length == 2) {
|
||||
// Request cliReq = messageFactory.createRequest(request.toString());
|
// String[] info2 = info1[1].split(":");
|
||||
// cliReq.setRequestURI(contactURI);
|
// if (info2 != null && info2.length == 2) {
|
||||
//
|
// platformId = info2[0];
|
||||
// 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);
|
FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); |
||||
// cliReq.addHeader(via);
|
AddressImpl address = (AddressImpl) fromHeader.getAddress(); |
||||
//
|
SipUri uri = (SipUri) address.getURI(); |
||||
// // 更新contact的地址
|
platformId = uri.getUser(); |
||||
// ContactHeader contactHeader = headerFactory.createContactHeader();
|
|
||||
// Address address = SipFactory.getInstance().createAddressFactory()
|
// if (platformId == null) { // 不存在则从fromHeader 获取平台信息
|
||||
// .createAddress("sip:sipsoft@" + SIPMain.ip + ":" + SIPMain.port);
|
// FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
|
||||
// contactHeader.setAddress(address);
|
// platformId = fromHeader.getName();
|
||||
// contactHeader.setExpires(3600);
|
// }
|
||||
// cliReq.setHeader(contactHeader);
|
if (platformId == null || channelId == null) { |
||||
//
|
response400Ack(evt); // 参数不全, 发400,请求错误
|
||||
// clientTransactionId = sipProvider.getNewClientTransaction(cliReq);
|
return; |
||||
// clientTransactionId.sendRequest();
|
} |
||||
//
|
// 查询平台下是否有该通道
|
||||
// System.out.println("processInvite clientTransactionId=" + clientTransactionId.toString());
|
DeviceChannel channel = storager.queryChannelInParentPlatform(platformId, channelId); |
||||
//
|
if (channel == null) { |
||||
// System.out.println("send invite to callee: " + cliReq);
|
response404Ack(evt); // 通道不存在,发404,资源不存在
|
||||
// } catch (TransactionUnavailableException e1) {
|
return; |
||||
// e1.printStackTrace();
|
}else { |
||||
// } catch (SipException e) {
|
response100Ack(evt); // 通道存在,发100,trying
|
||||
// e.printStackTrace();
|
} |
||||
// } catch (ParseException e) {
|
// 解析sdp消息
|
||||
// e.printStackTrace();
|
byte[] sdpByteArray = request.getRawContent(); |
||||
// } catch (Exception e) {
|
SdpParser sdpParser = new SdpParser(); // TODO keng
|
||||
// e.printStackTrace();
|
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