From d23d36974fafbd9b3a8f2f96b407a5cb72a199ee Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Wed, 21 May 2008 16:48:06 +0000 Subject: [PATCH] Updated Jingle implementation. SMACK-240. Thanks to Jeff Williams. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@10424 b35dd754-fafc-0310-a699-88a17e54d16e --- .../smackx/jingle/ContentNegotiator.java | 322 ++++++++++++++++++ .../mediaimpl/test/TestMediaManager.java | 93 +++++ .../mediaimpl/test/TestMediaSession.java | 92 +++++ .../jingle/nat/FixedTransportManager.java | 68 ++++ 4 files changed, 575 insertions(+) create mode 100644 jingle/extension/source/org/jivesoftware/smackx/jingle/ContentNegotiator.java create mode 100644 jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java create mode 100644 jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java create mode 100644 jingle/extension/source/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/ContentNegotiator.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/ContentNegotiator.java new file mode 100644 index 000000000..63be725da --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/ContentNegotiator.java @@ -0,0 +1,322 @@ +package org.jivesoftware.smackx.jingle; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.jingle.listeners.JingleListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.MediaNegotiator; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; +import org.jivesoftware.smackx.jingle.nat.TransportNegotiator; +import org.jivesoftware.smackx.packet.Jingle; +import org.jivesoftware.smackx.packet.JingleContent; + +/** + * @author Jeff Williams + */ +public class ContentNegotiator extends JingleNegotiator { + + public static final String INITIATOR = "initiator"; + public static final String RESPONDER = "responder"; + + private List transportNegotiators; + private MediaNegotiator mediaNeg; // The description... + private TransportNegotiator transNeg; // and transport negotiators + private JingleTransportManager jingleTransportManager; + private String creator; + private String name; + private JingleMediaSession jingleMediaSession = null; + + public ContentNegotiator(JingleSession session, String inCreator, String inName) { + super(session); + creator = inCreator; + name = inName; + transportNegotiators = new ArrayList(); + } + + public List dispatchIncomingPacket(IQ iq, String id) throws XMPPException { + List responses = new ArrayList(); + + // First only process IQ packets that contain stanzas that + // match this media manager. + + if (iq != null) { + if (iq.getType().equals(IQ.Type.ERROR)) { + // Process errors + // TODO getState().eventError(iq); + } else if (iq.getType().equals(IQ.Type.RESULT)) { + // Process ACKs + if (isExpectedId(iq.getPacketID())) { + removeExpectedId(iq.getPacketID()); + } + } else if (iq instanceof Jingle) { + Jingle jingle = (Jingle) iq; + + // There are 1 or more sections in a Jingle packet. + // Find out which section belongs to this content negotiator, and + // then dispatch the Jingle packet to the media and transport negotiators. + + for (JingleContent jingleContent : jingle.getContentsList()) { + if (jingleContent.getName().equals(name)) { + if (mediaNeg != null) { + responses.addAll(mediaNeg.dispatchIncomingPacket(iq, id)); + } + + if (transNeg != null) { + responses.addAll(transNeg.dispatchIncomingPacket(iq, id)); + } + } + + } + } + } + return responses; + } + + public String getCreator() { + return creator; + } + + public String getName() { + return name; + } + + /** + * Get the JingleMediaSession of this Jingle Session + * + * @return the JingleMediaSession + */ + public JingleMediaSession getJingleMediaSession() { + return jingleMediaSession; + } + + public void addTransportNegotiator(TransportNegotiator transportNegotiator) { + transportNegotiators.add(transportNegotiator); + } + + /** + * @param jingleTransportManager + */ + public void setJingleTransportManager(JingleTransportManager jingleTransportManager) { + this.jingleTransportManager = jingleTransportManager; + } + + /** + * @return + */ + public JingleTransportManager getTransportManager() { + return jingleTransportManager; + } + + /** + * Called from above when starting a new session. + */ + public void start() { + JingleContent result = new JingleContent(creator, name); + + // result.setDescription(mediaNeg.start()); + // result.addJingleTransport(transNeg.start()); + // + // return result; + + mediaNeg.start(); + transNeg.start(); + } + + /** + * Prepare to close the media manager. + */ + public void close() { + destroyMediaNegotiator(); + destroyTransportNegotiator(); + } + + /** + * Obtain the description negotiator for this session + * + * @return the description negotiator + */ + public MediaNegotiator getMediaNegotiator() { + return mediaNeg; + } + + /** + * Set the jmf negotiator. + * + * @param mediaNeg + * the description negotiator to set + */ + protected void setMediaNegotiator(MediaNegotiator mediaNeg) { + destroyMediaNegotiator(); + this.mediaNeg = mediaNeg; + } + + /** + * Destroy the jmf negotiator. + */ + protected void destroyMediaNegotiator() { + if (mediaNeg != null) { + mediaNeg.close(); + mediaNeg = null; + } + } + + /** + * Obtain the transport negotiator for this session. + * + * @return the transport negotiator instance + */ + public TransportNegotiator getTransportNegotiator() { + return transNeg; + } + + /** + * Set TransportNegociator + * + * @param transNeg + * the transNeg to set + */ + protected void setTransportNegotiator(TransportNegotiator transNeg) { + destroyTransportNegotiator(); + this.transNeg = transNeg; + } + + /** + * Destroy the transport negotiator. + */ + protected void destroyTransportNegotiator() { + if (transNeg != null) { + transNeg.close(); + transNeg = null; + } + } + + /** + * Return true if the transport and content negotiators have finished + */ + public boolean isFullyEstablished() { + boolean result = true; + + MediaNegotiator mediaNeg = getMediaNegotiator(); + if ((mediaNeg == null) || (!mediaNeg.isFullyEstablished())) { + result = false; + } + + TransportNegotiator transNeg = getTransportNegotiator(); + if ((transNeg == null) || (!transNeg.isFullyEstablished())) { + result = false; + } + + return result; + } + + public JingleContent getJingleContent() { + JingleContent result = new JingleContent(creator, name); + + // PayloadType.Audio bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt(); + // TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate(); + // + // // Ok, send a packet saying that we accept this session + // // with the audio payload type and the transport + // // candidate + // result.setDescription(new JingleDescription.Audio(new PayloadType(bestCommonAudioPt))); + // result.addJingleTransport(this.getTransportNegotiator().getJingleTransport(bestRemoteCandidate)); + if (mediaNeg != null) { + result.setDescription(mediaNeg.getJingleDescription()); + } + if (transNeg != null) { + result.addJingleTransport(transNeg.getJingleTransport()); + } + + return result; + } + + public void triggerContentEstablished() { + + PayloadType bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt(); + TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate(); + TransportCandidate acceptedLocalCandidate = getTransportNegotiator().getAcceptedLocalCandidate(); + + // Trigger the session established flag + triggerContentEstablished(bestCommonAudioPt, bestRemoteCandidate, acceptedLocalCandidate); + } + + /** + * Trigger a session established event. + */ + private void triggerContentEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc) { + + // Let the session know that we've established a content/media segment. + JingleSession session = getSession(); + if (session != null) { + List listeners = session.getListenersList(); + for (JingleListener li : listeners) { + if (li instanceof JingleSessionListener) { + JingleSessionListener sli = (JingleSessionListener) li; + sli.sessionEstablished(pt, rc, lc, session); + } + } + } + + // Create a media session for each media manager in the session. + if (mediaNeg.getMediaManager() != null) { + rc.removeCandidateEcho(); + lc.removeCandidateEcho(); + + jingleMediaSession = getMediaNegotiator().getMediaManager().createMediaSession(pt, rc, lc, session); + jingleMediaSession.addMediaReceivedListener(session); + if (jingleMediaSession != null) { + + jingleMediaSession.startTrasmit(); + jingleMediaSession.startReceive(); + + for (TransportCandidate candidate : getTransportNegotiator().getOfferedCandidates()) + candidate.removeCandidateEcho(); + } + JingleMediaManager mediaManager = getMediaNegotiator().getMediaManager(); + getSession().addJingleMediaSession(mediaManager.getName(), jingleMediaSession); + } + + } + + /** + * Stop a Jingle media session. + */ + public void stopJingleMediaSession() { + + if (jingleMediaSession != null) { + jingleMediaSession.stopTrasmit(); + jingleMediaSession.stopReceive(); + } + } + + /** + * The negotiator state for the ContentNegotiators is a special case. + * It is a roll-up of the sub-negotiator states. + */ + public JingleNegotiatorState getNegotiatorState() { + JingleNegotiatorState result = JingleNegotiatorState.PENDING; + + if ((mediaNeg != null) && (transNeg != null)) { + + if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED) + || (transNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED)) + result = JingleNegotiatorState.SUCCEEDED; + + if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.FAILED) + || (transNeg.getNegotiatorState() == JingleNegotiatorState.FAILED)) + result = JingleNegotiatorState.FAILED; + } + + // Store the state (to make it easier to know when debugging.) + setNegotiatorState(result); + + return result; + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java new file mode 100644 index 000000000..fdb754ac2 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaManager.java @@ -0,0 +1,93 @@ +/** + * $RCSfile: TestMediaManager.java,v $ + * $Revision: 1.3 $ + * $Date: 25/12/2006 + *

+ * Copyright 2003-2006 Jive Software. + *

+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.jingle.mediaimpl.test; + +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; +import org.jivesoftware.smackx.jingle.JingleSession; + +import java.util.*; + +/** + * Implements a MediaManager for test purposes. + * + * @author Thiago Camargo + */ + +public class TestMediaManager extends JingleMediaManager { + + public static final String MEDIA_NAME = "TestMedia"; + + private List payloads = new ArrayList(); + + private PayloadType preferredPayloadType = null; + + public TestMediaManager(JingleTransportManager transportManager) { + super(transportManager); + } + + /** + * Return all supported Payloads for this Manager. + * + * @return The Payload List + */ + public List getPayloads() { + return payloads; + } + + public void setPayloads(List payloads) { + this.payloads.addAll(payloads); + } + + /** + * Returns a new JingleMediaSession + * + * @param payloadType payloadType + * @param remote remote Candidate + * @param local local Candidate + * @return JingleMediaSession JingleMediaSession + */ + public JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, + final TransportCandidate local, final JingleSession jingleSession) { + TestMediaSession session = null; + + session = new TestMediaSession(payloadType, remote, local, "", jingleSession); + + return session; + } + + public PayloadType getPreferredPayloadType() { + if (preferredPayloadType != null) + return preferredPayloadType; + return super.getPreferredPayloadType(); + } + + public void setPreferredPayloadType(PayloadType preferredPayloadType) { + this.preferredPayloadType = preferredPayloadType; + } + + public String getName() { + return MEDIA_NAME; + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java new file mode 100644 index 000000000..fa004a395 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/mediaimpl/test/TestMediaSession.java @@ -0,0 +1,92 @@ +/** + * $RCSfile: TestMediaSession.java,v $ + * $Revision: 1.1 $ + * $Date: 08/11/2006 + *

+ * Copyright 2003-2006 Jive Software. + *

+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.jingle.mediaimpl.test; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +/** + * This Class implements a complete JingleMediaSession for unit testing. + * + * @author Thiago Camargo + */ +public class TestMediaSession extends JingleMediaSession { + + /** + * Creates a TestMediaSession with defined payload type, remote and local candidates + * + * @param payloadType Payload of the jmf + * @param remote the remote information. The candidate that the jmf will be sent to. + * @param local the local information. The candidate that will receive the jmf + * @param locator media locator + */ + public TestMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local, + final String locator, JingleSession jingleSession) { + super(payloadType, remote, local, "Test", jingleSession); + initialize(); + } + + /** + * Initialize the screen share channels. + */ + public void initialize() { + + } + + /** + * Starts transmission and for NAT Traversal reasons start receiving also. + */ + public void startTrasmit() { + + } + + /** + * Set transmit activity. If the active is true, the instance should trasmit. + * If it is set to false, the instance should pause transmit. + * + * @param active active state + */ + public void setTrasmit(boolean active) { + + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void startReceive() { + // Do nothing + } + + /** + * Stops transmission and for NAT Traversal reasons stop receiving also. + */ + public void stopTrasmit() { + + } + + /** + * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf + */ + public void stopReceive() { + + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java new file mode 100644 index 000000000..fe3db8a87 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/nat/FixedTransportManager.java @@ -0,0 +1,68 @@ +package org.jivesoftware.smackx.jingle.nat; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.ContentNegotiator; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.media.PayloadType; + +/** + * $RCSfile: FixedTransportManager.java,v $ + * $Revision: 1.1 $ + * $Date: 15/11/2006 + * + * Copyright 2003-2006 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A Fixed Jingle Transport Manager implementation. + * + */ +public class FixedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener { + + FixedResolver resolver; + + public FixedTransportManager(FixedResolver inResolver) { + resolver = inResolver; + } + + protected TransportResolver createResolver(JingleSession session) { + return resolver; + } + + public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) { + } + + public void sessionDeclined(String reason, JingleSession jingleSession) { + } + + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } + + public void sessionClosed(String reason, JingleSession jingleSession) { + } + + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + } + + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + // Do Nothing + } + + public void sessionCreated(JingleSession jingleSession) { + jingleSession.addListener(this); + } +}