Allows use of distinct port ranges for audio and video streams. Distinct ranges can be defined through: net.java.sip.communicator.service.protocol.[MAX|MIN]_[AUDIO|VIDEO]_PORT_NUMBER. Use of a single range from 5000 6000 is still the default and is still configurable via the previously existing properties

cusax-fix
Emil Ivov 13 years ago
parent faf1f39e6e
commit 85511815b3

@ -745,7 +745,7 @@ protected IceMediaStream createIceStream(String media)
{
//the following call involves STUN processing so it may take a while
stream = getNetAddrMgr().createIceStream(
getNextMediaPortToTry(), media, iceAgent);
getPortTracker(media).getPort(), media, iceAgent);
}
catch (Exception ex)
{
@ -761,14 +761,9 @@ protected IceMediaStream createIceStream(String media)
//would simply include one more bind retry.
try
{
setNextMediaPortToTry(
1
+ stream
.getComponent(Component.RTCP)
.getLocalCandidates()
.get(0)
.getTransportAddress()
.getPort());
getPortTracker(media).setNextPort(
1 + stream.getComponent(Component.RTCP).getLocalCandidates()
.get(0).getTransportAddress() .getPort());
}
catch(Throwable t)
{

@ -847,15 +847,18 @@ else if(mediaType == MediaType.VIDEO)
* name.
*
* @param media the name of the stream we'd like to create.
*
* @param rtcp if true allocate an RTCP port
* @param portTracker the port tracker that we should use to obtain ports
* for this stream.
*
* @return the newly created {@link IceMediaStream}
*
* @throws OperationFailedException if binding on the specified media stream
* fails for some reason.
*/
private IceMediaStream createIceStream(String media, boolean rtcp)
private IceMediaStream createIceStream(String media,
boolean rtcp,
PortTracker portTracker)
throws OperationFailedException
{
IceMediaStream stream;
@ -864,15 +867,15 @@ private IceMediaStream createIceStream(String media, boolean rtcp)
{
//the following call involves STUN processing so it may take a while
stream = iceAgent.createMediaStream(media);
int rtpPort = getNextMediaPortToTry();
int rtpPort = portTracker.getPort();
//rtp
iceAgent.createComponent(stream, Transport.UDP, rtpPort, rtpPort,
rtpPort + 100);
if(rtcp)
iceAgent.createComponent(stream, Transport.UDP,
rtpPort + 1, rtpPort + 1, rtpPort + 101);
iceAgent.createComponent(stream, Transport.UDP, rtpPort,
portTracker.getMinPort(), portTracker.getMaxPort());
}
catch (Exception ex)
{
@ -888,14 +891,10 @@ private IceMediaStream createIceStream(String media, boolean rtcp)
//would simply include one more bind retry.
try
{
setNextMediaPortToTry(
1
+ stream
.getComponent(rtcp ? Component.RTCP : Component.RTP)
.getLocalCandidates()
.get(0)
.getTransportAddress()
.getPort());
portTracker.setNextPort(1 + stream
.getComponent(rtcp ? Component.RTCP : Component.RTP)
.getLocalCandidates().get(0).getTransportAddress()
.getPort());
}
catch(Throwable t)
{
@ -950,15 +949,17 @@ else if(ext.getNamespace().equals(
if(audio)
{
IceMediaStream stream = createIceStream("rtp", video);
IceMediaStream stream = createIceStream(
"rtp", video, getPortTracker(MediaType.AUDIO));
candidates.addAll(GTalkPacketFactory.createCandidates("rtp",
stream));
candidates.addAll(GTalkPacketFactory.createCandidates(
"rtp", stream));
}
if(video)
{
IceMediaStream stream = createIceStream("video_rtp", true);
IceMediaStream stream = createIceStream(
"video_rtp", true, getPortTracker(MediaType.VIDEO));
candidates.addAll(
GTalkPacketFactory.createCandidates("video_rtp", stream));
}

@ -29,6 +29,13 @@
public interface OperationSetBasicTelephony<T extends ProtocolProviderService>
extends OperationSet
{
/**
* The name of the property that contains the maximum port number that we'd
* like our RTP managers to bind upon.
*/
public static final String MAX_MEDIA_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MAX_MEDIA_PORT_NUMBER";
/**
* The name of the property that contains the minimum port number that we'd
* like our RTP managers to bind upon.
@ -36,12 +43,52 @@ public interface OperationSetBasicTelephony<T extends ProtocolProviderService>
public static final String MIN_MEDIA_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MIN_MEDIA_PORT_NUMBER";
/**
* The name of the property that contains the minimum port number that we'd
* like our Video RTP managers to bind upon.
*/
public static final String MIN_VIDEO_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MIN_VIDEO_PORT_NUMBER";
/**
* The name of the property that contains the maximum port number that we'd
* like our RTP managers to bind upon.
* like our Video RTP managers to bind upon.
*/
public static final String MAX_MEDIA_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MAX_MEDIA_PORT_NUMBER";
public static final String MAX_VIDEO_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MAX_VIDEO_PORT_NUMBER";
/**
* The name of the property that contains the minimum port number that we'd
* like our Audio RTP managers to bind upon.
*/
public static final String MIN_AUDIO_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MIN_AUDIO_PORT_NUMBER";
/**
* The name of the property that contains the maximum port number that we'd
* like our Audio RTP managers to bind upon.
*/
public static final String MAX_AUDIO_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MAX_AUDIO_PORT_NUMBER";
/**
* The name of the property that contains the minimum port number that we'd
* like our Data Channel (e.g. Pseudo TCP) managers to bind upon.
*/
public static final String MIN_DATA_CHANNEL_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MIN_DATA_CHANNEL_PORT_NUMBER";
/**
* The name of the property that contains the maximum port number that we'd
* like our Data Channel RTP managers to bind upon.
*/
public static final String MAX_DATA_CHANNEL_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.protocol.MAX_DATA_CHANNEL_PORT_NUMBER";
/**
* Reason code used to hangup peer, indicates normal hangup.

@ -37,29 +37,39 @@ public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
= Logger.getLogger(TransportManager.class);
/**
* The minimum port number that we'd like our RTP sockets to bind upon.
* The port tracker that we should use when binding generic media streams.
* <p>
* Initialized by {@link #initializePortNumbers()}.
* </p>
*/
private static int minMediaPort = -1;
private static PortTracker defaultPortTracker = new PortTracker(5000, 6000);
/**
* The maximum port number that we'd like our RTP sockets to bind upon.
* The port tracker that we should use when binding video media streams.
* <p>
* Initialized by {@link #initializePortNumbers()}.
* Potentially initialized by {@link #initializePortNumbers()} if the
* necessary properties are set.
* </p>
*/
private static int maxMediaPort = -1;
private static PortTracker videoPortTracker = null;
/**
* The port that we should try to bind our next media stream's RTP socket
* to.
* The port tracker that we should use when binding data channels.
* <p>
* Initialized by {@link #initializePortNumbers()}.
* Potentially initialized by {@link #initializePortNumbers()} if the
* necessary properties are set
* </p>
*/
private static PortTracker dataChannelPortTracker = null;
/**
* The port tracker that we should use when binding data media streams.
* <p>
* Potentially initialized by {@link #initializePortNumbers()} if the
* necessary properties are set
* </p>
*/
private static int nextMediaPortToTry = -1;
private static PortTracker audioPortTracker = null;
/**
* RTP audio DSCP configuration property name.
@ -106,7 +116,7 @@ public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
*/
protected TransportManager(U callPeer)
{
this.callPeer = callPeer;
this.callPeer = callPeer;
}
/**
@ -229,12 +239,15 @@ protected StreamConnector createStreamConnector(MediaType mediaType)
//make sure our port numbers reflect the configuration service settings
initializePortNumbers();
PortTracker portTracker = getPortTracker(mediaType);
//create the RTP socket.
DatagramSocket rtpSocket = null;
try
{
rtpSocket = nam.createDatagramSocket( localHostForPeer,
nextMediaPortToTry, minMediaPort, maxMediaPort);
rtpSocket = nam.createDatagramSocket(
localHostForPeer, portTracker.getPort(),
portTracker.getMinPort(), portTracker.getMaxPort());
}
catch (Exception exc)
{
@ -244,14 +257,16 @@ protected StreamConnector createStreamConnector(MediaType mediaType)
}
//make sure that next time we don't try to bind on occupied ports
nextMediaPortToTry = rtpSocket.getLocalPort() + 1;
//also, refuse validation in case someone set the tracker range to 1
portTracker.setNextPort( rtpSocket.getLocalPort() + 1, false);
//create the RTCP socket, preferably on the port following our RTP one.
DatagramSocket rtcpSocket = null;
try
{
rtcpSocket = nam.createDatagramSocket(localHostForPeer,
nextMediaPortToTry, minMediaPort, maxMediaPort);
rtcpSocket = nam.createDatagramSocket(
localHostForPeer, portTracker.getPort(),
portTracker.getMinPort(), portTracker.getMaxPort());
}
catch (Exception exc)
{
@ -262,76 +277,68 @@ protected StreamConnector createStreamConnector(MediaType mediaType)
}
//make sure that next time we don't try to bind on occupied ports
nextMediaPortToTry = rtcpSocket.getLocalPort() + 1;
if (nextMediaPortToTry > maxMediaPort - 1)// take RTCP into account.
nextMediaPortToTry = minMediaPort;
portTracker.setNextPort( rtcpSocket.getLocalPort() + 1);
return new DefaultStreamConnector(rtpSocket, rtcpSocket);
}
/**
* (Re)Sets the <tt>minPortNumber</tt> and <tt>maxPortNumber</tt> to their
* defaults or to the values specified in the <tt>ConfigurationService</tt>.
* (Re)Sets the all the port allocators to reflect current values specified
* in the <tt>ConfigurationService</tt>. Calling this method may very well
* result in creating new port allocators or destroying exising ones.
*/
protected static void initializePortNumbers()
{
//first reset to default values
minMediaPort = 5000;
maxMediaPort = 6000;
//then set to anything the user might have specified.
//try the default tracker first
ConfigurationService configuration
= ProtocolMediaActivator.getConfigurationService();
String minPortNumberStr
= configuration.getString(
OperationSetBasicTelephony
.MIN_MEDIA_PORT_NUMBER_PROPERTY_NAME);
String minPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MIN_MEDIA_PORT_NUMBER_PROPERTY_NAME);
if (minPortNumberStr != null)
{
try
{
minMediaPort = Integer.parseInt(minPortNumberStr);
}
catch (NumberFormatException ex)
{
logger.warn(minPortNumberStr
+ " is not a valid min port number value. "
+ "using min port " + minMediaPort);
}
}
String maxPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MAX_MEDIA_PORT_NUMBER_PROPERTY_NAME);
String maxPortNumberStr
= configuration.getString(
OperationSetBasicTelephony
.MAX_MEDIA_PORT_NUMBER_PROPERTY_NAME);
//try to send the specified range. If there's no specified range in
//configuration, we'll just leave the tracker as it is: [5000 to 6000]
defaultPortTracker.tryRange(minPortNumberStr, maxPortNumberStr);
if (maxPortNumberStr != null)
{
try
{
maxMediaPort = Integer.parseInt(maxPortNumberStr);
}
catch (NumberFormatException ex)
{
logger.warn(maxPortNumberStr
+ " is not a valid max port number value. "
+"using max port " + maxMediaPort,
ex);
}
}
/*
* Make sure that nextMediaPortToTry is within the range of minMediaPort
* and maxMediaPort as
* NetworkAddressManagerServiceImpl#createDatagramSocket(InetAddress,
* int, int, int) does.
*/
if ((minMediaPort <= maxMediaPort)
&& ((nextMediaPortToTry < minMediaPort)
|| (nextMediaPortToTry > maxMediaPort)))
nextMediaPortToTry = minMediaPort;
//try the VIDEO tracker
minPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MIN_VIDEO_PORT_NUMBER_PROPERTY_NAME);
maxPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MAX_VIDEO_PORT_NUMBER_PROPERTY_NAME);
//try to send the specified range. If there's no specified range in
//configuration, we'll just leave this tracker to null
videoPortTracker
= PortTracker.createTracker(minPortNumberStr, maxPortNumberStr);
//try the AUDIO tracker
minPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MIN_AUDIO_PORT_NUMBER_PROPERTY_NAME);
maxPortNumberStr = configuration.getString(
OperationSetBasicTelephony.MAX_AUDIO_PORT_NUMBER_PROPERTY_NAME);
//try to send the specified range. If there's no specified range in
//configuration, we'll just leave this tracker to null
audioPortTracker
= PortTracker.createTracker(minPortNumberStr, maxPortNumberStr);
//try the DATA CHANNEL tracker
minPortNumberStr = configuration.getString(OperationSetBasicTelephony
.MIN_DATA_CHANNEL_PORT_NUMBER_PROPERTY_NAME);
maxPortNumberStr = configuration.getString(OperationSetBasicTelephony
.MAX_DATA_CHANNEL_PORT_NUMBER_PROPERTY_NAME);
//try to send the specified range. If there's no specified range in
//configuration, we'll just leave this tracker to null
dataChannelPortTracker
= PortTracker.createTracker(minPortNumberStr, maxPortNumberStr);
}
/**
@ -554,29 +561,54 @@ public U getCallPeer()
}
/**
* Gets the port that we should try to bind our next media stream's RTP
* socket to.
* Returns the port tracker that we are supposed to use when binding ports
* for the specified {@link MediaType}.
*
* @param mediaType the media type that we want to obtain a locator for.
*
* @return the port that we should try to bind our next media stream's RTP
* socket to
* @return the port tracker that we are supposed to use when binding ports
* for the specified {@link MediaType}.
*/
protected static int getNextMediaPortToTry()
protected static PortTracker getPortTracker(MediaType mediaType)
{
if (nextMediaPortToTry == -1)
initializePortNumbers();
return nextMediaPortToTry;
if(MediaType.AUDIO == mediaType && audioPortTracker != null)
return audioPortTracker;
if(MediaType.VIDEO == mediaType && videoPortTracker != null)
return videoPortTracker;
return defaultPortTracker;
}
/**
* Sets the port that we should try to bind our next media stream's RTP
* socket to
* Returns the port tracker that we are supposed to use when binding ports
* for the {@link MediaType} indicated by the string param. If we do not
* recognize the string as a valid media type, we simply return the default
* port tracker.
*
* @param mediaTypeStr the name of the media type that we want to obtain a
* locator for.
*
* @param nextMediaPortToTry the port that we should try to bind our next
* media stream's RTP socket to
* @return the port tracker that we are supposed to use when binding ports
* for the {@link MediaType} with the specified name or the default tracker
* in case the name doesn't ring a bell.
*/
protected static void setNextMediaPortToTry(int nextMediaPortToTry)
protected static PortTracker getPortTracker(String mediaTypeStr)
{
TransportManager.nextMediaPortToTry = nextMediaPortToTry;
try
{
MediaType mediaType = MediaType.parseString(mediaTypeStr);
return getPortTracker(mediaType);
}
catch (Exception exc)
{
logger.info(
"Returning default port tracker for unrecognized media type: "
+ mediaTypeStr);
return defaultPortTracker;
}
}
/**

@ -0,0 +1,222 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.util;
/**
* The <tt>PortTracker</tt> class allows for a controlled selection of bind
* ports. This is typically useful in cases where we would like to set bounds
* for the ports that we are going to use for a particular socket. For example,
* at the time of writing of this class, this policy allows Jitsi to bind RTP
* sockets on ports that are always between 5000 and 6000 (default values). It
* is also used to allow for different port ranges for Audio and Video streams.
*
* @author Emil Ivov
*/
public class PortTracker
{
/**
* The local logger.
*/
private static final Logger logger = Logger.getLogger(NetworkUtils.class);
/**
* The minimum port number that this allocator would be allocate to return.
*/
private int minPort = NetworkUtils.MIN_PORT_NUMBER;
/**
* The maximum port number that this allocator would be allocate to return.
*/
private int maxPort = NetworkUtils.MAX_PORT_NUMBER;
/**
* The next port that we will return if asked.
*/
private int port = -1;
/**
* Initializes a port tracker with the specified port range.
*
* @param minPort the minimum port that we would like to bind on
* @param maxPort the maximum port that we would like to bind on
*/
public PortTracker(int minPort, int maxPort)
{
setRange(minPort, maxPort);
}
/**
* Returns the next port that the using class is supposed to try allocating.
*
* @return the next port that the using class is supposed to try allocating.
*/
public int getPort()
{
return port;
}
/**
* (Re)Sets the range that this tracker returns values in. The method would
* also update the value of the next port to allocate in case it is
* currently outside the specified range. The method also allows configuring
* this allocator in a way that it would always return the same port. This
* would happen when <tt>newMinPort</tt> is equal to <tt>newMaxPort</tt>
* which would make both equal to the only possible value.
*
* @param newMinPort the minimum port that we would like to bind on
* @param newMaxPort the maximum port that we would like to bind on
*
* @throws IllegalArgumentException if the arguments do not correspond to
* valid port numbers, or in case newMaxPort < newMinPort
*/
public void setRange(int newMinPort, int newMaxPort)
throws IllegalArgumentException
{
//validate
if( !NetworkUtils.isValidPortNumber(newMinPort)
|| !NetworkUtils.isValidPortNumber(newMaxPort)
|| newMaxPort < newMinPort)
{
throw new IllegalArgumentException(
"[" + newMinPort + " to "
+ newMaxPort + "] is not a valid port range.");
}
//reset bounds
minPort = newMinPort;
maxPort = newMaxPort;
/*
* Make sure that nextPort is within the specified range. Preserve value
* if already valid.
*/
if (port < minPort || port > maxPort)
{
port = minPort;
}
}
/**
* Attempts to set the range specified by the min and max port string
* params. If the attempt fails, for reasons such as invalid porameters,
* this method will simply return without an exception and without an impact
* on the state of this class.
*
* @param newMinPortString the minimum port that we would like to bind on
* @param newMaxPortString the maximum port that we would like to bind on
*/
public void tryRange(String newMinPortString, String newMaxPortString)
{
try
{
int newMinPort = Integer.parseInt(newMinPortString);
int newMaxPort = Integer.parseInt(newMaxPortString);
setRange(newMinPort, newMaxPort);
}
catch(Exception exc)//Null, NumberFormat, IllegalArgument
{
logger.info("Ignoring invalid port range ["+ newMinPortString
+ " to " + newMaxPortString +"]");
logger.debug("Cause: ", exc);
}
}
/**
* Sets the next port to specified value unless allowing the caller to
* request validation and force the port into the range that this tracker
* operates in.
*
* @param nextPort the next port we'd like this tracker to return.
* @param validate determines whether this tracker should bring the new
* value into its current range.
*/
public void setNextPort(int nextPort, boolean validate)
{
/*
* Make sure that nextPort is within the specified range unless
*/
if ((nextPort < minPort || nextPort > maxPort ) && validate)
{
port = minPort;
}
else
{
this.port = nextPort;
}
}
/**
* Sets the next port to specified value unless it is outside the range that
* this tracker operates in, in which case it sets it to the minimal
* possible.
*
* @param nextPort the next port we'd like this tracker to return.
*/
public void setNextPort(int nextPort)
{
setNextPort(nextPort, true);
}
/**
* Returns the lowest/minimum port that this tracker would use.
*
* @return the minimum port number allowed by this tracker.
*/
public int getMinPort()
{
return minPort;
}
/**
* Returns the highest/maximum port that this tracker would use.
*
* @return the maximum port number allowed by this tracker.
*/
public int getMaxPort()
{
return maxPort;
}
/**
* Attempts to create a port tracker that uses the min and max values
* indicated by the <tt>newMinPortString</tt> and <tt>newMinPortString</tt>
* strings and returns it if successful. The method fails silently
* (returning <tt>null</tt>) otherwise.
*
* @param newMinPortString the {@link String} containing the minimum port
* number that this tracker should allow.
* @param newMaxPortString the {@link String} containing the minimum port
* number that this tracker should allow.
*
* @return the newly created port tracker or <tt>null</tt> if the string
* params do not contain valid port numbers.
*/
public static PortTracker createTracker(String newMinPortString,
String newMaxPortString)
{
try
{
int minPort = Integer.parseInt(newMinPortString);
int maxPort = Integer.parseInt(newMaxPortString);
return new PortTracker(minPort, maxPort);
}
catch(Exception exc)//Null, NumberFormat, IllegalArgument
{
logger.info("Ignoring invalid port range ["+ newMinPortString
+ " to " + newMaxPortString +"]");
logger.debug("Cause: ", exc);
return null;
}
}
}
Loading…
Cancel
Save