diff options
author | Sebastien Vincent <seb@jitsi.org> | 2010-09-11 05:40:58 +0000 |
---|---|---|
committer | Sebastien Vincent <seb@jitsi.org> | 2010-09-11 05:40:58 +0000 |
commit | 2d711b828b0447fe0195325bd6a6bec8945120a9 (patch) | |
tree | 8b0b9acbc7a73ecffc0f6c17712a5cd5be72e175 /src/net/java/sip | |
parent | edab91d34a45cf98ee52e6efed6f527bd8334cd4 (diff) | |
download | jitsi-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.java | 158 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java | 227 |
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>. * |