/*
* Copyright 2007 Sun Microsystems, Inc.
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge 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/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.ParseException;
import java.util.Vector;
public class SdpManager {
private static Vector supportedMedia = new Vector();
private MediaInfo localMediaPreference;
private MediaInfo transmitMediaInfo;
private boolean isMacOS = false;
private int maxSampleRate;
private int maxChannels;
private static boolean useTelephoneEvent = true;
public SdpManager() {
String s = System.getProperty("os.name");
if (s.equals("Mac OS X")) {
isMacOS = true;
maxSampleRate = RtpPacket.MAC_SAMPLE_RATE;
maxChannels = RtpPacket.MAC_CHANNELS;
} else {
maxSampleRate = RtpPacket.MAX_SAMPLE_RATE;
maxChannels = RtpPacket.MAX_CHANNELS;
}
}
public static void useTelephoneEvent(boolean useTelephoneEvent) {
SdpManager.useTelephoneEvent = useTelephoneEvent;
}
public static boolean useTelephoneEvent() {
return useTelephoneEvent;
}
public SdpManager(Vector supportedMedia) {
this.supportedMedia = supportedMedia;
}
public static void setSupportedMedia(Vector supportedMedia) {
SdpManager.supportedMedia = supportedMedia;
}
public void setPreferredMedia(int encoding, int sampleRate, int channels)
throws ParseException {
if (sampleRate == 8000 && channels == 1) {
encoding = RtpPacket.PCMU_ENCODING;
}
setPreferredMediaInfo(findMediaInfo(encoding, sampleRate, channels));
}
public void setPreferredMediaInfo(MediaInfo preferredMediaInfo) {
localMediaPreference = preferredMediaInfo;
maxSampleRate = localMediaPreference.getSampleRate();
maxChannels = localMediaPreference.getChannels();
}
public MediaInfo getPreferredMediaInfo() {
return localMediaPreference;
}
public void setTransmitMediaInfo(int encoding, int sampleRate,
int channels) throws ParseException {
transmitMediaInfo = findMediaInfo(encoding, sampleRate, channels);
}
public void setTransmitMediaInfo(MediaInfo transmitMediaInfo) {
this.transmitMediaInfo = transmitMediaInfo;
}
public MediaInfo getTransmitMediaInfo() {
return transmitMediaInfo;
}
public static SdpInfo parseSdp(String sdpData) throws ParseException {
return new SdpParser().parseSdp(sdpData);
}
/*
* Get supported media
*/
private String getSupportedMedia() {
String s = "";
int n = 0;
for (int i = 0; i < supportedMedia.size(); i++) {
MediaInfo mediaInfo = (MediaInfo) supportedMedia.elementAt(i);
if (mediaInfo.getSampleRate() > maxSampleRate || mediaInfo.getChannels() > maxChannels) {
continue;
}
if (useTelephoneEvent == false && mediaInfo.isTelephoneEventPayload())
{
continue;
}
if (n > 0) {
s += " ";
}
s += mediaInfo.getPayload();
n++;
}
return s;
}
/*
* Get supported rtpmaps
*/
private String getRtpmaps() {
String rtpmaps = "";
for (int i = 0; i < supportedMedia.size(); i++)
{
MediaInfo mediaInfo = (MediaInfo) supportedMedia.elementAt(i);
if (mediaInfo.getSampleRate() > maxSampleRate || mediaInfo.getChannels() > maxChannels)
{
continue;
}
rtpmaps += generateRtpmap(mediaInfo) + "\r\n";
}
return rtpmaps;
}
/*
* Get the rtpmap for a specific payload
*/
private String getRtpmap(byte payload) throws ParseException {
return generateRtpmap(findMediaInfo(payload));
}
/*
* Generate an rtpmap entry for a speicifed MediaInfo.
*/
private String generateRtpmap(MediaInfo mediaInfo) {
if (mediaInfo.isTelephoneEventPayload()) {
// if (useTelephoneEvent == false) {
// return "";
// }
return "a=rtpmap:" + mediaInfo.getPayload()
+ " telephone-event/8000";
}
return "a=rtpmap:" + mediaInfo.getPayload() + " "
+ mediaInfo.getEncodingString() + "/"
+ mediaInfo.getSampleRate() + "/"
+ mediaInfo.getChannels();
}
/*
* Find the MediaInfo for a specified payload
*/
public static MediaInfo findMediaInfo(byte payload)
throws ParseException {
for (int i = 0; i < supportedMedia.size(); i++) {
MediaInfo mediaInfo = (MediaInfo)
supportedMedia.elementAt(i);
if (mediaInfo.getPayload() == payload) {
return mediaInfo;
}
}
throw new ParseException("Unsupported payload " + payload, 0);
}
/*
* Find the MediaInfo for specified parameters
*/
public static MediaInfo findMediaInfo(int encoding, int sampleRate,
int channels) throws ParseException {
for (int i = 0; i < supportedMedia.size(); i++) {
MediaInfo mediaInfo = (MediaInfo)
supportedMedia.elementAt(i);
if (mediaInfo.isTelephoneEventPayload()) {
continue; // skip this one
}
if (mediaInfo.getEncoding() == encoding &&
mediaInfo.getSampleRate() == sampleRate &&
mediaInfo.getChannels() == channels) {
return mediaInfo;
}
}
throw new ParseException("Unsupported media " +
"encoding " + encoding + " sample rate " + sampleRate
+ " channels " + channels, 0);
}
public String generateSdp(CallParticipant cp, String name, InetSocketAddress isa)
{
String toNumber = cp.getPhoneNumber();
String sdp = "v=0\r\n"
+ "o=" + name + " 1 1 IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "s=SIP Call\r\n"
+ "c=IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "t=0 0 \r\n"
+ "m=audio " + isa.getPort();
if (toNumber.indexOf("sip:") == 0) // TODO hack for Lync DTMF
{
sdp += " RTP/AVP " + "13 " + getSupportedMedia() + "\r\n"
+ "a=rtpmap:13 CN/8000" + "\r\n";
} else { // Lync, add DTMF support
sdp += " RTP/AVP " + "13 101 " + getSupportedMedia() + "\r\n"
+ "a=rtpmap:13 CN/8000" + "\r\n"
+ "a=rtpmap:101 telephone-event/8000" + "\r\n"
+ "a=fmtp:101 0-16" + "\r\n";
}
sdp += getRtpmaps();
if (localMediaPreference != null)
{
sdp += "a=PreferredPayload:" + localMediaPreference.getPayload() + "\r\n";
}
if (transmitMediaInfo != null) {
sdp += "a=transmitPayload:"
+ transmitMediaInfo.getPayload() + "\r\n";
}
return sdp;
}
public String generateSdp(String name, InetSocketAddress isa, SdpInfo remoteSdpInfo) throws IOException
{
MediaInfo mediaInfo = null;
if (localMediaPreference != null)
{
if (remoteSdpInfo.isSupported(localMediaPreference)) {
mediaInfo = localMediaPreference;
Logger.println("Using local media preference: " + mediaInfo);
}
}
/*
* Try remote media preference
*/
if (remoteSdpInfo.preferredMediaSpecified())
{
MediaInfo remoteMediaPreference = remoteSdpInfo.getMediaInfo();
if (remoteMediaPreference.getSampleRate() <= maxSampleRate &&
remoteMediaPreference.getChannels() <= maxChannels) {
/*
* See if remote media preference is supported
*/
try {
mediaInfo = findMediaInfo(remoteMediaPreference.getPayload());
Logger.println("Using remote media preference: " + mediaInfo);
} catch (ParseException e) {
}
}
}
if (mediaInfo == null) {
/*
* default to 8000/1 ulaw
*/
mediaInfo = remoteSdpInfo.findBestMediaInfo(supportedMedia, localMediaPreference);
Logger.println("Using best media " + mediaInfo);
}
remoteSdpInfo.setMediaInfo(mediaInfo);
String payloads = "13 101 " + mediaInfo.getPayload();
byte telephoneEventPayload = remoteSdpInfo.getTelephoneEventPayload();
String telephoneEvent = "";
if (useTelephoneEvent == true && telephoneEventPayload != 0) {
try {
MediaInfo m = findMediaInfo(telephoneEventPayload);
payloads += " " + telephoneEventPayload;
telephoneEvent += generateRtpmap(m) + "\r\n";
} catch (ParseException e) {
Logger.println("Failed to add rtpmap for telephone event " + telephoneEventPayload);
}
}
String transmitMap = "";
if (transmitMediaInfo != null) {
transmitMap = generateRtpmap(transmitMediaInfo) + "\r\n";
}
String sdp =
"v=0\r\n"
+ "o=" + name + " 1 1 IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "s=SIP Call\r\n"
+ "c=IN IP4 "
+ isa.getAddress().getHostAddress() + "\r\n"
+ "t=0 0 \r\n"
+ "m=audio " + isa.getPort()
+ " RTP/AVP " + payloads + "\r\n"
+ "a=rtpmap:13 CN/8000" + "\r\n"
+ "a=rtpmap:101 telephone-event/8000" + "\r\n"
+ "a=fmtp:101 0-16" + "\r\n"
+ generateRtpmap(mediaInfo) + "\r\n"
+ transmitMap
+ telephoneEvent;
if (transmitMediaInfo != null) {
sdp += "a=transmitPayload:"
+ transmitMediaInfo.getPayload() + "\r\n";
}
return sdp;
}
}