mirror of https://github.com/sipwise/jitsi.git
parent
f4df77d8e2
commit
2437a95d8e
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.jabber;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Keeps a list of all calls currently active and maintained by this protocol
|
||||
* provider. Offers methods for finding a call by its ID, peer session
|
||||
* and others.
|
||||
*
|
||||
* @author Emil Ivov
|
||||
* @author Symphorien Wanko
|
||||
*/
|
||||
public class ActiveCallsRepository
|
||||
extends CallChangeAdapter
|
||||
{
|
||||
/**
|
||||
* logger of this class
|
||||
*/
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(ActiveCallsRepository.class);
|
||||
|
||||
/**
|
||||
* The operation set that created us. Instance is mainly used for firing
|
||||
* events when necessary.
|
||||
*/
|
||||
private OperationSetBasicTelephonyJabberImpl parentOperationSet = null;
|
||||
|
||||
/**
|
||||
* A table mapping call ids against call instances.
|
||||
*/
|
||||
private Hashtable<String, CallJabberImpl> activeCalls
|
||||
= new Hashtable<String, CallJabberImpl>();
|
||||
|
||||
/**
|
||||
* It's where we store all active calls
|
||||
* @param opSet the <tt>OperationSetBasicTelphony</tt> instance which has
|
||||
* been used to create calls in this repository
|
||||
*/
|
||||
public ActiveCallsRepository(OperationSetBasicTelephonyJabberImpl opSet)
|
||||
{
|
||||
this.parentOperationSet = opSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified call to the list of calls tracked by this repository.
|
||||
* @param call CallJabberImpl
|
||||
*/
|
||||
public void addCall(CallJabberImpl call)
|
||||
{
|
||||
activeCalls.put(call.getCallID(), call);
|
||||
call.addCallChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* If <tt>evt</tt> indicates that the call has been ended we remove it from
|
||||
* the repository.
|
||||
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
|
||||
* calls and its old and new state.
|
||||
*/
|
||||
public void callStateChanged(CallChangeEvent evt)
|
||||
{
|
||||
if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
|
||||
&& ((CallState)evt.getNewValue()).equals(CallState.CALL_ENDED))
|
||||
{
|
||||
CallJabberImpl sourceCall = this.activeCalls
|
||||
.remove(evt.getSourceCall().getCallID());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace( "Removing call " + sourceCall + " from the list of "
|
||||
+ "active calls because it entered an ENDED state");
|
||||
|
||||
this.parentOperationSet.fireCallEvent(
|
||||
CallEvent.CALL_ENDED, sourceCall);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over all currently active (non-ended) calls.
|
||||
*
|
||||
* @return an iterator over all currently active (non-ended) calls.
|
||||
*/
|
||||
public Iterator<CallJabberImpl> getActiveCalls()
|
||||
{
|
||||
return new LinkedList<CallJabberImpl>(activeCalls.values()).iterator();
|
||||
}
|
||||
}
|
||||
@ -1,329 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.sip;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.sip.*;
|
||||
import javax.sip.header.*;
|
||||
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Keeps a list of all calls currently active and maintained by this protocol
|
||||
* povider. Offers methods for finding a call by its ID, peer dialog
|
||||
* and others.
|
||||
*
|
||||
* @author Emil Ivov
|
||||
*/
|
||||
public class ActiveCallsRepository
|
||||
extends CallChangeAdapter
|
||||
{
|
||||
/**
|
||||
* Our class logger.
|
||||
*/
|
||||
private static final Logger logger
|
||||
= Logger.getLogger(ActiveCallsRepository.class);
|
||||
|
||||
/**
|
||||
* The operation set that created us. Instance is mainly used for firing
|
||||
* events when necessary.
|
||||
*/
|
||||
private final OperationSetBasicTelephonySipImpl parentOperationSet;
|
||||
|
||||
/**
|
||||
* A table mapping call ids against call instances.
|
||||
*/
|
||||
private Hashtable<String, CallSipImpl> activeCalls
|
||||
= new Hashtable<String, CallSipImpl>();
|
||||
|
||||
/**
|
||||
* Creates a new instance of this repository.
|
||||
*
|
||||
* @param opSet a reference to the
|
||||
* <tt>OperationSetBasicTelephonySipImpl</tt> that craeted us.
|
||||
*/
|
||||
public ActiveCallsRepository(OperationSetBasicTelephonySipImpl opSet)
|
||||
{
|
||||
this.parentOperationSet = opSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified call to the list of calls tracked by this repository.
|
||||
* @param call CallSipImpl
|
||||
*/
|
||||
public void addCall(CallSipImpl call)
|
||||
{
|
||||
activeCalls.put(call.getCallID(), call);
|
||||
call.addCallChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* If <tt>evt</tt> indicates that the call has been ended we remove it from
|
||||
* the repository.
|
||||
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
|
||||
* calls and its old and new state.
|
||||
*/
|
||||
public void callStateChanged(CallChangeEvent evt)
|
||||
{
|
||||
if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
|
||||
&& evt.getNewValue().equals(CallState.CALL_ENDED))
|
||||
{
|
||||
CallSipImpl sourceCall =
|
||||
this.activeCalls.remove(evt.getSourceCall().getCallID());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Removing call " + sourceCall + " from the list of "
|
||||
+ "active calls because it entered an ENDED state");
|
||||
|
||||
this.parentOperationSet.fireCallEvent(
|
||||
CallEvent.CALL_ENDED, sourceCall);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over all currently active (non-ended) calls.
|
||||
*
|
||||
* @return an iterator over all currently active (non-ended) calls.
|
||||
*/
|
||||
public Iterator<CallSipImpl> getActiveCalls()
|
||||
{
|
||||
return new LinkedList<CallSipImpl>(activeCalls.values()).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call that contains the specified dialog (i.e. it is
|
||||
* established between us and one of the other call peers).
|
||||
* <p>
|
||||
* @param dialog the jain sip <tt>Dialog</tt> whose containing call we're
|
||||
* looking for.
|
||||
* @return the <tt>CallSipImpl</tt> containing <tt>dialog</tt> or null
|
||||
* if no call contains the specified dialog.
|
||||
*/
|
||||
public CallSipImpl findCall(Dialog dialog)
|
||||
{
|
||||
Iterator<CallSipImpl> activeCalls = getActiveCalls();
|
||||
|
||||
if(dialog == null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Cannot find a peer with a null dialog. "
|
||||
+ "Returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Looking for peer with dialog: " + dialog
|
||||
+ " among " + this.activeCalls.size() + " calls");
|
||||
}
|
||||
|
||||
|
||||
while(activeCalls.hasNext())
|
||||
{
|
||||
CallSipImpl call = activeCalls.next();
|
||||
if(call.contains(dialog))
|
||||
return call;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call peer whose associated jain sip dialog matches
|
||||
* <tt>dialog</tt>.
|
||||
*
|
||||
* @param dialog the jain sip dialog whose corresponding peer we're
|
||||
* looking for.
|
||||
* @return the call peer whose jain sip dialog is the same as the
|
||||
* specified or null if no such call peer was found.
|
||||
*/
|
||||
public CallPeerSipImpl findCallPeer(Dialog dialog)
|
||||
{
|
||||
if(dialog == null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Cannot find a peer with a null dialog. "
|
||||
+ "Returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Looking for peer with dialog: " + dialog
|
||||
+ " among " + this.activeCalls.size() + " calls");
|
||||
}
|
||||
|
||||
for (Iterator<CallSipImpl> activeCalls = getActiveCalls();
|
||||
activeCalls.hasNext();)
|
||||
{
|
||||
CallSipImpl call = activeCalls.next();
|
||||
CallPeerSipImpl callPeer
|
||||
= call.findCallPeer(dialog);
|
||||
if(callPeer != null)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Returning peer " + callPeer);
|
||||
return callPeer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <tt>CallPeerSipImpl</tt> instance with a <tt>Dialog</tt>
|
||||
* matching CallID, local and remote tags.
|
||||
*
|
||||
* @param callID the <tt>Call-ID</tt> of the dialog we are looking for.
|
||||
* @param localTag the local tag of the dialog we are looking for.
|
||||
* @param remoteTag the remote tag of the dialog we are looking for.
|
||||
*
|
||||
* @return the <tt>CallPeerSipImpl</tt> matching specified dialog ID or
|
||||
* <tt>null</tt> if no such peer is known to this repository.
|
||||
*/
|
||||
public CallPeerSipImpl findCallPeer(String callID,
|
||||
String localTag, String remoteTag)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Looking for call peer with callID " + callID
|
||||
+ ", localTag " + localTag + ", and remoteTag " + remoteTag
|
||||
+ " among " + this.activeCalls.size() + " calls.");
|
||||
}
|
||||
|
||||
for (Iterator<CallSipImpl> activeCalls = getActiveCalls();
|
||||
activeCalls.hasNext();)
|
||||
{
|
||||
CallSipImpl call = activeCalls.next();
|
||||
|
||||
if (!callID.equals(call.getCallID()))
|
||||
continue;
|
||||
|
||||
for (Iterator<? extends CallPeer> callPeerIter
|
||||
= call.getCallPeers();
|
||||
callPeerIter.hasNext();)
|
||||
{
|
||||
CallPeerSipImpl callPeer =
|
||||
(CallPeerSipImpl) callPeerIter.next();
|
||||
Dialog dialog = callPeer.getDialog();
|
||||
|
||||
if (dialog != null)
|
||||
{
|
||||
String dialogLocalTag = dialog.getLocalTag();
|
||||
|
||||
if (((localTag == null) || "0".equals(localTag)) ?
|
||||
((dialogLocalTag == null) || "0".equals(dialogLocalTag)) :
|
||||
localTag.equals(dialogLocalTag))
|
||||
{
|
||||
String dialogRemoteTag = dialog.getRemoteTag();
|
||||
|
||||
if (((remoteTag == null) || "0".equals(remoteTag))
|
||||
? ((dialogRemoteTag == null)
|
||||
|| "0".equals(dialogRemoteTag))
|
||||
: remoteTag.equals(dialogRemoteTag))
|
||||
{
|
||||
return callPeer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <tt>CallPeerSipImpl</tt> whose INVITE transaction has the
|
||||
* specified <tt>branchID</tt> and whose corresponding INVITE request
|
||||
* contains the specified <tt>callID</tt>.
|
||||
*
|
||||
* @param callID the <tt>Call-ID</tt> of the dialog we are looking for.
|
||||
* @param branchID a <tt>String</tt> corresponding to the branch id of the
|
||||
* latest INVITE transaction that was associated with the peer we are
|
||||
* looking for.
|
||||
*
|
||||
* @return the <tt>CallPeerSipImpl</tt> matching specified call and branch
|
||||
* id-s or <tt>null</tt> if no such peer is known to this repository.
|
||||
*/
|
||||
public CallPeerSipImpl findCallPeer(String branchID, String callID)
|
||||
{
|
||||
Iterator<CallSipImpl> activeCallsIter = getActiveCalls();
|
||||
|
||||
while (activeCallsIter.hasNext())
|
||||
{
|
||||
CallSipImpl activeCall = activeCallsIter.next();
|
||||
Iterator<CallPeerSipImpl> callPeersIter = activeCall.getCallPeers();
|
||||
|
||||
while (callPeersIter.hasNext())
|
||||
{
|
||||
CallPeerSipImpl cp = callPeersIter.next();
|
||||
Dialog cpDialog = cp.getDialog();
|
||||
Transaction cpTran = cp.getLatestInviteTransaction();
|
||||
|
||||
if( cpDialog == null
|
||||
|| cpDialog.getCallId() == null
|
||||
|| cpTran == null)
|
||||
continue;
|
||||
|
||||
if ( cp.getLatestInviteTransaction() != null
|
||||
&& cpDialog.getCallId().getCallId().equals(callID)
|
||||
&& branchID.equals(cpTran.getBranchId()))
|
||||
{
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <tt>CallPeerSipImpl</tt> whose INVITE transaction has the
|
||||
* specified <tt>branchID</tt> and whose corresponding INVITE request
|
||||
* contains the specified <tt>callID</tt>.
|
||||
*
|
||||
* @param cidHeader the <tt>Call-ID</tt> of the dialog we are looking for.
|
||||
* @param branchID a <tt>String</tt> corresponding to the branch id of the
|
||||
* latest INVITE transaction that was associated with the peer we are
|
||||
* looking for.
|
||||
*
|
||||
* @return the <tt>CallPeerSipImpl</tt> matching specified call and branch
|
||||
* id-s or <tt>null</tt> if no such peer is known to this repository.
|
||||
*/
|
||||
public CallPeerSipImpl findCallPeer(String branchID, Header cidHeader)
|
||||
{
|
||||
if(cidHeader == null || ! (cidHeader instanceof CallIdHeader))
|
||||
return null;
|
||||
|
||||
return findCallPeer(branchID, (((CallIdHeader)cidHeader).getCallId()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the <tt>CallSipImpl</tt> instance with a <tt>Dialog</tt>
|
||||
* matching the specified <tt>Call-ID</tt>, local and remote tags.
|
||||
*
|
||||
* @param callID the <tt>Call-ID</tt> of the dialog we are looking for.
|
||||
* @param localTag the local tag of the dialog we are looking for.
|
||||
* @param remoteTag the remote tag of the dialog we are looking for.
|
||||
*
|
||||
* @return the <tt>CallSipImpl</tt> responsible for handling the
|
||||
* <tt>Dialog</tt> with the matching ID or <tt>null</tt> if no such call was
|
||||
* found.
|
||||
*/
|
||||
public CallSipImpl findCall(String callID,
|
||||
String localTag,
|
||||
String remoteTag)
|
||||
{
|
||||
CallPeerSipImpl peer = findCallPeer(callID, localTag, remoteTag);
|
||||
|
||||
return (peer == null)? null : peer.getCall();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.sip.sdp;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.neomedia.*;
|
||||
import net.java.sip.communicator.service.neomedia.format.*;
|
||||
|
||||
/**
|
||||
* The RTP Audio/Video Profile [RFC 3551] specifies a number of static payload
|
||||
* types for use with RTP and reserves the 96-127 field for use with dynamic
|
||||
* payload types.
|
||||
* <p>
|
||||
* Mappings of dynamic payload types are handled with SDP. They are created for
|
||||
* a particular session and remain the same for its entire lifetime. They may
|
||||
* however change in following sessions.
|
||||
* </p>
|
||||
* <p>
|
||||
* We use this class as a utility for easily creating and tracking dynamic
|
||||
* payload mappings for the lifetime of a particular session. One instance of
|
||||
* this registry is supposed to be mapped to one media session. They should
|
||||
* have pretty much the same life cycle.
|
||||
* </p>
|
||||
* @author Emil Ivov
|
||||
*/
|
||||
public class DynamicPayloadTypeRegistry
|
||||
{
|
||||
/**
|
||||
* A field that we use to track dynamic payload numbers that we allocate.
|
||||
*/
|
||||
private byte nextDynamicPayloadType = MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE;
|
||||
|
||||
/**
|
||||
* A table mapping <tt>MediaFormat</tt> instances to the dynamic payload
|
||||
* type number they have obtained for the lifetime of this registry.
|
||||
*/
|
||||
private Map<MediaFormat, Byte> payloadTypeMappings
|
||||
= new Hashtable<MediaFormat, Byte>();
|
||||
|
||||
/**
|
||||
* Returns the dynamic payload type that has been allocated for
|
||||
* <tt>format</tt>. A mapping for the specified <tt>format</tt> would be
|
||||
* created even if it did not previously exist. The method is meant for use
|
||||
* primarily during generation of SDP descriptions.
|
||||
*
|
||||
* @param format the <tt>MediaFormat</tt> instance that we'd like to obtain
|
||||
* a payload type number for..
|
||||
*
|
||||
* @return the (possibly newly allocated) payload type number corresponding
|
||||
* to the specified <tt>format</tt> instance for the lifetime of the media
|
||||
* session.
|
||||
*
|
||||
* @throws IllegalStateException if we have already registered more dynamic
|
||||
* formats than allowed for by RTP.
|
||||
*/
|
||||
public byte obtainPayloadTypeNumber(MediaFormat format)
|
||||
throws IllegalStateException
|
||||
{
|
||||
MediaType mediaType = format.getMediaType();
|
||||
String encoding = format.getEncoding();
|
||||
double clockRate = format.getClockRate();
|
||||
int channels
|
||||
= MediaType.AUDIO.equals(mediaType)
|
||||
? ((AudioMediaFormat) format).getChannels()
|
||||
: MediaFormatFactory.CHANNELS_NOT_SPECIFIED;
|
||||
Byte payloadType = null;
|
||||
|
||||
for (Map.Entry<MediaFormat, Byte> payloadTypeMapping
|
||||
: payloadTypeMappings.entrySet())
|
||||
{
|
||||
if (AbstractMediaStream.matches(
|
||||
payloadTypeMapping.getKey(),
|
||||
mediaType, encoding, clockRate, channels))
|
||||
{
|
||||
payloadType = payloadTypeMapping.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//hey, we already had this one, let's return it ;)
|
||||
if (payloadType == null)
|
||||
{
|
||||
payloadType = nextPayloadTypeNumber();
|
||||
payloadTypeMappings.put(format, payloadType);
|
||||
}
|
||||
|
||||
return payloadType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified <tt>format</tt> to <tt>payloadType</tt> mapping to
|
||||
* the list of mappings known to this registry. The method is meant for
|
||||
* use primarily when handling incoming media descriptions, methods
|
||||
* generating local SDP should use the <tt>obtainPayloadTypeNumber</tt>
|
||||
* instead.
|
||||
*
|
||||
* @param payloadType the payload type number that we'd like to allocated
|
||||
* to <tt>format</tt>.
|
||||
* @param format the <tt>MediaFormat</tt> that we'd like to create a
|
||||
* dynamic mapping for.
|
||||
*
|
||||
* @throws IllegalArgumentException in case <tt>payloadType</tt> has
|
||||
* already been assigned to another format.
|
||||
*/
|
||||
public void addMapping(MediaFormat format, byte payloadType)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
MediaFormat alreadyMappedFmt = findFormat(payloadType);
|
||||
|
||||
if(alreadyMappedFmt != null)
|
||||
{
|
||||
throw new IllegalArgumentException(payloadType
|
||||
+ " has already been allocated to " + alreadyMappedFmt);
|
||||
}
|
||||
|
||||
if( payloadType < MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE)
|
||||
{
|
||||
throw new IllegalArgumentException(payloadType
|
||||
+ " is not a valid dynamic payload type number."
|
||||
+ " (must be between " + MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE
|
||||
+ " and " + MediaFormat.MAX_DYNAMIC_PAYLOAD_TYPE);
|
||||
}
|
||||
|
||||
payloadTypeMappings.put(format, Byte.valueOf(payloadType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the <tt>MediaFormat</tt> with the specified
|
||||
* mapping or <tt>null</tt> if the number specified by <tt>payloadType</tt>
|
||||
* has not been allocated yet.
|
||||
*
|
||||
* @param payloadType the number of the payload type that we are trying to
|
||||
* get a format for.
|
||||
*
|
||||
* @return the <tt>MediaFormat</tt> that has been mapped to
|
||||
* <tt>payloadType</tt> in this registry or <tt>null</tt> if it hasn't been
|
||||
* allocated yet.
|
||||
*/
|
||||
public MediaFormat findFormat(byte payloadType)
|
||||
{
|
||||
for (Map.Entry<MediaFormat, Byte> entry
|
||||
: payloadTypeMappings.entrySet())
|
||||
{
|
||||
byte fmtPayloadType = entry.getValue();
|
||||
|
||||
if(fmtPayloadType == payloadType)
|
||||
return entry.getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-allocated dynamic payload type number.
|
||||
*
|
||||
* @return the first non-allocated dynamic payload type number.
|
||||
*
|
||||
* @throws IllegalStateException if we have already registered more dynamic
|
||||
* formats than allowed for by RTP.
|
||||
*/
|
||||
private byte nextPayloadTypeNumber()
|
||||
throws IllegalStateException
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (nextDynamicPayloadType < 0)
|
||||
{
|
||||
throw new IllegalStateException(
|
||||
"Impossible to allocate more than the already 32 mapped "
|
||||
+"dynamic payload type numbers");
|
||||
}
|
||||
|
||||
byte payloadType = nextDynamicPayloadType++;
|
||||
|
||||
if(findFormat(payloadType) == null)
|
||||
return payloadType;
|
||||
|
||||
//if we get here then that means that the number we obtained by
|
||||
//incrementing our PT counter was already occupied (probably by an
|
||||
//incoming SDP). continue bravely and get the next free one.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of all mappings currently registered in this registry.
|
||||
*
|
||||
* @return a copy of all mappings currently registered in this registry.
|
||||
*/
|
||||
public Map<MediaFormat, Byte> getMappings()
|
||||
{
|
||||
return new Hashtable<MediaFormat, Byte>(payloadTypeMappings);
|
||||
}
|
||||
}
|
||||
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.protocol.sip.sdp;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.java.sip.communicator.service.neomedia.*;
|
||||
|
||||
/**
|
||||
* RFC [RFC 5285] defines a mechanism for attaching multiple extensions to
|
||||
* RTP packets. Part of this mechanism consists in negotiating their
|
||||
* identifiers using <tt>extmap</tt> attributes pretty much the same way one
|
||||
* would negotiate payload types with <tt>rtpmap</tt> attributes.
|
||||
* <p>
|
||||
* Mappings of extension IDs are handled with SDP. They are created for
|
||||
* a particular session and remain the same for its entire lifetime. They may
|
||||
* however change in following sessions.
|
||||
* </p>
|
||||
* <p>
|
||||
* We use this class as a utility for easily creating and tracking extension
|
||||
* mappings for the lifetime of a particular session. One instance of this
|
||||
* registry is supposed to be mapped to one media session and they should
|
||||
* have the same life cycle.
|
||||
* </p>
|
||||
* @author Emil Ivov
|
||||
*/
|
||||
public class DynamicRTPExtensionsRegistry
|
||||
{
|
||||
/**
|
||||
* The minimum integer that is allowed for use when mapping extensions using
|
||||
* the one-byte header.
|
||||
*/
|
||||
public static final int MIN_HEADER_ID = 1;
|
||||
|
||||
/**
|
||||
* The maximum integer that is allowed for use when mapping extensions using
|
||||
* the one-byte header. Note that 15 is reserved for future use by 5285
|
||||
*/
|
||||
public static final int MAX_ONE_BYTE_HEADER_ID = 14;
|
||||
|
||||
/**
|
||||
* The maximum integer that is allowed for use when mapping extensions using
|
||||
* the two-byte header.
|
||||
*/
|
||||
public static final int MAX_TWO_BYTE_HEADER_ID = 255;
|
||||
|
||||
/**
|
||||
* A field that we use to track mapping IDs.
|
||||
*/
|
||||
private byte nextExtensionMapping = MIN_HEADER_ID;
|
||||
|
||||
/**
|
||||
* A table mapping <tt>RTPExtension</tt> instances to the dynamically
|
||||
* allocated ID they have obtained for the lifetime of this registry.
|
||||
*/
|
||||
private Map<RTPExtension, Byte> extMap
|
||||
= new Hashtable<RTPExtension, Byte>();
|
||||
|
||||
/**
|
||||
* Returns the ID that has been allocated for <tt>extension</tt>. A mapping
|
||||
* for the specified <tt>extension</tt> would be created even if it did not
|
||||
* previously exist. The method is meant for use primarily during generation
|
||||
* of SDP descriptions.
|
||||
*
|
||||
* @param extension the <tt>RTPExtension</tt> instance that we'd like to
|
||||
* obtain a dynamic ID for.
|
||||
*
|
||||
* @return the (possibly newly allocated) ID corresponding to the specified
|
||||
* <tt>extension</tt> and valid for the lifetime of the media session.
|
||||
*
|
||||
* @throws IllegalStateException if we have already registered more RTP
|
||||
* extensions than allowed for by RTP.
|
||||
*/
|
||||
public byte obtainExtensionMapping(RTPExtension extension)
|
||||
throws IllegalStateException
|
||||
{
|
||||
Byte extID = extMap.get(extension);
|
||||
|
||||
//hey, we already had this one, let's return it ;)
|
||||
if( extID == null)
|
||||
{
|
||||
extID = nextExtensionID();
|
||||
extMap.put(extension, extID);
|
||||
}
|
||||
|
||||
return extID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID that has been allocated for <tt>extension</tt> or
|
||||
* <tt>-1</tt> if no extension exists.
|
||||
*
|
||||
* @param extension the <tt>RTPExtension</tt> instance whose ID we'd like to
|
||||
* find.
|
||||
*
|
||||
* @return the ID corresponding to the specified <tt>extension</tt> or
|
||||
* <tt>-1</tt> if <tt>extension</tt> is not registered with this registry.
|
||||
*/
|
||||
public byte getExtensionMapping(RTPExtension extension)
|
||||
{
|
||||
Byte extID = extMap.get(extension);
|
||||
|
||||
//hey, we already had this one, let's return it ;)
|
||||
if( extID == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return extID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified <tt>extension</tt> to <tt>extID</tt> mapping to
|
||||
* the list of mappings known to this registry. The method is meant for
|
||||
* use primarily when handling incoming media descriptions, methods
|
||||
* generating local SDP should use the <tt>obtainExtensionMapping</tt>
|
||||
* instead.
|
||||
*
|
||||
* @param extID the extension ID that we'd like to allocated to
|
||||
* <tt>extension</tt>.
|
||||
* @param extension the <tt>RTPExtension</tt> that we'd like to create a
|
||||
* dynamic mapping for.
|
||||
*
|
||||
* @throws IllegalArgumentException in case <tt>extID</tt> has already been
|
||||
* assigned to another <tt>RTPExtension</tt>.
|
||||
*/
|
||||
public void addMapping(RTPExtension extension, byte extID)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
RTPExtension alreadyMappedExt = findExtension(extID);
|
||||
|
||||
if(alreadyMappedExt != null)
|
||||
{
|
||||
throw new IllegalArgumentException(extID
|
||||
+ " has already been allocated to " + alreadyMappedExt);
|
||||
}
|
||||
|
||||
if( extID < MIN_HEADER_ID)
|
||||
{
|
||||
throw new IllegalArgumentException(extID
|
||||
+ " is not a valid RTP extensino header ID."
|
||||
+ " (must be between " + MIN_HEADER_ID
|
||||
+ " and " + MAX_TWO_BYTE_HEADER_ID);
|
||||
}
|
||||
|
||||
extMap.put(extension, Byte.valueOf(extID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the <tt>RTPExtension</tt> with the specified
|
||||
* mapping or <tt>null</tt> if the number specified by <tt>extID</tt>
|
||||
* has not been allocated yet.
|
||||
*
|
||||
* @param extID the ID whose <tt>RTPExtension</tt> we are trying to
|
||||
* discover.
|
||||
*
|
||||
* @return the <tt>RTPExtension</tt> that has been mapped to
|
||||
* <tt>extID</tt> in this registry or <tt>null</tt> if it hasn't been
|
||||
* allocated yet.
|
||||
*/
|
||||
public RTPExtension findExtension(byte extID)
|
||||
{
|
||||
for (Map.Entry<RTPExtension, Byte> entry
|
||||
: extMap.entrySet())
|
||||
{
|
||||
byte currentExtensionID = entry.getValue();
|
||||
|
||||
if(currentExtensionID == extID)
|
||||
return entry.getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-allocated dynamic extension ID number.
|
||||
*
|
||||
* @return the first non-allocated dynamic extension ID number..
|
||||
*
|
||||
* @throws IllegalStateException if we have already registered more RTP
|
||||
* extension headers than allowed for by RTP.
|
||||
*/
|
||||
private byte nextExtensionID()
|
||||
throws IllegalStateException
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (nextExtensionMapping < 0)
|
||||
{
|
||||
throw new IllegalStateException(
|
||||
"Impossible to map more than the 255 already mapped "
|
||||
+" RTP extensions");
|
||||
}
|
||||
|
||||
byte extID = nextExtensionMapping++;
|
||||
|
||||
if(findExtension(extID) == null)
|
||||
return extID;
|
||||
|
||||
//if we get here then that means that the number we obtained by
|
||||
//incrementing our ID counter was already occupied (probably by an
|
||||
//incoming SDP). continue bravely and get the next free one.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of all mappings currently registered in this registry.
|
||||
*
|
||||
* @return a copy of all mappings currently registered in this registry.
|
||||
*/
|
||||
public Map<RTPExtension, Byte> getMappings()
|
||||
{
|
||||
return new Hashtable<RTPExtension, Byte>(extMap);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue