aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2010-09-11 05:40:58 +0000
committerSebastien Vincent <seb@jitsi.org>2010-09-11 05:40:58 +0000
commit2d711b828b0447fe0195325bd6a6bec8945120a9 (patch)
tree8b0b9acbc7a73ecffc0f6c17712a5cd5be72e175 /src/net/java/sip
parentedab91d34a45cf98ee52e6efed6f527bd8334cd4 (diff)
downloadjitsi-2d711b828b0447fe0195325bd6a6bec8945120a9.zip
jitsi-2d711b828b0447fe0195325bd6a6bec8945120a9.tar.gz
jitsi-2d711b828b0447fe0195325bd6a6bec8945120a9.tar.bz2
Add XMPP content-add and content-remove support.
Diffstat (limited to 'src/net/java/sip')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java158
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java227
2 files changed, 361 insertions, 24 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 d42e1e6..c77f890 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
@@ -487,17 +487,85 @@ public class CallPeerJabberImpl
}
/**
- * Send a <tt>content-modify</tt> to reflect change in video setup (start or
- * stop).
+ * Send a <tt>content-add</tt> to add video setup.
+ */
+ private void sendAddVideoContent()
+ {
+ List<ContentPacketExtension> contents = null;
+
+ try
+ {
+ contents = getMediaHandler().
+ createContentList(MediaType.VIDEO);
+ }
+ catch(Exception exc)
+ {
+ logger.warn("Failed to gather content for video type", exc);
+ return;
+ }
+
+ JingleIQ contentIQ = JinglePacketFactory
+ .createContentAdd(getProtocolProvider().getOurJID(),
+ this.peerJID, getJingleSID(), contents);
+
+ getProtocolProvider().getConnection().sendPacket(contentIQ);
+ }
+
+ /**
+ * Send a <tt>content-remove</tt> to remove video setup.
+ */
+ private void sendRemoveVideoContent()
+ {
+ ContentPacketExtension content = new ContentPacketExtension();
+ ContentPacketExtension remoteContent = getMediaHandler().
+ getRemoteContent(MediaType.VIDEO.toString());
+ List<ContentPacketExtension> contents =
+ new ArrayList<ContentPacketExtension>();
+
+ 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);
+
+ getProtocolProvider().getConnection().sendPacket(contentIQ);
+ getMediaHandler().removeRemoteContent(remoteContent.getName());
+ }
+
+ /**
+ * Send a <tt>content</tt> message to reflect change in video setup (start
+ * or stop). Message can be content-modify if video content exists,
+ * content-add if we start video but video is not enabled on the peer or
+ * content-remove if we stop video and video is not enabled on the peer.
*
* @param allowed if the local video is allowed or not
-
*/
public void sendModifyVideoContent(boolean allowed)
{
ContentPacketExtension ext = new ContentPacketExtension();
- SendersEnum senders = getMediaHandler().getDirection(
- MediaType.VIDEO.toString());
+ ContentPacketExtension remoteContent = getMediaHandler().
+ getRemoteContent(MediaType.VIDEO.toString());
+
+ if(remoteContent == null)
+ {
+ if(allowed)
+ sendAddVideoContent();
+ return;
+ }
+ else if(!allowed &&
+ ((!isInitiator &&
+ remoteContent.getSenders() == SendersEnum.initiator) ||
+ (isInitiator &&
+ remoteContent.getSenders() == SendersEnum.responder)))
+ {
+ sendRemoveVideoContent();
+ return;
+ }
+
+ SendersEnum senders = remoteContent.getSenders();
/* adjust the senders attribute depending on current value and if we
* allowed or not local video streaming
@@ -531,9 +599,8 @@ public class CallPeerJabberImpl
}
ext.setSenders(senders);
- ext.setCreator(isInitiator ? CreatorEnum.initiator :
- CreatorEnum.responder);
- ext.setName(MediaType.VIDEO.toString());
+ ext.setCreator(remoteContent.getCreator());
+ ext.setName(remoteContent.getName());
JingleIQ contentIQ = JinglePacketFactory
.createContentModify(getProtocolProvider().getOurJID(),
@@ -543,8 +610,7 @@ public class CallPeerJabberImpl
try
{
- getMediaHandler().reinitContent(MediaType.VIDEO.toString(),
- senders);
+ getMediaHandler().reinitContent(remoteContent.getName(), senders);
}
catch(Exception e)
{
@@ -560,7 +626,35 @@ public class CallPeerJabberImpl
*/
public void processContentAdd(JingleIQ content)
{
- /* TODO */
+ List<ContentPacketExtension> contents = content.getContentList();
+ JingleIQ contentIQ = null;
+ List<ContentPacketExtension> answerContents =
+ new ArrayList<ContentPacketExtension>();
+
+ try
+ {
+ getMediaHandler().processOffer(contents);
+ answerContents = getMediaHandler().generateSessionAccept();
+ }
+ catch(Exception e)
+ {
+ logger.warn("Exception occurred", e);
+
+ contentIQ = JinglePacketFactory.createContentReject(
+ getProtocolProvider().getOurJID(),
+ this.peerJID, getJingleSID(), answerContents);
+ }
+
+ if(contentIQ == null)
+ {
+ /* send content-accept */
+ contentIQ = JinglePacketFactory.
+ createContentAccept(getProtocolProvider().getOurJID(),
+ this.peerJID, getJingleSID(), answerContents);
+ }
+
+ getProtocolProvider().getConnection().sendPacket(contentIQ);
+ getMediaHandler().start();
}
/**
@@ -571,7 +665,27 @@ public class CallPeerJabberImpl
*/
public void processContentAccept(JingleIQ content)
{
- /* TODO */
+ List<ContentPacketExtension> contents = content.getContentList();
+
+ try
+ {
+ getMediaHandler().processAnswer(contents);
+ }
+ catch(Exception exc)
+ {
+ logger.warn("Exception occurred", exc);
+ //send an error response;
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
+ "Error: " + exc.getMessage());
+
+ setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ getMediaHandler().start();
}
/**
@@ -613,7 +727,12 @@ public class CallPeerJabberImpl
*/
public void processContentRemove(JingleIQ content)
{
- /* TODO */
+ List<ContentPacketExtension> contents = content.getContentList();
+
+ for(ContentPacketExtension ext : contents)
+ {
+ getMediaHandler().removeRemoteContent(ext.getName());
+ }
}
/**
@@ -623,6 +742,17 @@ public class CallPeerJabberImpl
*/
public void processContentReject(JingleIQ content)
{
- /* TODO */
+ if(content.getContentList().size() == 0)
+ {
+ //send an error response;
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
+ "Error: content rejected");
+
+ setState(CallPeerState.FAILED, "Error: content rejected");
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
}
}
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 8f27338..7552f1f 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -97,15 +97,47 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
- * Get the direction of a specific content (like audio or video).
+ * Get the remote content of a specific content type (like audio or video).
*
- * @param content content name
- * @return the direction of the media
+ * @param contentType content type name
+ * @return remote <tt>ContentPacketExtension</tt> or null if not found
*/
- public SendersEnum getDirection(String content)
+ public ContentPacketExtension getRemoteContent(String contentType)
{
- ContentPacketExtension pkt = remoteContentMap.get(content);
- return pkt.getSenders();
+ for(String key : remoteContentMap.keySet())
+ {
+ ContentPacketExtension content = remoteContentMap.get(key);
+ RtpDescriptionPacketExtension description
+ = JingleUtils.getRtpDescription(content);
+
+ if(description.getMedia().equals(contentType))
+ {
+ return content;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the local content of a specific content type (like audio or video).
+ *
+ * @param contentType content type name
+ * @return remote <tt>ContentPacketExtension</tt> or null if not found
+ */
+ public ContentPacketExtension getLocalContent(String contentType)
+ {
+ for(String key : localContentMap.keySet())
+ {
+ ContentPacketExtension content = localContentMap.get(key);
+ RtpDescriptionPacketExtension description
+ = JingleUtils.getRtpDescription(content);
+
+ if(description.getMedia().equals(contentType))
+ {
+ return content;
+ }
+ }
+ return null;
}
/**
@@ -334,9 +366,29 @@ public class CallPeerMediaHandlerJabberImpl
//let's now see what was the format we announced as first and
//configure the stream with it.
- MediaFormat format = JingleUtils.payloadTypeToMediaFormat(
- theirDescription.getPayloadTypes().get(0),
- getDynamicPayloadTypes());
+ MediaFormat format = null;
+ List<PayloadTypePacketExtension> payloadTypes =
+ theirDescription.getPayloadTypes();
+
+ for(PayloadTypePacketExtension payload : payloadTypes)
+ {
+ format = JingleUtils.payloadTypeToMediaFormat(
+ payload,
+ getDynamicPayloadTypes());
+
+ if(format != null)
+ break;
+ }
+
+ if(format == null)
+ {
+ ProtocolProviderServiceJabberImpl.
+ throwOperationFailedException(
+ "No matching codec.",
+ OperationFailedException.ILLEGAL_ARGUMENT,
+ null,
+ logger);
+ }
//extract the extensions that we are advertising:
// check whether we will be exchanging any RTP extensions.
@@ -352,6 +404,107 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
+ * Creates a {@link ContentPacketExtension}s of the streams for a
+ * specific <tt>MediaDevice</tt>.
+ *
+ * @param type <tt>MediaDevice</tt>
+ * @return the {@link ContentPacketExtension}s of stream that this
+ * handler is prepared to initiate.
+ * @throws OperationFailedException if we fail to create the descriptions
+ * for reasons like - problems with device interaction, allocating ports,
+ * etc.
+ */
+ private ContentPacketExtension createContent(MediaDevice dev)
+ {
+ MediaDirection direction = dev.getDirection().and(
+ getDirectionUserPreference(
+ dev.getMediaType()));
+
+ if(isLocallyOnHold())
+ direction = direction.and(MediaDirection.SENDONLY);
+
+ if(direction != MediaDirection.INACTIVE)
+ {
+ ContentPacketExtension content = createContentForOffer(
+ dev.getSupportedFormats(), direction,
+ dev.getSupportedExtensions());
+
+ //ZRTP
+ if(getPeer().getCall().isSipZrtpAttribute())
+ {
+ ZrtpControl control = getZrtpControls().get(dev.getMediaType());
+ if(control == null)
+ {
+ control = JabberActivator.getMediaService()
+ .createZrtpControl();
+ getZrtpControls().put(dev.getMediaType(), control);
+ }
+
+ String helloHash[] = control.getHelloHashSep();
+
+ if(helloHash != null && helloHash[1].length() > 0)
+ {
+ ZrtpHashPacketExtension hash
+ = new ZrtpHashPacketExtension();
+ hash.setVersion(helloHash[0]);
+ hash.setValue(helloHash[1]);
+
+ content.addChildExtension(hash);
+ }
+ }
+
+ return content;
+ }
+ return null;
+ }
+
+ /**
+ * Creates a <tt>List</tt> containing the {@link ContentPacketExtension}s of
+ * the streams of a specific <tt>MediaType</tt> that this handler is
+ * prepared to initiate depending on available <tt>MediaDevice</tt>s and
+ * local on-hold and video transmission preferences.
+ *
+ * @param type <tt>MediaType</tt> of the content
+ * @return a {@link List} containing the {@link ContentPacketExtension}s of
+ * streams that this handler is prepared to initiate.
+ *
+ * @throws OperationFailedException if we fail to create the descriptions
+ * for reasons like - problems with device interaction, allocating ports,
+ * etc.
+ */
+ public List<ContentPacketExtension> createContentList(MediaType mediaType)
+ throws OperationFailedException
+ {
+ MediaDevice dev = getDefaultDevice(mediaType);
+ List<ContentPacketExtension> mediaDescs
+ = new ArrayList<ContentPacketExtension>();
+
+ if (dev != null)
+ {
+ ContentPacketExtension content = createContent(dev);
+
+ if(content != null)
+ mediaDescs.add(content);
+ }
+
+ //fail if all devices were inactive
+ if(mediaDescs.isEmpty())
+ {
+ ProtocolProviderServiceJabberImpl
+ .throwOperationFailedException(
+ "We couldn't find any active Audio/Video devices and "
+ + "couldn't create a call",
+ OperationFailedException.GENERAL_ERROR, null, logger);
+ }
+
+ //now add the transport elements
+ getTransportManager().startCandidateHarvest(mediaDescs);
+
+ //XXX ideally we wouldn't wrapup that quickly. we need to revisit this
+ return getTransportManager().wrapupHarvest();
+ }
+
+ /**
* Creates a <tt>List</tt> containing the {@link ContentPacketExtension}s of
* the streams that this handler is prepared to initiate depending on
* available <tt>MediaDevice</tt>s and local on-hold and video transmission
@@ -468,7 +621,7 @@ public class CallPeerMediaHandlerJabberImpl
return content;
}
- /**
+ /**
* Reinitialize all media contents.
*
* @throws OperationFailedException if we fail to handle <tt>content</tt>
@@ -525,6 +678,60 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
+ * Remove a media content and stop the corresponding stream.
+ *
+ * @param name of the Jingle content
+ */
+ public void removeLocalContent(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;
+ }
+ }
+
+ /**
+ * Remove a media content and stop the corresponding stream.
+ *
+ * @param name of the Jingle content
+ */
+ public void removeRemoteContent(String name)
+ {
+ ContentPacketExtension content = remoteContentMap.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;
+ }
+ }
+
+ /**
* Process a <tt>ContentPacketExtension</tt> and initialize its
* corresponding <tt>MediaStream</tt>.
*