aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-10-19 09:51:19 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-10-19 09:51:19 +0000
commit0bfadb48948260b88bc252695b3c84e5ff6af9f4 (patch)
tree34d0f2cd003453def98e308b660a162d51a79dea
parenta496b37ac740dddebf9d4f2efd809b7a252c7def (diff)
downloadjitsi-0bfadb48948260b88bc252695b3c84e5ff6af9f4.zip
jitsi-0bfadb48948260b88bc252695b3c84e5ff6af9f4.tar.gz
jitsi-0bfadb48948260b88bc252695b3c84e5ff6af9f4.tar.bz2
Commits work in progress on enabling ICE support in Jingle calls:
- Sends transport-info without content child extensions other than transport. - Does not send candidates in session-accept if they have already been sent in transport-info. - Does not wait for connectivity establishment to conclude before sending session-accept because the ICE controlling agent determines when the connectivity establishment concludes.
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java91
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java292
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java202
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java59
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/TransportInfoSender.java30
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java106
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/AbstractPacketExtension.java82
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/ExtmapPacketExtension.java1
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java23
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JinglePacketFactory.java10
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/PayloadTypePacketExtension.java6
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/RawUdpTransportPacketExtension.java38
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java19
13 files changed, 653 insertions, 306 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
index 01b3def..84949a6 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
@@ -8,6 +8,7 @@ package net.java.sip.communicator.impl.protocol.jabber;
import java.util.*;
+import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smackx.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
@@ -68,7 +69,7 @@ public class CallPeerJabberImpl
super(owningCall);
this.peerJID = peerAddress;
- super.setMediaHandler( new CallPeerMediaHandlerJabberImpl(this) );
+ setMediaHandler( new CallPeerMediaHandlerJabberImpl(this) );
}
/**
@@ -375,15 +376,17 @@ public class CallPeerJabberImpl
{
this.sessionInitIQ = sessionInitIQ;
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
List<ContentPacketExtension> answer = sessionInitIQ.getContentList();
try
{
- getMediaHandler().processAnswer(answer);
+ mediaHandler.processAnswer(answer);
}
catch(Exception exc)
{
- logger.info("Failed to process a session-accept", exc);
+ if (logger.isInfoEnabled())
+ logger.info("Failed to process a session-accept", exc);
//send an error response;
JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
@@ -400,7 +403,7 @@ public class CallPeerJabberImpl
//stop
setState(CallPeerState.CONNECTED);
- getMediaHandler().start();
+ mediaHandler.start();
}
/**
@@ -533,23 +536,27 @@ public class CallPeerJabberImpl
*/
private void sendRemoveVideoContent()
{
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
ContentPacketExtension content = new ContentPacketExtension();
- ContentPacketExtension remoteContent = getMediaHandler().
- getRemoteContent(MediaType.VIDEO.toString());
- List<ContentPacketExtension> contents =
- new ArrayList<ContentPacketExtension>();
+ ContentPacketExtension remoteContent
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
content.setName(remoteContent.getName());
content.setCreator(remoteContent.getCreator());
content.setSenders(remoteContent.getSenders());
- contents.add(content);
- JingleIQ contentIQ = JinglePacketFactory
- .createContentRemove(getProtocolProvider().getOurJID(),
- this.peerJID, getJingleSID(), contents);
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentRemove(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getJingleSID(),
+ Arrays.asList(content));
- getProtocolProvider().getConnection().sendPacket(contentIQ);
- getMediaHandler().removeRemoteContent(remoteContent.getName());
+ protocolProvider.getConnection().sendPacket(contentIQ);
+ mediaHandler.removeContent(remoteContent.getName());
}
/**
@@ -643,14 +650,16 @@ public class CallPeerJabberImpl
*/
public void processContentAdd(JingleIQ content)
{
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
List<ContentPacketExtension> contents = content.getContentList();
- JingleIQ contentIQ = null;
Iterable<ContentPacketExtension> answerContents;
+ JingleIQ contentIQ;
try
{
- getMediaHandler().processOffer(contents);
- answerContents = getMediaHandler().generateSessionAccept();
+ mediaHandler.processOffer(contents);
+ answerContents = mediaHandler.generateSessionAccept();
+ contentIQ = null;
}
catch(Exception e)
{
@@ -677,7 +686,7 @@ public class CallPeerJabberImpl
}
getProtocolProvider().getConnection().sendPacket(contentIQ);
- getMediaHandler().start();
+ mediaHandler.start();
}
/**
@@ -696,7 +705,7 @@ public class CallPeerJabberImpl
}
catch(Exception exc)
{
- logger.warn("Exception occurred", exc);
+ logger.warn("Failed to process a content-accept", exc);
//send an error response;
JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
@@ -752,8 +761,21 @@ public class CallPeerJabberImpl
{
List<ContentPacketExtension> contents = content.getContentList();
- for(ContentPacketExtension ext : contents)
- getMediaHandler().removeRemoteContent(ext.getName());
+ if (!contents.isEmpty())
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ for(ContentPacketExtension c : contents)
+ mediaHandler.removeContent(c.getName());
+
+ /*
+ * TODO XEP-0166: Jingle says: If the content-remove results in zero
+ * content definitions for the session, the entity that receives the
+ * content-remove SHOULD send a session-terminate action to the
+ * other party (since a session with no content definitions is
+ * void).
+ */
+ }
}
/**
@@ -797,4 +819,31 @@ public class CallPeerJabberImpl
logger.error("Failed to process an incoming transport-info", ofe);
}
}
+
+ /**
+ * Sends local candidate addresses from the local peer to the remote peer
+ * using the <tt>transport-info</tt> {@link JingleIQ}.
+ *
+ * @param contents the local candidate addresses to be sent from the local
+ * peer to the remote peer using the <tt>transport-info</tt>
+ * {@link JingleIQ}
+ */
+ void sendTransportInfo(Iterable<ContentPacketExtension> contents)
+ {
+ JingleIQ transportInfo = new JingleIQ();
+
+ for (ContentPacketExtension content : contents)
+ transportInfo.addContent(content);
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+
+ transportInfo.setAction(JingleAction.TRANSPORT_INFO);
+ transportInfo.setFrom(protocolProvider.getOurJID());
+ transportInfo.setSID(getJingleSID());
+ transportInfo.setTo(getAddress());
+ transportInfo.setType(IQ.Type.SET);
+
+ protocolProvider.getConnection().sendPacket(transportInfo);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
index 05664ba..d5a2ff3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -6,9 +6,9 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
+import java.lang.reflect.*;
import java.util.*;
-import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smackx.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
@@ -376,14 +376,32 @@ public class CallPeerMediaHandlerJabberImpl
}
/*
- * In order to minimize post-pickup delay, establish the connectivity
- * prior to ringing.
+ * In order to minimize post-pickup delay, start establishing the
+ * connectivity prior to ringing.
*/
- List<ContentPacketExtension> candidates
- = harvestCandidates(offer, answer);
-
- sendTransportInfo(candidates);
- establishConnectivity(offer);
+ harvestCandidates(
+ offer,
+ answer,
+ new TransportInfoSender()
+ {
+ public void sendTransportInfo(
+ Iterable<ContentPacketExtension> contents)
+ {
+ getPeer().sendTransportInfo(contents);
+ }
+ });
+ /*
+ * While it may sound like we can completely eliminate the post-pickup
+ * delay by waiting for the connectivity establishment to finish, it may
+ * not be possible in all cases. We are the Jingle session responder so,
+ * in the case of the ICE UDP transport, we are not the controlling ICE
+ * Agent and we cannot be sure when the controlling ICE Agent will
+ * perform the nomination. It could, for example, choose to wait for our
+ * session-accept to perform the nomination which will deadlock us if we
+ * have chosen to wait for the connectivity establishment to finish
+ * before we begin ringing and send session-accept.
+ */
+ getTransportManager().startConnectivityEstablishment(offer);
}
/**
@@ -401,7 +419,7 @@ public class CallPeerMediaHandlerJabberImpl
{
TransportManagerJabberImpl transportManager = getTransportManager();
Iterable<ContentPacketExtension> sessAccept
- = transportManager.wrapupConnectivityEstablishment();
+ = transportManager.wrapupCandidateHarvest();
CallPeerJabberImpl peer = getPeer();
//user answered an incoming call so we go through whatever content
@@ -412,7 +430,7 @@ public class CallPeerMediaHandlerJabberImpl
= JingleUtils.getRtpDescription(ourContent);
MediaType type = MediaType.parseString(description.getMedia());
- //
+ // stream connector
StreamConnector connector
= transportManager.getStreamConnector(type);
@@ -420,24 +438,22 @@ public class CallPeerMediaHandlerJabberImpl
MediaDevice dev = getDefaultDevice(type);
// stream target
- ContentPacketExtension theirContent
- = this.remoteContentMap.get(ourContent.getName());
- MediaStreamTarget target
- = JingleUtils.extractDefaultTarget(theirContent);
- RtpDescriptionPacketExtension theirDescription
- = JingleUtils.getRtpDescription(theirContent);
+ MediaStreamTarget target = transportManager.getStreamTarget(type);
//stream direction
- MediaDirection direction = JingleUtils.getDirection(
- ourContent, !peer.isInitiator());
+ MediaDirection direction
+ = JingleUtils.getDirection(ourContent, !peer.isInitiator());
//let's now see what was the format we announced as first and
//configure the stream with it.
+ ContentPacketExtension theirContent
+ = this.remoteContentMap.get(ourContent.getName());
+ RtpDescriptionPacketExtension theirDescription
+ = JingleUtils.getRtpDescription(theirContent);
MediaFormat format = null;
- List<PayloadTypePacketExtension> payloadTypes =
- theirDescription.getPayloadTypes();
- for(PayloadTypePacketExtension payload : payloadTypes)
+ for(PayloadTypePacketExtension payload
+ : theirDescription.getPayloadTypes())
{
format
= JingleUtils.payloadTypeToMediaFormat(
@@ -583,7 +599,7 @@ public class CallPeerMediaHandlerJabberImpl
}
//now add the transport elements
- return harvestCandidates(null, mediaDescs);
+ return harvestCandidates(null, mediaDescs, null);
}
/**
@@ -678,7 +694,7 @@ public class CallPeerMediaHandlerJabberImpl
}
//now add the transport elements
- return harvestCandidates(null, mediaDescs);
+ return harvestCandidates(null, mediaDescs, null);
}
/**
@@ -771,52 +787,42 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
- * Remove a media content and stop the corresponding stream.
+ * Removes a media content with a specific name from the session represented
+ * by this <tt>CallPeerMediaHandlerJabberImpl</tt> and closes its associated
+ * media stream.
*
- * @param name of the Jingle content
+ * @param name the name of the media content to be removed from this session
*/
- public void removeLocalContent(String name)
+ public void removeContent(String name)
{
- ContentPacketExtension content = localContentMap.remove(name);
-
- if(content == null)
- return;
-
- RtpDescriptionPacketExtension description
- = JingleUtils.getRtpDescription(content);
-
- String media = description.getMedia();
-
- if(media != null )
- {
- MediaStream stream = getStream(MediaType.parseString(media));
- stream.stop();
- stream = null;
- }
+ removeContent(localContentMap, name);
+ removeContent(remoteContentMap, name);
+ getTransportManager().removeContent(name);
}
/**
- * Remove a media content and stop the corresponding stream.
+ * Removes a media content with a specific name from the session represented
+ * by this <tt>CallPeerMediaHandlerJabberImpl</tt> and closes its associated
+ * media stream.
*
- * @param name of the Jingle content
+ * @param contentMap the <tt>Map</tt> in which the specified <tt>name</tt>
+ * has an association with the media content to be removed
+ * @param name the name of the media content to be removed from this session
*/
- public void removeRemoteContent(String name)
+ private void removeContent(
+ Map<String, ContentPacketExtension> contentMap,
+ String name)
{
- ContentPacketExtension content = remoteContentMap.remove(name);
-
- if(content == null)
- return;
-
- RtpDescriptionPacketExtension description
- = JingleUtils.getRtpDescription(content);
-
- String media = description.getMedia();
+ ContentPacketExtension content = contentMap.remove(name);
- if(media != null )
+ if (content != null)
{
- MediaStream stream = getStream(MediaType.parseString(media));
- stream.stop();
- stream = null;
+ RtpDescriptionPacketExtension description
+ = JingleUtils.getRtpDescription(content);
+ String media = description.getMedia();
+
+ if (media != null)
+ closeStream(MediaType.parseString(media));
}
}
@@ -841,16 +847,18 @@ public class CallPeerMediaHandlerJabberImpl
{
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
-
MediaType mediaType
= MediaType.parseString( description.getMedia() );
//stream target
- MediaStreamTarget target
- = JingleUtils.extractDefaultTarget(content);
+ TransportManagerJabberImpl transportManager = getTransportManager();
+ MediaStreamTarget target = transportManager.getStreamTarget(mediaType);
+
+ if (target == null)
+ target = JingleUtils.extractDefaultTarget(content);
// no target port - try next media description
- if(target.getDataAddress().getPort() == 0)
+ if((target == null) || (target.getDataAddress().getPort() == 0))
{
closeStream(mediaType);
return;
@@ -885,7 +893,7 @@ public class CallPeerMediaHandlerJabberImpl
}
StreamConnector connector
- = getTransportManager().getStreamConnector(mediaType);
+ = transportManager.getStreamConnector(mediaType);
//determine the direction that we need to announce.
MediaDirection remoteDirection
@@ -929,18 +937,19 @@ public class CallPeerMediaHandlerJabberImpl
throws OperationFailedException,
IllegalArgumentException
{
- for (ContentPacketExtension content : answer)
- {
- remoteContentMap.put(content.getName(), content);
-
- processContent(content);
- }
/*
* The answer given in session-accept may contain transport-related
* information compatible with that carried in transport-info.
*/
processTransportInfo(answer);
+
+ for (ContentPacketExtension content : answer)
+ {
+ remoteContentMap.put(content.getName(), content);
+
+ processContent(content);
+ }
}
/**
@@ -1149,6 +1158,7 @@ public class CallPeerMediaHandlerJabberImpl
* peer to the remote peer. If <tt>remote</tt> is <tt>null</tt>,
* <tt>local</tt> represents an offer from the local peer to be sent to the
* remote peer
+ * @param transportInfoSender
* @return the media descriptions of the local peer after the local
* candidate addresses have been gathered as returned by
* {@link TransportManagerJabberImpl#wrapupCandidateHarvest()}
@@ -1157,15 +1167,30 @@ public class CallPeerMediaHandlerJabberImpl
*/
private List<ContentPacketExtension> harvestCandidates(
List<ContentPacketExtension> remote,
- List<ContentPacketExtension> local)
+ List<ContentPacketExtension> local,
+ TransportInfoSender transportInfoSender)
throws OperationFailedException
{
TransportManagerJabberImpl transportManager = getTransportManager();
if (remote == null)
+ {
+ /*
+ * We'll be harvesting candidates in order to make an offer so it
+ * doesn't make sense to send them in transport-info.
+ */
+ if (transportInfoSender != null)
+ throw new IllegalArgumentException("transportInfoSender");
+
transportManager.startCandidateHarvest(local);
+ }
else
- transportManager.startCandidateHarvest(remote, local);
+ {
+ transportManager.startCandidateHarvest(
+ remote,
+ local,
+ transportInfoSender);
+ }
/*
* XXX Ideally, we wouldn't wrap up that quickly. We need to revisit
@@ -1175,104 +1200,73 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
- * Establishes connectivity between the candidate addresses of the local and
- * the remote peers.
+ * Processes the transport-related information provided by the remote
+ * <tt>peer</tt> in a specific set of <tt>ContentPacketExtension</tt>s.
*
- * @param remote the media descriptions received from the remote peer which
- * contain the remote candidate addresses to establish connectivity with
- * @throws OperationFailedException if anything goes wrong while starting or
- * wrapping up the establishment of connectivity between the candidate
- * addresses of the local and the remote peers
+ * @param contents the <tt>ContentPacketExtenion</tt>s provided by the
+ * remote <tt>peer</tt> and containing the transport-related information to
+ * be processed
+ * @throws OperationFailedException if anything goes wrong while processing
+ * the transport-related information provided by the remote <tt>peer</tt> in
+ * the specified set of <tt>ContentPacketExtension</tt>s
*/
- private void establishConnectivity(Iterable<ContentPacketExtension> remote)
+ public void processTransportInfo(Iterable<ContentPacketExtension> contents)
throws OperationFailedException
{
- TransportManagerJabberImpl transportManager = getTransportManager();
-
- transportManager.startConnectivityEstablishment(remote);
- transportManager.wrapupConnectivityEstablishment();
- for (MediaType mediaType : MediaType.values())
- {
- MediaStream stream = getStream(mediaType);
-
- if (stream != null)
- {
- stream.setConnector(
- transportManager.getStreamConnector(mediaType));
- stream.setTarget(transportManager.getStreamTarget(mediaType));
- }
- }
+ if (getTransportManager().startConnectivityEstablishment(contents))
+ wrapupConnectivityEstablishment();
}
/**
- * Sends local candidate addresses from the local peer to the remote peer
- * using the <tt>transport-info</tt> {@link JingleIQ}. Since only ICE UDP
- * among the supported Jingle transports is documented to utilize
- * <tt>transport-info</tt>, the <tt>sendTransportInfo</tt> method does not
- * send <tt>ContentPacketExtension</tt>s with transport other than ICE UDP
- * and silently ignores them (i.e. no <tt>transport-info</tt>
- * <tt>JingleIQ</tt> is sent in the case of all <tt>candidates</tt> not
- * being from the ICE UDP transport).
+ * Waits for the associated <tt>TransportManagerJabberImpl</tt> to conclude
+ * any started connectivity establishment and then starts this
+ * <tt>CallPeerMediaHandler</tt>.
*
- * @param candidates the local candidate addresses to be sent from the local
- * peer to the remote peer using the <tt>transport-info</tt>
- * {@link JingleIQ}
+ * @throws IllegalStateException if no offer or answer has been provided or
+ * generated earlier
*/
- private void sendTransportInfo(Iterable<ContentPacketExtension> candidates)
+ @Override
+ public void start()
+ throws IllegalStateException
{
- JingleIQ transportInfo = new JingleIQ();
- boolean sendTransportInfo = false;
-
- for (ContentPacketExtension content : candidates)
+ try
{
- IceUdpTransportPacketExtension transport
- = content.getFirstChildOfType(
- IceUdpTransportPacketExtension.class);
-
- if ((transport != null)
- && IceUdpTransportPacketExtension.NAMESPACE.equals(
- transport.getNamespace()))
- {
- List<CandidatePacketExtension> candidateList
- = transport.getCandidateList();
-
- if ((candidateList != null) && !candidateList.isEmpty())
- {
- transportInfo.addContent(content);
- sendTransportInfo = true;
- }
- }
+ wrapupConnectivityEstablishment();
}
- if (sendTransportInfo)
+ catch (OperationFailedException ofe)
{
- CallPeerJabberImpl peer = getPeer();
- ProtocolProviderServiceJabberImpl protocolProvider
- = peer.getProtocolProvider();
-
- transportInfo.setAction(JingleAction.TRANSPORT_INFO);
- transportInfo.setFrom(protocolProvider.getOurJID());
- transportInfo.setSID(peer.getJingleSID());
- transportInfo.setTo(peer.getAddress());
- transportInfo.setType(IQ.Type.SET);
-
- protocolProvider.getConnection().sendPacket(transportInfo);
+ throw new UndeclaredThrowableException(ofe);
}
+
+ super.start();
}
/**
- * Processes the transport-related information provided by the remote
- * <tt>peer</tt> in a specific set of <tt>ContentPacketExtension</tt>s.
+ * Notifies the associated <tt>TransportManagerJabberImpl</tt> that it
+ * should conclude any connectivity establishment, waits for it to actually
+ * do so and sets the <tt>connector</tt>s and <tt>target</tt>s of the
+ * <tt>MediaStream</tt>s managed by this <tt>CallPeerMediaHandler</tt>.
*
- * @param contents the <tt>ContentPacketExtenion</tt>s provided by the
- * remote <tt>peer</tt> and containing the transport-related information to
- * be processed
- * @throws OperationFailedException if anything goes wrong while processing
- * the transport-related information provided by the remote <tt>peer</tt> in
- * the specified set of <tt>ContentPacketExtension</tt>s
+ * @throws OperationFailedException if anything goes wrong while setting the
+ * <tt>connector</tt>s and/or <tt>target</tt>s of the <tt>MediaStream</tt>s
+ * managed by this <tt>CallPeerMediaHandler</tt>
*/
- public void processTransportInfo(Iterable<ContentPacketExtension> contents)
+ private void wrapupConnectivityEstablishment()
throws OperationFailedException
{
- establishConnectivity(contents);
+ TransportManagerJabberImpl transportManager = getTransportManager();
+
+ transportManager.wrapupConnectivityEstablishment();
+ for (MediaType mediaType : MediaType.values())
+ {
+ MediaStream stream = getStream(mediaType);
+
+ if (stream != null)
+ {
+ stream.setConnector(
+ transportManager.getStreamConnector(mediaType));
+ stream.setTarget(transportManager.getStreamTarget(mediaType));
+ }
+ }
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
index 56fc5ed..928aa24 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
@@ -364,13 +364,30 @@ public class IceUdpTransportManager
* transports our peer is using.
* @param ourAnswer the content descriptions that we should be adding our
* transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
*
* @throws OperationFailedException if we fail to allocate a port number.
+ * @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
+ * TransportInfoSender)
*/
- public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer)
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> theirOffer,
+ List<ContentPacketExtension> ourAnswer,
+ TransportInfoSender transportInfoSender)
throws OperationFailedException
{
+ Collection<ContentPacketExtension> transportInfoContents
+ = (transportInfoSender ==null)
+ ? null
+ : new LinkedList<ContentPacketExtension>();
+
for(ContentPacketExtension theirContent : theirOffer)
{
//now add our transport to our answer
@@ -386,10 +403,47 @@ public class IceUdpTransportManager
RtpDescriptionPacketExtension.class);
IceMediaStream stream = createIceStream(rtpDesc.getMedia());
- //we now generate the XMPP code containing the candidates.
- ourContent.addChildExtension(JingleUtils.createTransport(stream));
+ // Report the gathered candidate addresses.
+ if (transportInfoSender == null)
+ {
+ ourContent.addChildExtension(
+ JingleUtils.createTransport(stream));
+ }
+ else
+ {
+ /*
+ * The candidates will be sent in transport-info so the
+ * transport of session-accept just has to be present, not
+ * populated with candidates.
+ */
+ ourContent.addChildExtension(
+ new IceUdpTransportPacketExtension());
+
+ /*
+ * Create the content to be sent in a transport-info. The
+ * transport is the only extension to be sent in transport-info
+ * so the content has the same attributes as in our answer and
+ * none of its non-transport extensions.
+ */
+ ContentPacketExtension transportInfoContent
+ = new ContentPacketExtension();
+
+ for (String name : ourContent.getAttributeNames())
+ {
+ Object value = ourContent.getAttribute(name);
+
+ if (value != null)
+ transportInfoContent.setAttribute(name, value);
+ }
+ transportInfoContent.addChildExtension(
+ JingleUtils.createTransport(stream));
+ transportInfoContents.add(transportInfoContent);
+ }
}
+ if (transportInfoSender != null)
+ transportInfoSender.sendTransportInfo(transportInfoContents);
+
this.cpeList = ourAnswer;
}
@@ -499,17 +553,18 @@ public class IceUdpTransportManager
}
/**
- * Implements
- * {@link TransportManagerJabberImpl#startConnectivityEstablishment(Collection)}.
* Starts the connectivity establishment of the associated ICE
* <tt>Agent</tt>.
*
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represents the remote counterpart of the negotiation between the local
* and the remote peers
+ * @return <tt>true</tt> if connectivity establishment has been started in
+ * response to the call; otherwise, <tt>false</tt>
* @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
*/
- public void startConnectivityEstablishment(
+ @Override
+ public boolean startConnectivityEstablishment(
Iterable<ContentPacketExtension> remote)
{
/*
@@ -518,7 +573,7 @@ public class IceUdpTransportManager
* has been started.
*/
if (!IceProcessingState.WAITING.equals(iceAgent.getState()))
- return;
+ return false;
int generation = iceAgent.getGeneration();
boolean startConnectivityEstablishment = false;
@@ -543,9 +598,29 @@ public class IceUdpTransportManager
= transport.getChildExtensionsOfType(
CandidatePacketExtension.class);
+ /*
+ * If we cannot associate an IceMediaStream with the remote content,
+ * we will not have anything to add the remote candidates to.
+ */
RtpDescriptionPacketExtension description
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
+
+ if ((description == null) && (cpeList != null))
+ {
+ ContentPacketExtension localContent
+ = findContentByName(cpeList, content.getName());
+
+ if (localContent != null)
+ {
+ description
+ = localContent.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+ }
+ }
+ if (description == null)
+ continue;
+
IceMediaStream stream = iceAgent.getStream(description.getMedia());
for (CandidatePacketExtension candidate : candidates)
@@ -576,22 +651,46 @@ public class IceUdpTransportManager
}
}
if (startConnectivityEstablishment)
- iceAgent.startConnectivityEstablishment();
+ {
+ /*
+ * Once again because the ICE Agent does not support adding
+ * candidates after the connectivity establishment has been started
+ * and because multiple transport-info JingleIQs may be used to send
+ * the whole set of transport candidates from the remote peer to the
+ * local peer, do not really start the connectivity establishment
+ * until we have at least one remote candidate per ICE Component.
+ */
+ for (IceMediaStream stream : iceAgent.getStreams())
+ {
+ for (Component component : stream.getComponents())
+ {
+ if (component.getRemoteCandidateCount() < 1)
+ {
+ startConnectivityEstablishment = false;
+ break;
+ }
+ }
+ if (!startConnectivityEstablishment)
+ break;
+ }
+
+ if (startConnectivityEstablishment)
+ {
+ iceAgent.startConnectivityEstablishment();
+ return true;
+ }
+ }
+ return false;
}
/**
- * Implements
- * {@link TransportManagerJabberImpl#wrapupConnectivityEstablishment()}.
* Waits for the associated ICE <tt>Agent</tt> to finish any started
- * connectivity checks and returns the collection of
- * <tt>ContentPacketExtension</tt>s which represents the local counterpart
- * of the negotiation between the local and the remote peers.
+ * connectivity checks.
*
- * @return the collection of <tt>ContentPacketExtension</tt>s which
- * represents the local counterpart of the negotiation between the local and
- * the remote peer after the end of any started connectivity establishment
+ * @see TransportManagerJabberImpl#wrapupConnectivityEstablishment()
*/
- public Iterable<ContentPacketExtension> wrapupConnectivityEstablishment()
+ @Override
+ public void wrapupConnectivityEstablishment()
{
final Object iceProcessingStateSyncRoot = new Object();
PropertyChangeListener stateChangeListener
@@ -651,6 +750,71 @@ public class IceUdpTransportManager
*/
iceAgent.removeStateChangeListener(stateChangeListener);
- return cpeList;
+ /*
+ * Once we're done establishing connectivity, we shouldn't be sending
+ * any more candidates because we will not be able to perform
+ * connectivity checks for them. Besides, they must have been sent in
+ * transport-info already.
+ */
+ if (cpeList != null)
+ {
+ for (ContentPacketExtension content : cpeList)
+ {
+ IceUdpTransportPacketExtension transport
+ = content.getFirstChildOfType(
+ IceUdpTransportPacketExtension.class);
+
+ if (transport != null)
+ {
+ for (CandidatePacketExtension candidate
+ : transport.getCandidateList())
+ transport.removeCandidate(candidate);
+
+ Collection<?> childExtensions
+ = transport.getChildExtensions();
+
+ if ((childExtensions == null) || childExtensions.isEmpty())
+ {
+ transport.removeAttribute(
+ IceUdpTransportPacketExtension.UFRAG_ATTR_NAME);
+ transport.removeAttribute(
+ IceUdpTransportPacketExtension.PWD_ATTR_NAME);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a content with a specific name from the transport-related part of
+ * the session represented by this <tt>TransportManagerJabberImpl</tt> which
+ * may have been reported through previous calls to the
+ * <tt>startCandidateHarvest</tt> and
+ * <tt>startConnectivityEstablishment</tt> methods.
+ *
+ * @param name the name of the content to be removed from the
+ * transport-related part of the session represented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ * @see TransportManagerJabberImpl#removeContent(String)
+ */
+ public void removeContent(String name)
+ {
+ ContentPacketExtension content = removeContent(cpeList, name);
+
+ if (content != null)
+ {
+ RtpDescriptionPacketExtension rtpDescription
+ = content.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+
+ if (rtpDescription != null)
+ {
+ IceMediaStream stream
+ = iceAgent.getStream(rtpDescription.getMedia());
+
+ if (stream != null)
+ iceAgent.removeStream(stream);
+ }
+ }
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
index f177aaf..24f1b4c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
@@ -110,11 +110,23 @@ public class RawUdpTransportManager
* transports our peer is using.
* @param ourAnswer the content descriptions that we should be adding our
* transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
*
* @throws OperationFailedException if we fail to allocate a port number.
+ * @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
+ * TransportInfoSender)
*/
- public void startCandidateHarvest(List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer)
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> theirOffer,
+ List<ContentPacketExtension> ourAnswer,
+ TransportInfoSender transportInfoSender)
throws OperationFailedException
{
for(ContentPacketExtension content : theirOffer)
@@ -239,37 +251,22 @@ public class RawUdpTransportManager
}
/**
- * Implements
- * {@link TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)}.
- * Since this represents a raw UDP transport, performs no connectivity
- * checks and just remembers the remote counterpart of the negotiation
- * between the local and the remote peers in order to be able to report the
- * <tt>MediaStreamTarget</tt>s upon request.
+ * Removes a content with a specific name from the transport-related part of
+ * the session represented by this <tt>TransportManagerJabberImpl</tt> which
+ * may have been reported through previous calls to the
+ * <tt>startCandidateHarvest</tt> and
+ * <tt>startConnectivityEstablishment</tt> methods.
*
- * @param remote the collection of <tt>ContentPacketExtension</tt>s which
- * represent the remote counterpart of the negotiation between the local and
- * the remote peers
- * @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
- */
- public void startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote)
- {
- this.remote = remote;
- }
-
- /**
- * Implements
- * {@link TransportManagerJabberImpl#wrapupConnectivityEstablishment()}.
- * Since this represents a raw UDP transport i.e. no connectivity checks are
- * performed, just returns the local counterpart of the negotiation between
- * the local and the remote peers.
- *
- * @return the local counterpart of the negotiation between the local and
- * the remote peers
- * @see TransportManagerJabberImpl#wrapupConnectivityEstablishment()
+ * @param name the name of the content to be removed from the
+ * transport-related part of the session represented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ * @see TransportManagerJabberImpl#removeContent(String)
*/
- public Iterable<ContentPacketExtension> wrapupConnectivityEstablishment()
+ public void removeContent(String name)
{
- return local;
+ if (local != null)
+ removeContent(local, name);
+ if (remote != null)
+ removeContent(remote, name);
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/TransportInfoSender.java b/src/net/java/sip/communicator/impl/protocol/jabber/TransportInfoSender.java
new file mode 100644
index 0000000..e40cbc7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/TransportInfoSender.java
@@ -0,0 +1,30 @@
+/*
+ * 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 net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+
+/**
+ * Represents functionality which allows a <tt>TransportManagerJabberImpl</tt>
+ * implementation to send <tt>transport-info</tt> {@link JingleIQ}s for the
+ * purposes of expediting candidate negotiation.
+ *
+ * @author Lyubomir Marinov
+ */
+public interface TransportInfoSender
+{
+ /**
+ * Sends specific {@link ContentPacketExtension}s in a
+ * <tt>transport-info</tt> {@link JingleIQ} from the local peer to the
+ * remote peer.
+ *
+ * @param contents the <tt>ContentPacketExtension</tt>s to be sent in a
+ * <tt>transport-info</tt> <tt>JingleIQ</tt> from the local peer to the
+ * remote peer
+ */
+ public void sendTransportInfo(Iterable<ContentPacketExtension> contents);
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
index a872760..f680bbe 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
@@ -18,8 +18,8 @@ import net.java.sip.communicator.service.protocol.media.*;
* <tt>TransportManager</tt>s gather local candidates for incoming and outgoing
* calls. Their work starts by calling a start method which, using the remote
* peer's session description, would start the harvest. Calling a second wrapup
- * method would deliver the candidate harvest, possibly after blocking if
- * it has not yet completed.
+ * method would deliver the candidate harvest, possibly after blocking if it has
+ * not yet completed.
*
* @author Emil Ivov
* @author Lyubomir Marinov
@@ -136,12 +136,21 @@ public abstract class TransportManagerJabberImpl
* transports our peer is using.
* @param ourAnswer the content descriptions that we should be adding our
* transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
*
* @throws OperationFailedException if we fail to allocate a port number.
*/
public abstract void startCandidateHarvest(
- List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer)
+ List<ContentPacketExtension> theirOffer,
+ List<ContentPacketExtension> ourAnswer,
+ TransportInfoSender transportInfoSender)
throws OperationFailedException;
/**
@@ -180,9 +189,9 @@ public abstract class TransportManagerJabberImpl
* @return the {@link ContentPacketExtension} with the specified name or
* <tt>null</tt> if no such content element exists.
*/
- protected ContentPacketExtension findContentByName(
- List<ContentPacketExtension> cpExtList,
- String name)
+ protected static ContentPacketExtension findContentByName(
+ Iterable<ContentPacketExtension> cpExtList,
+ String name)
{
for(ContentPacketExtension cpExt : cpExtList)
{
@@ -201,18 +210,85 @@ public abstract class TransportManagerJabberImpl
* @param remote the collection of <tt>ContentPacketExtension</tt>s which
* represents the remote counterpart of the negotiation between the local
* and the remote peer
+ * @return <tt>true</tt> if connectivity establishment has been started in
+ * response to the call; otherwise, <tt>false</tt>.
+ * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
+ * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
+ * default implementation does not perform connectivity checks and always
+ * returns <tt>true</tt>.
*/
- public abstract void startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote);
+ public boolean startConnectivityEstablishment(
+ Iterable<ContentPacketExtension> remote)
+ {
+ return true;
+ }
/**
* Notifies this <tt>TransportManagerJabberImpl</tt> that it should conclude
- * any started connectivity establishment and return (at least) the
- * transport-related media descriptions of the local peer.
+ * any started connectivity establishment.
+ */
+ public void wrapupConnectivityEstablishment()
+ {
+ }
+
+ /**
+ * Removes a content with a specific name from the transport-related part of
+ * the session represented by this <tt>TransportManagerJabberImpl</tt> which
+ * may have been reported through previous calls to the
+ * <tt>startCandidateHarvest</tt> and
+ * <tt>startConnectivityEstablishment</tt> methods.
+ * <p>
+ * <b>Note</b>: Because <tt>TransportManager</tt> deals with
+ * <tt>MediaType</tt>s, not content names and
+ * <tt>TransportManagerJabberImpl</tt> does not implement translating from
+ * content name to <tt>MediaType</tt>, implementers are expected to call
+ * {@link TransportManager#closeStreamConnector(MediaType)}.
+ * </p>
+ *
+ * @param name the name of the content to be removed from the
+ * transport-related part of the session represented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ */
+ public abstract void removeContent(String name);
+
+ /**
+ * Removes a content with a specific name from a specific collection of
+ * contents and closes any associated <tt>StreamConnector</tt>.
*
- * @return the transport-related media descriptions of the local peer after
- * the end of the connectivity establishment
+ * @param contents the collection of contents to remove the content with the
+ * specified name from
+ * @param name the name of the content to remove
+ * @return the removed <tt>ContentPacketExtension</tt> if any; otherwise,
+ * <tt>null</tt>
*/
- public abstract Iterable<ContentPacketExtension>
- wrapupConnectivityEstablishment();
+ protected ContentPacketExtension removeContent(
+ Iterable<ContentPacketExtension> contents,
+ String name)
+ {
+ Iterator<ContentPacketExtension> contentIter = contents.iterator();
+
+ while (contentIter.hasNext())
+ {
+ ContentPacketExtension content = contentIter.next();
+
+ if (name.equals(content.getName()))
+ {
+ contentIter.remove();
+
+ // closeStreamConnector
+ RtpDescriptionPacketExtension rtpDescription
+ = content.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+
+ if (rtpDescription != null)
+ {
+ closeStreamConnector(
+ MediaType.parseString(rtpDescription.getMedia()));
+ }
+
+ return content;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/AbstractPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/AbstractPacketExtension.java
index 32271ff..4bb1521 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/AbstractPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/AbstractPacketExtension.java
@@ -20,6 +20,7 @@ import org.jivesoftware.smack.packet.*;
* handling instead.
*
* @author Emil Ivov
+ * @author Lyubomir Marinov
*/
public abstract class AbstractPacketExtension
implements PacketExtension
@@ -95,42 +96,61 @@ public abstract class AbstractPacketExtension
*/
public String toXML()
{
- StringBuilder bldr = new StringBuilder("<" + getElementName() + " ");
+ StringBuilder bldr = new StringBuilder();
+
+ bldr.append("<").append(getElementName()).append(" ");
if(getNamespace() != null)
- bldr.append("xmlns='" + getNamespace() + "'");
+ bldr.append("xmlns='").append(getNamespace()).append("'");
//add the rest of the attributes if any
for(Map.Entry<String, String> entry : attributes.entrySet())
{
- bldr.append(" " + entry.getKey() + "='" + entry.getValue() + "'");
+ bldr.append(" ")
+ .append(entry.getKey())
+ .append("='")
+ .append(entry.getValue())
+ .append("'");
}
//add child elements if any
List<? extends PacketExtension> childElements = getChildExtensions();
- synchronized(childElements)
+ String text = getText();
+
+ if (childElements == null)
{
- if( (childElements == null || childElements.size() == 0)
- && (getText() == null || getText().length() == 0))
+ if ((text == null) || (text.length() == 0))
{
bldr.append("/>");
return bldr.toString();
}
else
+ bldr.append('>');
+ }
+ else
+ {
+ synchronized(childElements)
{
- bldr.append(">");
- for(PacketExtension packExt : childElements)
+ if (childElements.isEmpty()
+ && ((text == null) || (text.length() == 0)))
+ {
+ bldr.append("/>");
+ return bldr.toString();
+ }
+ else
{
- bldr.append(packExt.toXML());
+ bldr.append(">");
+ for(PacketExtension packExt : childElements)
+ bldr.append(packExt.toXML());
}
}
}
//text content if any
- if(getText() != null && getText().trim().length() > 0)
- bldr.append(getText());
+ if((text != null) && (text.trim().length() > 0))
+ bldr.append(text);
- bldr.append("</"+getElementName()+">");
+ bldr.append("</").append(getElementName()).append(">");
return bldr.toString();
}
@@ -246,13 +266,28 @@ public abstract class AbstractPacketExtension
*/
public int getAttributeAsInt(String attribute)
{
+ return getAttributeAsInt(attribute, -1);
+ }
+
+ /**
+ * Returns the <tt>int</tt> value of the attribute with the specified
+ * <tt>name</tt>.
+ *
+ * @param attribute the name of the attribute that we'd like to retrieve
+ * @param defaultValue the <tt>int</tt> to be returned as the value of the
+ * specified attribute if no such attribute is currently registered with
+ * this extension
+ * @return the <tt>int</tt> value of the specified <tt>attribute</tt> or
+ * <tt>defaultValue</tt> if no such attribute is currently registered with
+ * this extension
+ */
+ public int getAttributeAsInt(String attribute, int defaultValue)
+ {
synchronized(attributes)
{
- String attributeVal = getAttributeAsString(attribute);
+ String value = getAttributeAsString(attribute);
- return attributeVal == null
- ? -1
- : Integer.parseInt(attributeVal);
+ return (value == null) ? defaultValue : Integer.parseInt(value);
}
}
@@ -292,6 +327,21 @@ public abstract class AbstractPacketExtension
}
/**
+ * Gets the names of the attributes which currently have associated values
+ * in this extension.
+ *
+ * @return the names of the attributes which currently have associated
+ * values in this extension
+ */
+ public List<String> getAttributeNames()
+ {
+ synchronized (attributes)
+ {
+ return new ArrayList<String>(attributes.keySet());
+ }
+ }
+
+ /**
* Specifies the text content of this extension.
*
* @param text the text content of this extension.
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/ExtmapPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/ExtmapPacketExtension.java
index 5b3e568..2a728a7 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/ExtmapPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/ExtmapPacketExtension.java
@@ -133,5 +133,4 @@ public class ExtmapPacketExtension
{
return super.getAttributeAsString(ATTRIBUTES_ATTR_NAME);
}
-
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
index 9c9f663..b98960c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
@@ -16,6 +16,7 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
* An {@link AbstractPacketExtension} implementation for transport elements.
*
* @author Emil Ivov
+ * @author Lyubomir Marinov
*/
public class IceUdpTransportPacketExtension extends AbstractPacketExtension
{
@@ -158,7 +159,24 @@ public class IceUdpTransportPacketExtension extends AbstractPacketExtension
{
synchronized(candidateList)
{
- this.candidateList.add(candidate);
+ candidateList.add(candidate);
+ }
+ }
+
+ /**
+ * Removes <tt>candidate</tt> from the list of
+ * {@link CandidatePacketExtension}s registered with this transport.
+ *
+ * @param candidate the <tt>CandidatePacketExtension</tt> to remove from
+ * this transport element
+ * @return <tt>true</tt> if the list of <tt>CandidatePacketExtension</tt>s
+ * registered with this transport contained the specified <tt>candidate</tt>
+ */
+ public boolean removeCandidate(CandidatePacketExtension candidate)
+ {
+ synchronized (candidateList)
+ {
+ return candidateList.remove(candidate);
}
}
@@ -215,7 +233,4 @@ public class IceUdpTransportPacketExtension extends AbstractPacketExtension
else if(childExtension instanceof CandidatePacketExtension)
addCandidate((CandidatePacketExtension) childExtension);
}
-
-
-
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JinglePacketFactory.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JinglePacketFactory.java
index a728030..b3eb0a3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JinglePacketFactory.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JinglePacketFactory.java
@@ -394,10 +394,10 @@ public class JinglePacketFactory
* packet.
*/
public static JingleIQ createContentRemove(
- String from,
- String to,
- String sid,
- List<ContentPacketExtension> contentList)
+ String from,
+ String to,
+ String sid,
+ Iterable<ContentPacketExtension> contentList)
{
JingleIQ contentRemove = new JingleIQ();
@@ -410,9 +410,7 @@ public class JinglePacketFactory
contentRemove.setAction(JingleAction.CONTENT_REMOVE);
for(ContentPacketExtension content : contentList)
- {
contentRemove.addContent(content);
- }
return contentRemove;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/PayloadTypePacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/PayloadTypePacketExtension.java
index 4612eb1..e76060c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/PayloadTypePacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/PayloadTypePacketExtension.java
@@ -79,7 +79,11 @@ public class PayloadTypePacketExtension extends AbstractPacketExtension
*/
public int getChannels()
{
- return getAttributeAsInt(CHANNELS_ATTR_NAME);
+ /*
+ * XEP-0167: Jingle RTP Sessions says: if omitted, it MUST be assumed
+ * to contain one channel.
+ */
+ return getAttributeAsInt(CHANNELS_ATTR_NAME, 1);
}
/**
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/RawUdpTransportPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/RawUdpTransportPacketExtension.java
index 9ffd35b..a81c0b7 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/RawUdpTransportPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/RawUdpTransportPacketExtension.java
@@ -16,6 +16,7 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
* An {@link AbstractPacketExtension} implementation for transport elements.
*
* @author Emil Ivov
+ * @author Lyubomir Marinov
*/
public class RawUdpTransportPacketExtension
extends IceUdpTransportPacketExtension
@@ -31,14 +32,6 @@ public class RawUdpTransportPacketExtension
public static final String ELEMENT_NAME = "transport";
/**
- * A list of one or more candidates representing each of the initiator's
- * higher-priority transport candidates as determined in accordance with
- * the ICE methodology.
- */
- private final List<CandidatePacketExtension> candidateList
- = new ArrayList<CandidatePacketExtension>();
-
- /**
* Creates a new {@link RawUdpTransportPacketExtension} instance.
*/
public RawUdpTransportPacketExtension()
@@ -54,33 +47,6 @@ public class RawUdpTransportPacketExtension
@Override
public List<? extends PacketExtension> getChildExtensions()
{
- return candidateList;
- }
-
- /**
- * Adds <tt>candidate</tt> to the list of {@link CandidatePacketExtension}s
- * registered with this transport.
- *
- * @param candidate the new {@link CandidatePacketExtension} to add to this
- * transport element.
- */
- public void addCandidate(CandidatePacketExtension candidate)
- {
- this.candidateList.add(candidate);
- }
-
- /**
- * Returns the list of {@link CandidatePacketExtension}s currently
- * registered with this transport.
- *
- * @return the list of {@link CandidatePacketExtension}s currently
- * registered with this transport.
- */
- public List<CandidatePacketExtension> getCandidateList()
- {
- synchronized(candidateList)
- {
- return new ArrayList<CandidatePacketExtension>(this.candidateList);
- }
+ return getCandidateList();
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
index bef4beb..bbb21b0 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
@@ -116,19 +116,24 @@ public class JingleUtils
DynamicPayloadTypeRegistry ptRegistry)
{
byte pt = (byte)payloadType.getID();
- List<ParameterPacketExtension> params = payloadType.getParameters();
//convert params to a name:value map
+ List<ParameterPacketExtension> params = payloadType.getParameters();
Map<String, String> paramsMap = new HashMap<String, String>();
for(ParameterPacketExtension param : params)
paramsMap.put(param.getName(), param.getValue());
//now create the format.
- MediaFormat format = JabberActivator.getMediaService()
- .getFormatFactory().createMediaFormat(
- pt, payloadType.getName(), (double)payloadType.getClockrate(),
- payloadType.getChannels(), paramsMap, null);
+ MediaFormat format
+ = JabberActivator.getMediaService().getFormatFactory()
+ .createMediaFormat(
+ pt,
+ payloadType.getName(),
+ (double)payloadType.getClockrate(),
+ payloadType.getChannels(),
+ paramsMap,
+ null);
//we don't seem to know anything about this format
if(format == null)
@@ -527,8 +532,8 @@ public class JingleUtils
for(Component component : stream.getComponents())
{
- for(Candidate cand : component.getLocalCandidates())
- trans.addCandidate(createCandidate(cand));
+ for(Candidate candidate : component.getLocalCandidates())
+ trans.addCandidate(createCandidate(candidate));
}
return trans;