aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2012-08-21 17:56:38 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2012-08-21 17:56:38 +0000
commit073d01525102b87c8a4c0b85c8636a264cc3dac2 (patch)
tree6907fe747e7ec10454417ba6e63a8f16f8c8f30e /src/net/java/sip/communicator/impl
parentc3f189792fa30712a9516b226a135dac6f95691b (diff)
downloadjitsi-073d01525102b87c8a4c0b85c8636a264cc3dac2.zip
jitsi-073d01525102b87c8a4c0b85c8636a264cc3dac2.tar.gz
jitsi-073d01525102b87c8a4c0b85c8636a264cc3dac2.tar.bz2
Fixes issues with video conferencing such as the display of a non-focus participant's video to other non-focus participants and the stopping of the streaming between non-focus participants upon stopping of the streaming of the focus' local video.
Diffstat (limited to 'src/net/java/sip/communicator/impl')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerJabberGTalkImpl.java127
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java760
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java1311
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java5
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java12
5 files changed, 1068 insertions, 1147 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerJabberGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerJabberGTalkImpl.java
index 188482b..917c47b 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerJabberGTalkImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerJabberGTalkImpl.java
@@ -6,6 +6,8 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
@@ -17,14 +19,12 @@ import org.jivesoftware.smackx.packet.*;
* of Jabber and Gtalk protocols.
*
* @author Vincent Lucas
+ * @author Lyubomir Marinov
*/
public abstract class AbstractCallPeerJabberGTalkImpl
<T extends AbstractCallJabberGTalkImpl<?>,
- U extends AbstractCallPeerMediaHandlerJabberGTalkImpl<?>>
- extends MediaAwareCallPeer<
- T,
- U,
- ProtocolProviderServiceJabberImpl>
+ U extends AbstractCallPeerMediaHandlerJabberGTalkImpl<?>>
+ extends MediaAwareCallPeer<T, U, ProtocolProviderServiceJabberImpl>
{
/**
* The <tt>Logger</tt> used by the <tt>AbstractCallPeerJabberGTalkImpl</tt>
@@ -34,14 +34,20 @@ public abstract class AbstractCallPeerJabberGTalkImpl
= Logger.getLogger(AbstractCallPeerJabberGTalkImpl.class);
/**
- * The jabber address of this peer
+ * Any discovery information that we have for this peer.
*/
- protected String peerJID = null;
+ private DiscoverInfo discoverInfo;
/**
- * Any discovery information that we have for this peer.
+ * The indicator which determines whether this peer was initiated the
+ * session.
*/
- private DiscoverInfo discoverInfo;
+ protected boolean initiator = false;
+
+ /**
+ * The jabber address of this peer
+ */
+ protected String peerJID;
/**
* Creates a new call peer with address <tt>peerAddress</tt>.
@@ -50,9 +56,7 @@ public abstract class AbstractCallPeerJabberGTalkImpl
* peer.
* @param owningCall the call that contains this call peer.
*/
- protected AbstractCallPeerJabberGTalkImpl(
- String peerAddress,
- T owningCall)
+ protected AbstractCallPeerJabberGTalkImpl(String peerAddress, T owningCall)
{
super(owningCall);
@@ -60,14 +64,29 @@ public abstract class AbstractCallPeerJabberGTalkImpl
}
/**
- * Sets the service discovery information that we have for this peer.
+ * Returns a String locator for that peer.
*
- * @param discoverInfo the discovery information that we have obtained for
- * this peer.
+ * @return the peer's address or phone number.
*/
- public void setDiscoverInfo(DiscoverInfo discoverInfo)
+ public String getAddress()
{
- this.discoverInfo = discoverInfo;
+ return peerJID;
+ }
+
+ /**
+ * Returns the contact corresponding to this peer or null if no
+ * particular contact has been associated.
+ * <p>
+ * @return the <tt>Contact</tt> corresponding to this peer or null
+ * if no particular contact has been associated.
+ */
+ public Contact getContact()
+ {
+ ProtocolProviderService pps = getCall().getProtocolProvider();
+ OperationSetPresence opSetPresence
+ = pps.getOperationSet(OperationSetPresence.class);
+
+ return opSetPresence.findContactByID(getAddress());
}
/**
@@ -81,7 +100,46 @@ public abstract class AbstractCallPeerJabberGTalkImpl
}
/**
- * Retrives the DiscoverInfo for a given peer identified by its URI.
+ * Returns a human readable name representing this peer.
+ *
+ * @return a String containing a name for that peer.
+ */
+ public String getDisplayName()
+ {
+ if (getCall() != null)
+ {
+ Contact contact = getContact();
+
+ if (contact != null)
+ return contact.getDisplayName();
+ }
+ return peerJID;
+ }
+
+ /**
+ * Returns full URI of the address.
+ *
+ * @return full URI of the address
+ */
+ public String getURI()
+ {
+ return "xmpp:" + peerJID;
+ }
+
+ /**
+ * Determines whether this peer initiated the session. Note that if this
+ * peer is the initiator of the session, then we are the responder!
+ *
+ * @return <tt>true</tt> if this peer initiated the session; <tt>false</tt>,
+ * otherwise (i.e. if _we_ initiated the session).
+ */
+ public boolean isInitiator()
+ {
+ return initiator;
+ }
+
+ /**
+ * Retrieves the DiscoverInfo for a given peer identified by its URI.
*
* @param calleeURI The URI of the call peer.
* @param ppsJabberImpl The call protocol provider service.
@@ -105,4 +163,37 @@ public abstract class AbstractCallPeerJabberGTalkImpl
logger.warn("could not retrieve info for " + calleeURI, ex);
}
}
+
+ /**
+ * Specifies the address, phone number, or other protocol specific
+ * identifier that represents this call peer. This method is to be
+ * used by service users and MUST NOT be called by the implementation.
+ *
+ * @param address The address of this call peer.
+ */
+ public void setAddress(String address)
+ {
+ if (!peerJID.equals(address))
+ {
+ String oldAddress = getAddress();
+
+ peerJID = address;
+
+ fireCallPeerChangeEvent(
+ CallPeerChangeEvent.CALL_PEER_ADDRESS_CHANGE,
+ oldAddress,
+ address);
+ }
+ }
+
+ /**
+ * Sets the service discovery information that we have for this peer.
+ *
+ * @param discoverInfo the discovery information that we have obtained for
+ * this peer.
+ */
+ public void setDiscoverInfo(DiscoverInfo discoverInfo)
+ {
+ this.discoverInfo = discoverInfo;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java
index 30e6644..a3329c3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java
@@ -12,7 +12,6 @@ import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.packet.*;
@@ -21,11 +20,11 @@ import org.jivesoftware.smack.packet.*;
* Implements a Google Talk <tt>CallPeer</tt>.
*
* @author Sebastien Vincent
+ * @author Lyubomir Marinov
*/
public class CallPeerGTalkImpl
extends AbstractCallPeerJabberGTalkImpl
- <CallGTalkImpl,
- CallPeerMediaHandlerGTalkImpl>
+ <CallGTalkImpl, CallPeerMediaHandlerGTalkImpl>
{
/**
* The <tt>Logger</tt> used by the <tt>CallPeerGTalkImpl</tt> class and its
@@ -35,25 +34,49 @@ public class CallPeerGTalkImpl
= Logger.getLogger(CallPeerGTalkImpl.class);
/**
- * The {@link SessionIQ} that created the session that this call represents.
+ * Returns whether or not the <tt>CallPeer</tt> is an Android phone or
+ * a call pass throught Google Voice or uses Google Talk client.
+ *
+ * We base the detection of the JID's resource which in the case of Android
+ * is android/Vtok/Talk.vXXXXXXX (where XXXXXX is a suite of
+ * numbers/letters).
*/
- private SessionIQ sessionInitIQ = null;
+ private static boolean isAndroidOrVtokOrTalkClient(String fullJID)
+ {
+ int idx = fullJID.indexOf('/');
+
+ if(idx != -1)
+ {
+ String res = fullJID.substring(idx + 1);
+ if(res.startsWith("android") || res.startsWith("Vtok") ||
+ res.startsWith("Talk.v"))
+ {
+ return true;
+ }
+ }
+
+ if(fullJID.contains(
+ "@" + ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN))
+ return true;
+
+ return false;
+ }
/**
- * Indicates whether this peer was the one that initiated the session.
+ * Temporary variable for handling client like FreeSwitch that sends
+ * "accept" message before sending candidates.
*/
- protected boolean isInitiator = false;
+ private SessionIQ sessAcceptedWithNoCands = null;
/**
- * Session ID.
+ * The {@link SessionIQ} that created the session that this call represents.
*/
- private String sid = null;
+ private SessionIQ sessionInitIQ = null;
/**
- * Temporary variable for handling client like FreeSwitch that sends
- * "accept" message before sending candidates.
+ * Session ID.
*/
- private SessionIQ sessAcceptedWithNoCands = null;
+ private String sid = null;
/**
* Creates a new call peer with address <tt>peerAddress</tt>.
@@ -69,131 +92,55 @@ public class CallPeerGTalkImpl
}
/**
- * Returns a String locator for that peer.
- *
- * @return the peer's address or phone number.
- */
- public String getAddress()
- {
- return peerJID;
- }
-
- /**
- * Returns full URI of the address.
+ * Indicates a user request to answer an incoming call from this
+ * <tt>CallPeer</tt>.
*
- * @return full URI of the address
- */
- public String getURI()
- {
- return "xmpp:" + peerJID;
- }
-
- /**
- * Specifies the address, phone number, or other protocol specific
- * identifier that represents this call peer. This method is to be
- * used by service users and MUST NOT be called by the implementation.
+ * Sends an OK response to <tt>callPeer</tt>. Make sure that the call
+ * peer contains an SDP description when you call this method.
*
- * @param address The address of this call peer.
+ * @throws OperationFailedException if we fail to create or send the
+ * response.
*/
- public void setAddress(String address)
+ public synchronized void answer()
+ throws OperationFailedException
{
- String oldAddress = getAddress();
-
- if(peerJID.equals(address))
- return;
-
- this.peerJID = address;
- //Fire the Event
- fireCallPeerChangeEvent(
- CallPeerChangeEvent.CALL_PEER_ADDRESS_CHANGE,
- oldAddress,
- address);
- }
+ RtpDescriptionPacketExtension answer = null;
- /**
- * Returns a human readable name representing this peer.
- *
- * @return a String containing a name for that peer.
- */
- public String getDisplayName()
- {
- if (getCall() != null)
+ try
{
- Contact contact = getContact();
-
- if (contact != null)
- return contact.getDisplayName();
+ getMediaHandler().getTransportManager().
+ wrapupConnectivityEstablishment();
+ answer = getMediaHandler().generateSessionAccept(true);
}
- return peerJID;
- }
-
- /**
- * Determines whether this peer was the one that initiated the session. Note
- * that if this peer is the initiator of the session then this means we are
- * the responder!
- *
- * @return <tt>true</tt> if this peer is the one that initiated the session
- * and <tt>false</tt> otherwise (i.e. if _we_ initiated the session).
- */
- public boolean isInitiator()
- {
- return isInitiator;
- }
-
- /**
- * Returns the contact corresponding to this peer or null if no
- * particular contact has been associated.
- * <p>
- * @return the <tt>Contact</tt> corresponding to this peer or null
- * if no particular contact has been associated.
- */
- public Contact getContact()
- {
- ProtocolProviderService pps = getCall().getProtocolProvider();
- OperationSetPresence opSetPresence
- = pps.getOperationSet(OperationSetPresence.class);
-
- return opSetPresence.findContactByID(getAddress());
- }
+ catch(IllegalArgumentException e)
+ {
+ sessAcceptedWithNoCands = new SessionIQ();
- /**
- * Processes the session initiation {@link SessionIQ} that we were created
- * with, passing its content to the media handler and then sends either a
- * "session-info/ringing" or a "terminate" response.
- *
- * @param sessionInitIQ The {@link SessionIQ} that created the session that
- * we are handling here.
- */
- protected synchronized void processSessionInitiate(SessionIQ sessionInitIQ)
- {
- // Do initiate the session.
- this.sessionInitIQ = sessionInitIQ;
- this.isInitiator = true;
+ // HACK apparently FreeSwitch need to have accept before
+ answer = getMediaHandler().generateSessionAccept(false);
- RtpDescriptionPacketExtension description = null;
+ SessionIQ response
+ = GTalkPacketFactory.createSessionAccept(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ getSessionID(),
+ answer);
- for(PacketExtension ext : sessionInitIQ.getExtensions())
- {
- if(ext.getElementName().equals(
- RtpDescriptionPacketExtension.ELEMENT_NAME))
- {
- description = (RtpDescriptionPacketExtension)ext;
- break;
- }
+ getProtocolProvider().getConnection().sendPacket(response);
+ return;
}
-
- if(description == null)
+ catch(Exception exc)
{
- logger.info("No description in incoming session initiate");
+ logger.info("Failed to answer an incoming call", exc);
- //send an error response;
- String reasonText = "Error: no description";
+ //send an error response
+ String reasonText = "Error: " + exc.getMessage();
SessionIQ errResp
= GTalkPacketFactory.createSessionTerminate(
sessionInitIQ.getTo(),
sessionInitIQ.getFrom(),
sessionInitIQ.getID(),
- Reason.INCOMPATIBLE_PARAMETERS,
+ Reason.FAILED_APPLICATION,
reasonText);
setState(CallPeerState.FAILED, reasonText);
@@ -201,35 +148,165 @@ public class CallPeerGTalkImpl
return;
}
+ SessionIQ response
+ = GTalkPacketFactory.createSessionAccept(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ getSessionID(),
+ answer);
+
+ //send the packet first and start the stream later in case the media
+ //relay needs to see it before letting hole punching techniques through.
+ if(sessAcceptedWithNoCands == null)
+ getProtocolProvider().getConnection().sendPacket(response);
+
try
{
- getMediaHandler().processOffer(description);
+ getMediaHandler().start();
}
- catch(Exception ex)
+ catch(UndeclaredThrowableException e)
{
- logger.info("Failed to process an incoming session initiate", ex);
+ Throwable exc = e.getUndeclaredThrowable();
- //send an error response;
- String reasonText = "Error: " + ex.getMessage();
+ logger.info("Failed to establish a connection", exc);
+
+ //send an error response
+ String reasonText = "Error: " + exc.getMessage();
SessionIQ errResp
= GTalkPacketFactory.createSessionTerminate(
sessionInitIQ.getTo(),
sessionInitIQ.getFrom(),
sessionInitIQ.getID(),
- Reason.INCOMPATIBLE_PARAMETERS,
+ Reason.GENERAL_ERROR,
reasonText);
+ getMediaHandler().getTransportManager().close();
setState(CallPeerState.FAILED, reasonText);
getProtocolProvider().getConnection().sendPacket(errResp);
return;
}
- // If we do not get the info about the remote peer yet. Get it right
- // now.
- if(this.getDiscoverInfo() == null)
+ //tell everyone we are connecting so that the audio notifications would
+ //stop
+ setState(CallPeerState.CONNECTED);
+ }
+
+ /**
+ * Returns the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ *
+ * @return the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ */
+ public String getSessInitID()
+ {
+ return sessionInitIQ != null ? sessionInitIQ.getPacketID() : null;
+ }
+
+ /**
+ * Returns the session ID of the Jingle session associated with this call.
+ *
+ * @return the session ID of the Jingle session associated with this call.
+ */
+ public String getSessionID()
+ {
+ return sessionInitIQ != null ? sessionInitIQ.getID() : sid;
+ }
+
+ /**
+ * Ends the call with for this <tt>CallPeer</tt>. Depending on the state
+ * of the peer the method would send a CANCEL, BYE, or BUSY_HERE message
+ * and set the new state to DISCONNECTED.
+ *
+ * @param failed indicates if the hangup is following to a call failure or
+ * simply a disconnect
+ * @param reasonText the text, if any, to be set on the
+ * <tt>ReasonPacketExtension</tt> as the value of its
+ * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be
+ * set on the <tt>ReasonPacketExtension</tt> as the value of its
+ * <tt>otherExtension</tt> property
+ */
+ public void hangup(boolean failed,
+ String reasonText,
+ PacketExtension reasonOtherExtension)
+ {
+ // do nothing if the call is already ended
+ if (CallPeerState.DISCONNECTED.equals(getState())
+ || CallPeerState.FAILED.equals(getState()))
{
- String calleeURI = sessionInitIQ.getFrom();
- retrieveDiscoverInfo(calleeURI);
+ if (logger.isDebugEnabled())
+ logger.debug("Ignoring a request to hangup a call peer "
+ + "that is already DISCONNECTED");
+ return;
+ }
+
+ CallPeerState prevPeerState = getState();
+ getMediaHandler().getTransportManager().close();
+
+ if (failed)
+ setState(CallPeerState.FAILED, reasonText);
+ else
+ setState(CallPeerState.DISCONNECTED, reasonText);
+
+ SessionIQ responseIQ = null;
+
+ if (prevPeerState.equals(CallPeerState.CONNECTED)
+ || CallPeerState.isOnHold(prevPeerState))
+ {
+ responseIQ = GTalkPacketFactory.createBye(
+ getProtocolProvider().getOurJID(), peerJID, getSessionID());
+ responseIQ.setInitiator(isInitiator() ? getAddress() :
+ getProtocolProvider().getOurJID());
+ }
+ else if (CallPeerState.CONNECTING.equals(prevPeerState)
+ || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
+ || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState))
+ {
+ responseIQ = GTalkPacketFactory.createCancel(
+ getProtocolProvider().getOurJID(), peerJID, getSessionID());
+ responseIQ.setInitiator(isInitiator() ? getAddress() :
+ getProtocolProvider().getOurJID());
+ }
+ else if (prevPeerState.equals(CallPeerState.INCOMING_CALL))
+ {
+ responseIQ = GTalkPacketFactory.createBusy(
+ getProtocolProvider().getOurJID(), peerJID, getSessionID());
+ responseIQ.setInitiator(isInitiator() ? getAddress() :
+ getProtocolProvider().getOurJID());
+ }
+ else if (prevPeerState.equals(CallPeerState.BUSY)
+ || prevPeerState.equals(CallPeerState.FAILED))
+ {
+ // For FAILED and BUSY we only need to update CALL_STATUS
+ // as everything else has been done already.
+ }
+ else
+ {
+ logger.info("Could not determine call peer state!");
+ }
+
+ if (responseIQ != null)
+ {
+ if (reasonOtherExtension != null)
+ {
+ ReasonPacketExtension reason
+ = (ReasonPacketExtension)
+ responseIQ.getExtension(
+ ReasonPacketExtension.ELEMENT_NAME,
+ ReasonPacketExtension.NAMESPACE);
+
+ if (reason != null)
+ {
+ reason.setOtherExtension(reasonOtherExtension);
+ }
+ else if(reason instanceof ReasonPacketExtension)
+ {
+ responseIQ.setReason(
+ (ReasonPacketExtension)reasonOtherExtension);
+ }
+ }
+
+ getProtocolProvider().getConnection().sendPacket(responseIQ);
}
}
@@ -247,7 +324,7 @@ public class CallPeerGTalkImpl
throws OperationFailedException
{
sid = SessionIQ.generateSID();
- isInitiator = false;
+ initiator = false;
//Create the media description that we'd like to send to the other side.
RtpDescriptionPacketExtension offer
@@ -294,112 +371,6 @@ public class CallPeerGTalkImpl
}
/**
- * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
- * reason to the user, if there is one.
- *
- * @param sessionIQ the {@link SessionIQ} that's terminating our session.
- */
- public void processSessionReject(SessionIQ sessionIQ)
- {
- processSessionTerminate(sessionIQ);
- }
-
- /**
- * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
- * reason to the user, if there is one.
- *
- * @param sessionIQ the {@link SessionIQ} that's terminating our session.
- */
- public void processSessionTerminate(SessionIQ sessionIQ)
- {
- String reasonStr = "Call ended by remote side.";
- ReasonPacketExtension reasonExt = sessionIQ.getReason();
-
- if(reasonStr != null && reasonExt != null)
- {
- Reason reason = reasonExt.getReason();
-
- if(reason != null)
- reasonStr += " Reason: " + reason.toString() + ".";
-
- String text = reasonExt.getText();
-
- if(text != null)
- reasonStr += " " + text;
- }
-
- getMediaHandler().getTransportManager().close();
- setState(CallPeerState.DISCONNECTED, reasonStr);
- }
-
- /**
- * Processes the session initiation {@link SessionIQ} that we were created
- * with, passing its content to the media handler.
- *
- * @param sessionInitIQ The {@link SessionIQ} that created the session that
- * we are handling here.
- */
- public void processSessionAccept(SessionIQ sessionInitIQ)
- {
- this.sessionInitIQ = sessionInitIQ;
-
- CallPeerMediaHandlerGTalkImpl mediaHandler = getMediaHandler();
- Collection<PacketExtension> extensions =
- sessionInitIQ.getExtensions();
- RtpDescriptionPacketExtension answer = null;
-
- for(PacketExtension ext : extensions)
- {
- if(ext.getElementName().equalsIgnoreCase(
- RtpDescriptionPacketExtension.ELEMENT_NAME))
- {
- answer = (RtpDescriptionPacketExtension)ext;
- break;
- }
- }
-
- try
- {
- mediaHandler.getTransportManager().
- wrapupConnectivityEstablishment();
- mediaHandler.processAnswer(answer);
- }
- catch(IllegalArgumentException e)
- {
- // HACK for FreeSwitch that send accept message before sending
- // candidates
- sessAcceptedWithNoCands = sessionInitIQ;
- return;
- }
- catch(Exception exc)
- {
- if (logger.isInfoEnabled())
- logger.info("Failed to process a session-accept", exc);
-
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
- SessionIQ errResp
- = GTalkPacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getID(),
- Reason.GENERAL_ERROR,
- reasonText);
-
- getMediaHandler().getTransportManager().close();
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- //tell everyone we are connecting so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
-
- mediaHandler.start();
- }
-
- /**
* Process candidates received.
*
* @param sessionInitIQ The {@link SessionIQ} that created the session we
@@ -450,7 +421,18 @@ public class CallPeerGTalkImpl
// candidates
if(sessAcceptedWithNoCands != null)
{
- if(!isInitiator)
+ if(isInitiator())
+ {
+ try
+ {
+ answer();
+ }
+ catch(OperationFailedException e)
+ {
+ logger.info("Failed to answer call (FreeSwitch hack)");
+ }
+ }
+ else
{
final SessionIQ sess = sessAcceptedWithNoCands;
sessAcceptedWithNoCands = null;
@@ -465,190 +447,115 @@ public class CallPeerGTalkImpl
}
}.start();
}
- else
- {
- try
- {
- answer();
- }
- catch(OperationFailedException e)
- {
- logger.info("Failed to answer call (FreeSwitch hack)");
- }
- }
sessAcceptedWithNoCands = null;
}
}
/**
- * Returns the session ID of the Jingle session associated with this call.
+ * Processes the session initiation {@link SessionIQ} that we were created
+ * with, passing its content to the media handler.
*
- * @return the session ID of the Jingle session associated with this call.
+ * @param sessionInitIQ The {@link SessionIQ} that created the session that
+ * we are handling here.
*/
- public String getSessionID()
+ public void processSessionAccept(SessionIQ sessionInitIQ)
{
- return sessionInitIQ != null ? sessionInitIQ.getID() : sid;
- }
+ this.sessionInitIQ = sessionInitIQ;
- /**
- * Returns the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- *
- * @return the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- */
- public String getSessInitID()
- {
- return sessionInitIQ != null ? sessionInitIQ.getPacketID() : null;
- }
+ CallPeerMediaHandlerGTalkImpl mediaHandler = getMediaHandler();
+ Collection<PacketExtension> extensions =
+ sessionInitIQ.getExtensions();
+ RtpDescriptionPacketExtension answer = null;
- /**
- * Ends the call with for this <tt>CallPeer</tt>. Depending on the state
- * of the peer the method would send a CANCEL, BYE, or BUSY_HERE message
- * and set the new state to DISCONNECTED.
- *
- * @param failed indicates if the hangup is following to a call failure or
- * simply a disconnect
- * @param reasonText the text, if any, to be set on the
- * <tt>ReasonPacketExtension</tt> as the value of its
- * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be
- * set on the <tt>ReasonPacketExtension</tt> as the value of its
- * <tt>otherExtension</tt> property
- */
- public void hangup(boolean failed,
- String reasonText,
- PacketExtension reasonOtherExtension)
- {
- // do nothing if the call is already ended
- if (CallPeerState.DISCONNECTED.equals(getState())
- || CallPeerState.FAILED.equals(getState()))
+ for(PacketExtension ext : extensions)
{
- if (logger.isDebugEnabled())
- logger.debug("Ignoring a request to hangup a call peer "
- + "that is already DISCONNECTED");
- return;
+ if(ext.getElementName().equalsIgnoreCase(
+ RtpDescriptionPacketExtension.ELEMENT_NAME))
+ {
+ answer = (RtpDescriptionPacketExtension)ext;
+ break;
+ }
}
- CallPeerState prevPeerState = getState();
- getMediaHandler().getTransportManager().close();
-
- if (failed)
- setState(CallPeerState.FAILED, reasonText);
- else
- setState(CallPeerState.DISCONNECTED, reasonText);
-
- SessionIQ responseIQ = null;
-
- if (prevPeerState.equals(CallPeerState.CONNECTED)
- || CallPeerState.isOnHold(prevPeerState))
- {
- responseIQ = GTalkPacketFactory.createBye(
- getProtocolProvider().getOurJID(), peerJID, getSessionID());
- responseIQ.setInitiator(isInitiator() ? getAddress() :
- getProtocolProvider().getOurJID());
- }
- else if (CallPeerState.CONNECTING.equals(prevPeerState)
- || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
- || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState))
- {
- responseIQ = GTalkPacketFactory.createCancel(
- getProtocolProvider().getOurJID(), peerJID, getSessionID());
- responseIQ.setInitiator(isInitiator() ? getAddress() :
- getProtocolProvider().getOurJID());
- }
- else if (prevPeerState.equals(CallPeerState.INCOMING_CALL))
- {
- responseIQ = GTalkPacketFactory.createBusy(
- getProtocolProvider().getOurJID(), peerJID, getSessionID());
- responseIQ.setInitiator(isInitiator() ? getAddress() :
- getProtocolProvider().getOurJID());
- }
- else if (prevPeerState.equals(CallPeerState.BUSY)
- || prevPeerState.equals(CallPeerState.FAILED))
+ try
{
- // For FAILED and BUSY we only need to update CALL_STATUS
- // as everything else has been done already.
+ mediaHandler.getTransportManager().
+ wrapupConnectivityEstablishment();
+ mediaHandler.processAnswer(answer);
}
- else
+ catch(IllegalArgumentException e)
{
- logger.info("Could not determine call peer state!");
+ // HACK for FreeSwitch that send accept message before sending
+ // candidates
+ sessAcceptedWithNoCands = sessionInitIQ;
+ return;
}
-
- if (responseIQ != null)
+ catch(Exception exc)
{
- if (reasonOtherExtension != null)
- {
- ReasonPacketExtension reason
- = (ReasonPacketExtension)
- responseIQ.getExtension(
- ReasonPacketExtension.ELEMENT_NAME,
- ReasonPacketExtension.NAMESPACE);
+ if (logger.isInfoEnabled())
+ logger.info("Failed to process a session-accept", exc);
- if (reason != null)
- {
- reason.setOtherExtension(reasonOtherExtension);
- }
- else if(reason instanceof ReasonPacketExtension)
- {
- responseIQ.setReason(
- (ReasonPacketExtension)reasonOtherExtension);
- }
- }
+ //send an error response
+ String reasonText = "Error: " + exc.getMessage();
+ SessionIQ errResp
+ = GTalkPacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getID(),
+ Reason.GENERAL_ERROR,
+ reasonText);
- getProtocolProvider().getConnection().sendPacket(responseIQ);
+ getMediaHandler().getTransportManager().close();
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
}
+
+ //tell everyone we are connecting so that the audio notifications would
+ //stop
+ setState(CallPeerState.CONNECTED);
+
+ mediaHandler.start();
}
/**
- * Indicates a user request to answer an incoming call from this
- * <tt>CallPeer</tt>.
- *
- * Sends an OK response to <tt>callPeer</tt>. Make sure that the call
- * peer contains an SDP description when you call this method.
+ * Processes the session initiation {@link SessionIQ} that we were created
+ * with, passing its content to the media handler and then sends either a
+ * "session-info/ringing" or a "terminate" response.
*
- * @throws OperationFailedException if we fail to create or send the
- * response.
+ * @param sessionInitIQ The {@link SessionIQ} that created the session that
+ * we are handling here.
*/
- public synchronized void answer()
- throws OperationFailedException
+ protected synchronized void processSessionInitiate(SessionIQ sessionInitIQ)
{
- RtpDescriptionPacketExtension answer = null;
+ // Do initiate the session.
+ this.sessionInitIQ = sessionInitIQ;
+ this.initiator = true;
- try
+ RtpDescriptionPacketExtension description = null;
+
+ for(PacketExtension ext : sessionInitIQ.getExtensions())
{
- getMediaHandler().getTransportManager().
- wrapupConnectivityEstablishment();
- answer = getMediaHandler().generateSessionAccept(true);
+ if(ext.getElementName().equals(
+ RtpDescriptionPacketExtension.ELEMENT_NAME))
+ {
+ description = (RtpDescriptionPacketExtension)ext;
+ break;
+ }
}
- catch(IllegalArgumentException e)
- {
- sessAcceptedWithNoCands = new SessionIQ();
-
- // HACK apparently FreeSwitch need to have accept before
- answer = getMediaHandler().generateSessionAccept(false);
-
- SessionIQ response
- = GTalkPacketFactory.createSessionAccept(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- getSessionID(),
- answer);
- getProtocolProvider().getConnection().sendPacket(response);
- return;
- }
- catch(Exception exc)
+ if(description == null)
{
- logger.info("Failed to answer an incoming call", exc);
+ logger.info("No description in incoming session initiate");
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
+ //send an error response;
+ String reasonText = "Error: no description";
SessionIQ errResp
= GTalkPacketFactory.createSessionTerminate(
sessionInitIQ.getTo(),
sessionInitIQ.getFrom(),
sessionInitIQ.getID(),
- Reason.FAILED_APPLICATION,
+ Reason.INCOMPATIBLE_PARAMETERS,
reasonText);
setState(CallPeerState.FAILED, reasonText);
@@ -656,76 +563,75 @@ public class CallPeerGTalkImpl
return;
}
- SessionIQ response
- = GTalkPacketFactory.createSessionAccept(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- getSessionID(),
- answer);
-
- //send the packet first and start the stream later in case the media
- //relay needs to see it before letting hole punching techniques through.
- if(sessAcceptedWithNoCands == null)
- getProtocolProvider().getConnection().sendPacket(response);
-
try
{
- getMediaHandler().start();
+ getMediaHandler().processOffer(description);
}
- catch(UndeclaredThrowableException e)
+ catch(Exception ex)
{
- Throwable exc = e.getUndeclaredThrowable();
-
- logger.info("Failed to establish a connection", exc);
+ logger.info("Failed to process an incoming session initiate", ex);
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
+ //send an error response;
+ String reasonText = "Error: " + ex.getMessage();
SessionIQ errResp
= GTalkPacketFactory.createSessionTerminate(
sessionInitIQ.getTo(),
sessionInitIQ.getFrom(),
sessionInitIQ.getID(),
- Reason.GENERAL_ERROR,
+ Reason.INCOMPATIBLE_PARAMETERS,
reasonText);
- getMediaHandler().getTransportManager().close();
setState(CallPeerState.FAILED, reasonText);
getProtocolProvider().getConnection().sendPacket(errResp);
return;
}
- //tell everyone we are connecting so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
+ // If we do not get the info about the remote peer yet. Get it right
+ // now.
+ if(this.getDiscoverInfo() == null)
+ {
+ String calleeURI = sessionInitIQ.getFrom();
+ retrieveDiscoverInfo(calleeURI);
+ }
}
/**
- * Returns whether or not the <tt>CallPeer</tt> is an Android phone or
- * a call pass throught Google Voice or uses Google Talk client.
+ * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
+ * reason to the user, if there is one.
*
- * We base the detection of the JID's resource which in the case of Android
- * is android/Vtok/Talk.vXXXXXXX (where XXXXXX is a suite of
- * numbers/letters).
+ * @param sessionIQ the {@link SessionIQ} that's terminating our session.
*/
- private static boolean isAndroidOrVtokOrTalkClient(String fullJID)
+ public void processSessionReject(SessionIQ sessionIQ)
{
- int idx = fullJID.indexOf('/');
+ processSessionTerminate(sessionIQ);
+ }
- if(idx != -1)
+ /**
+ * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
+ * reason to the user, if there is one.
+ *
+ * @param sessionIQ the {@link SessionIQ} that's terminating our session.
+ */
+ public void processSessionTerminate(SessionIQ sessionIQ)
+ {
+ String reasonStr = "Call ended by remote side.";
+ ReasonPacketExtension reasonExt = sessionIQ.getReason();
+
+ if(reasonStr != null && reasonExt != null)
{
- String res = fullJID.substring(idx + 1);
- if(res.startsWith("android") || res.startsWith("Vtok") ||
- res.startsWith("Talk.v"))
- {
- return true;
- }
- }
+ Reason reason = reasonExt.getReason();
- if(fullJID.contains(
- "@" + ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN))
- return true;
+ if(reason != null)
+ reasonStr += " Reason: " + reason.toString() + ".";
- return false;
+ String text = reasonExt.getText();
+
+ if(text != null)
+ reasonStr += " " + text;
+ }
+
+ getMediaHandler().getTransportManager().close();
+ setState(CallPeerState.DISCONNECTED, reasonStr);
}
/**
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 e0eefe8..3053bf5 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
@@ -13,7 +13,6 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.neomedia.*;
@@ -29,8 +28,7 @@ import org.jivesoftware.smack.packet.*;
*/
public class CallPeerJabberImpl
extends AbstractCallPeerJabberGTalkImpl
- <CallJabberImpl,
- CallPeerMediaHandlerJabberImpl>
+ <CallJabberImpl, CallPeerMediaHandlerJabberImpl>
{
/**
* The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its
@@ -40,14 +38,9 @@ public class CallPeerJabberImpl
= Logger.getLogger(CallPeerJabberImpl.class);
/**
- * The {@link JingleIQ} that created the session that this call represents.
- */
- private JingleIQ sessionInitIQ;
-
- /**
- * Indicates whether this peer was the one that initiated the session.
+ * If the call is cancelled before session-initiate is sent.
*/
- private boolean isInitiator = false;
+ private boolean cancelled = false;
/**
* Synchronization object for candidates available.
@@ -60,24 +53,24 @@ public class CallPeerJabberImpl
private boolean contentAddWithNoCands = false;
/**
- * Synchronization object for SID.
+ * If we have processed the session initiate.
*/
- private final Object sidSyncRoot = new Object();
+ private boolean sessionInitiateProcessed = false;
/**
- * If the call is cancelled before session-initiate is sent.
+ * Synchronization object.
*/
- private boolean cancelled = false;
+ private final Object sessionInitiateSyncRoot = new Object();
/**
- * Synchronization object.
+ * The {@link JingleIQ} that created the session that this call represents.
*/
- private final Object sessionInitiateSyncRoot = new Object();
+ private JingleIQ sessionInitIQ;
/**
- * If we have processed the session initiate.
+ * Synchronization object for SID.
*/
- private boolean sessionInitiateProcessed = false;
+ private final Object sidSyncRoot = new Object();
/**
* Creates a new call peer with address <tt>peerAddress</tt>.
@@ -109,222 +102,6 @@ public class CallPeerJabberImpl
}
/**
- * Returns a String locator for that peer.
- *
- * @return the peer's address or phone number.
- */
- public String getAddress()
- {
- return peerJID;
- }
-
- /**
- * Returns full URI of the address.
- *
- * @return full URI of the address
- */
- public String getURI()
- {
- return "xmpp:" + peerJID;
- }
-
- /**
- * Specifies the address, phone number, or other protocol specific
- * identifier that represents this call peer. This method is to be
- * used by service users and MUST NOT be called by the implementation.
- *
- * @param address The address of this call peer.
- */
- public void setAddress(String address)
- {
- String oldAddress = getAddress();
-
- if(peerJID.equals(address))
- return;
-
- this.peerJID = address;
- //Fire the Event
- fireCallPeerChangeEvent(
- CallPeerChangeEvent.CALL_PEER_ADDRESS_CHANGE,
- oldAddress,
- address);
- }
-
- /**
- * Returns a human readable name representing this peer.
- *
- * @return a String containing a name for that peer.
- */
- public String getDisplayName()
- {
- if (getCall() != null)
- {
- Contact contact = getContact();
-
- if (contact != null)
- return contact.getDisplayName();
- }
- return peerJID;
- }
-
- /**
- * Returns the contact corresponding to this peer or null if no
- * particular contact has been associated.
- * <p>
- * @return the <tt>Contact</tt> corresponding to this peer or null
- * if no particular contact has been associated.
- */
- public Contact getContact()
- {
- ProtocolProviderService pps = getCall().getProtocolProvider();
- OperationSetPresence opSetPresence
- = pps.getOperationSet(OperationSetPresence.class);
-
- return opSetPresence.findContactByID(getAddress());
- }
-
- /**
- * Processes the session initiation {@link JingleIQ} that we were created
- * with, passing its content to the media handler and then sends either a
- * "session-info/ringing" or a "session-terminate" response.
- *
- * @param sessionInitIQ The {@link JingleIQ} that created the session that
- * we are handling here.
- */
- protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
- {
- // Do initiate the session.
- this.sessionInitIQ = sessionInitIQ;
- this.isInitiator = true;
-
- // This is the SDP offer that came from the initial session-initiate.
- // Contrary to SIP, we are guaranteed to have content because XEP-0166
- // says: "A session consists of at least one content type at a time."
- List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
-
- try
- {
- getMediaHandler().processOffer(offer);
-
- CoinPacketExtension coin = null;
-
- for(PacketExtension ext : sessionInitIQ.getExtensions())
- {
- if(ext.getElementName().equals(
- CoinPacketExtension.ELEMENT_NAME))
- {
- coin = (CoinPacketExtension)ext;
- break;
- }
- }
-
- /* does the call peer acts as a conference focus ? */
- if(coin != null)
- {
- setConferenceFocus(Boolean.parseBoolean(
- (String)coin.getAttribute("isfocus")));
- }
- }
- catch(Exception ex)
- {
- logger.info("Failed to process an incoming session initiate", ex);
-
- //send an error response;
- String reasonText = "Error: " + ex.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- // If we do not get the info about the remote peer yet. Get it right
- // now.
- if(this.getDiscoverInfo() == null)
- {
- String calleeURI = sessionInitIQ.getFrom();
- retrieveDiscoverInfo(calleeURI);
- }
-
- //send a ringing response
- if (logger.isTraceEnabled())
- logger.trace("will send ringing response: ");
-
- getProtocolProvider().getConnection().sendPacket(
- JinglePacketFactory.createRinging(sessionInitIQ));
-
- synchronized(sessionInitiateSyncRoot)
- {
- sessionInitiateProcessed = true;
- sessionInitiateSyncRoot.notify();
- }
- }
-
- /**
- * Processes the session initiation {@link JingleIQ} that we were created
- * with, passing its content to the media handler and then sends either a
- * "session-info/ringing" or a "session-terminate" response.
- *
- * @param sessionInitiateExtensions a collection of additional and optional
- * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt>
- * {@link JingleIQ} which is to initiate the session with this
- * <tt>CallPeerJabberImpl</tt>
- * @throws OperationFailedException exception
- */
- protected synchronized void initiateSession(
- Iterable<PacketExtension> sessionInitiateExtensions)
- throws OperationFailedException
- {
- isInitiator = false;
-
- //Create the media description that we'd like to send to the other side.
- List<ContentPacketExtension> offer
- = getMediaHandler().createContentList();
-
- //send a ringing response
- if (logger.isTraceEnabled())
- logger.trace("will send ringing response: ");
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- synchronized(sidSyncRoot)
- {
- sessionInitIQ
- = JinglePacketFactory.createSessionInitiate(
- protocolProvider.getOurJID(),
- this.peerJID,
- JingleIQ.generateSID(),
- offer);
-
- if(cancelled)
- {
- // we cancelled the call too early so no need to send the
- // session-initiate to peer
- getMediaHandler().getTransportManager().close();
- return;
- }
- }
-
- if (sessionInitiateExtensions != null)
- {
- for (PacketExtension sessionInitiateExtension
- : sessionInitiateExtensions)
- {
- sessionInitIQ.addExtension(sessionInitiateExtension);
- }
- }
-
- protocolProvider.getConnection().sendPacket(sessionInitIQ);
- }
-
- /**
* Indicates a user request to answer an incoming call from this
* <tt>CallPeer</tt>.
*
@@ -406,6 +183,40 @@ public class CallPeerJabberImpl
}
/**
+ * Returns the session ID of the Jingle session associated with this call.
+ *
+ * @return the session ID of the Jingle session associated with this call.
+ */
+ public String getJingleSID()
+ {
+ return sessionInitIQ != null ? sessionInitIQ.getSID() : null;
+ }
+
+ /**
+ * Returns the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ *
+ * @return the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ */
+ public String getSessInitID()
+ {
+ return sessionInitIQ != null ? sessionInitIQ.getPacketID() : null;
+ }
+
+ /**
+ * Returns the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ *
+ * @return the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ */
+ public JingleIQ getSessionIQ()
+ {
+ return sessionInitIQ;
+ }
+
+ /**
* Ends the call with for this <tt>CallPeer</tt>. Depending on the state
* of the peer the method would send a CANCEL, BYE, or BUSY_HERE message
* and set the new state to DISCONNECTED.
@@ -509,64 +320,309 @@ public class CallPeerJabberImpl
}
/**
- * Returns the session ID of the Jingle session associated with this call.
+ * Processes the session initiation {@link JingleIQ} that we were created
+ * with, passing its content to the media handler and then sends either a
+ * "session-info/ringing" or a "session-terminate" response.
*
- * @return the session ID of the Jingle session associated with this call.
+ * @param sessionInitiateExtensions a collection of additional and optional
+ * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt>
+ * {@link JingleIQ} which is to initiate the session with this
+ * <tt>CallPeerJabberImpl</tt>
+ * @throws OperationFailedException exception
*/
- public String getJingleSID()
+ protected synchronized void initiateSession(
+ Iterable<PacketExtension> sessionInitiateExtensions)
+ throws OperationFailedException
{
- return sessionInitIQ != null ? sessionInitIQ.getSID() : null;
+ initiator = false;
+
+ //Create the media description that we'd like to send to the other side.
+ List<ContentPacketExtension> offer
+ = getMediaHandler().createContentList();
+
+ //send a ringing response
+ if (logger.isTraceEnabled())
+ logger.trace("will send ringing response: ");
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+
+ synchronized(sidSyncRoot)
+ {
+ sessionInitIQ
+ = JinglePacketFactory.createSessionInitiate(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ JingleIQ.generateSID(),
+ offer);
+
+ if(cancelled)
+ {
+ // we cancelled the call too early so no need to send the
+ // session-initiate to peer
+ getMediaHandler().getTransportManager().close();
+ return;
+ }
+ }
+
+ if (sessionInitiateExtensions != null)
+ {
+ for (PacketExtension sessionInitiateExtension
+ : sessionInitiateExtensions)
+ {
+ sessionInitIQ.addExtension(sessionInitiateExtension);
+ }
+ }
+
+ protocolProvider.getConnection().sendPacket(sessionInitIQ);
}
/**
- * Returns the IQ ID of the Jingle session-initiate packet associated with
- * this call.
+ * Processes the content-accept {@link JingleIQ}.
*
- * @return the IQ ID of the Jingle session-initiate packet associated with
- * this call.
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer has accepted
*/
- public String getSessInitID()
+ public void processContentAccept(JingleIQ content)
{
- return sessionInitIQ != null ? sessionInitIQ.getPacketID() : null;
+ List<ContentPacketExtension> contents = content.getContentList();
+
+ try
+ {
+ getMediaHandler().getTransportManager().
+ wrapupConnectivityEstablishment();
+ getMediaHandler().processAnswer(contents);
+ }
+ catch(Exception exc)
+ {
+ logger.warn("Failed to process a content-accept", 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();
}
/**
- * Returns the IQ ID of the Jingle session-initiate packet associated with
- * this call.
+ * Processes the content-add {@link JingleIQ}.
*
- * @return the IQ ID of the Jingle session-initiate packet associated with
- * this call.
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be added
*/
- public JingleIQ getSessionIQ()
+ public void processContentAdd(final JingleIQ content)
{
- return sessionInitIQ;
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ List<ContentPacketExtension> contents = content.getContentList();
+ Iterable<ContentPacketExtension> answerContents;
+ JingleIQ contentIQ;
+ boolean noCands = false;
+
+ logger.info("nocand " + noCands);
+ logger.info("run code");
+
+ /*
+ * If a remote peer turns her video on in a conference which is hosted
+ * by the local peer and the local peer is not streaming her local
+ * video, reinvite the other remote peers to enable RTP translation.
+ */
+ MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO);
+
+ try
+ {
+ if(!contentAddWithNoCands)
+ {
+ mediaHandler.processOffer(contents);
+
+ /*
+ * Gingle transport will not put candidate in session-initiate
+ * and content-add.
+ */
+ for(ContentPacketExtension c : contents)
+ {
+ if(JingleUtils.getFirstCandidate(c, 1) == null)
+ {
+ contentAddWithNoCands = true;
+ noCands = true;
+ }
+ }
+ }
+
+ // if no candidates are present, launch a new Thread which will
+ // process and wait for the connectivity establishment (otherwise
+ // the existing thread will be blocked and thus cannot receive
+ // transport-info with candidates
+ if(noCands)
+ {
+ new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ synchronized(candSyncRoot)
+ {
+ candSyncRoot.wait();
+ }
+ }
+ catch(InterruptedException e)
+ {
+ }
+
+ processContentAdd(content);
+ contentAddWithNoCands = false;
+ }
+ }.start();
+ logger.info("start thread");
+ return;
+ }
+
+ mediaHandler.getTransportManager().
+ wrapupConnectivityEstablishment();
+ logger.info("wraping up");
+ answerContents = mediaHandler.generateSessionAccept();
+ contentIQ = null;
+ }
+ catch(Exception e)
+ {
+ logger.warn("Exception occurred", e);
+
+ answerContents = null;
+ 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);
+ mediaHandler.start();
+
+ /*
+ * If a remote peer turns her video on in a conference which is hosted
+ * by the local peer and the local peer is not streaming her local
+ * video, reinvite the other remote peers to enable RTP translation.
+ */
+ if (oldVideoStream == null)
+ {
+ MediaStream newVideoStream
+ = mediaHandler.getStream(MediaType.VIDEO);
+
+ if ((newVideoStream != null)
+ && mediaHandler.isRTPTranslationEnabled())
+ {
+ try
+ {
+ getCall().modifyVideoContent(true);
+ }
+ catch (OperationFailedException ofe)
+ {
+ logger.error("Failed to enable RTP translation", ofe);
+ }
+ }
+ }
}
/**
- * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
- * reason to the user, if there is one.
+ * Processes the content-modify {@link JingleIQ}.
*
- * @param jingleIQ the {@link JingleIQ} that's terminating our session.
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be modified
*/
- public void processSessionTerminate(JingleIQ jingleIQ)
+ public void processContentModify(JingleIQ content)
{
- String reasonStr = "Call ended by remote side.";
- ReasonPacketExtension reasonExt = jingleIQ.getReason();
+ ContentPacketExtension ext = content.getContentList().get(0);
- if(reasonStr != null)
+ try
{
- Reason reason = reasonExt.getReason();
+ boolean modify = false;
+ if(ext.getFirstChildOfType(RtpDescriptionPacketExtension.class)
+ != null)
+ {
+ modify = true;
+ }
+ getMediaHandler().reinitContent(ext.getName(), ext, modify);
+ }
+ catch(Exception exc)
+ {
+ logger.info("Failed to process an incoming content-modify", exc);
- if(reason != null)
- reasonStr += " Reason: " + reason.toString() + ".";
+ //send an error response;
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
+ "Error: " + exc.getMessage());
- String text = reasonExt.getText();
+ setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+ }
- if(text != null)
- reasonStr += " " + text;
+ /**
+ * Processes the content-reject {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ}
+ */
+ public void processContentReject(JingleIQ content)
+ {
+ if(content.getContentList().isEmpty())
+ {
+ //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;
}
+ }
- setState(CallPeerState.DISCONNECTED, reasonStr);
+ /**
+ * Processes the content-remove {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be removed
+ */
+ public void processContentRemove(JingleIQ content)
+ {
+ List<ContentPacketExtension> contents = content.getContentList();
+
+ 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).
+ */
+ }
}
/**
@@ -614,58 +670,6 @@ public class CallPeerJabberImpl
}
/**
- * Puts the <tt>CallPeer</tt> represented by this instance on or off hold.
- *
- * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold;
- * <tt>false</tt>, otherwise
- *
- * @throws OperationFailedException if we fail to construct or send the
- * INVITE request putting the remote side on/off hold.
- */
- public void putOnHold(boolean onHold)
- throws OperationFailedException
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- mediaHandler.setLocallyOnHold(onHold);
-
- SessionInfoType type;
-
- if(onHold)
- type = SessionInfoType.hold;
- else
- {
- type = SessionInfoType.unhold;
- getMediaHandler().reinitAllContents();
- }
-
- //we are now on hold and need to realize this before potentially
- //spoiling it all with an exception while sending the packet :).
- reevalLocalHoldStatus();
-
- JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo(
- getProtocolProvider().getOurJID(),
- peerJID,
- getJingleSID(),
- type);
-
- getProtocolProvider().getConnection().sendPacket(onHoldIQ);
- }
-
- /**
- * Determines whether this peer was the one that initiated the session. Note
- * that if this peer is the initiator of the session then this means we are
- * the responder!
- *
- * @return <tt>true</tt> if this peer is the one that initiated the session
- * and <tt>false</tt> otherwise (i.e. if _we_ initiated the session).
- */
- public boolean isInitiator()
- {
- return isInitiator;
- }
-
- /**
* Handles the specified session <tt>info</tt> packet according to its
* content.
*
@@ -693,6 +697,116 @@ public class CallPeerJabberImpl
}
/**
+ * Processes the session initiation {@link JingleIQ} that we were created
+ * with, passing its content to the media handler and then sends either a
+ * "session-info/ringing" or a "session-terminate" response.
+ *
+ * @param sessionInitIQ The {@link JingleIQ} that created the session that
+ * we are handling here.
+ */
+ protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
+ {
+ // Do initiate the session.
+ this.sessionInitIQ = sessionInitIQ;
+ this.initiator = true;
+
+ // This is the SDP offer that came from the initial session-initiate.
+ // Contrary to SIP, we are guaranteed to have content because XEP-0166
+ // says: "A session consists of at least one content type at a time."
+ List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
+
+ try
+ {
+ getMediaHandler().processOffer(offer);
+
+ CoinPacketExtension coin = null;
+
+ for(PacketExtension ext : sessionInitIQ.getExtensions())
+ {
+ if(ext.getElementName().equals(
+ CoinPacketExtension.ELEMENT_NAME))
+ {
+ coin = (CoinPacketExtension)ext;
+ break;
+ }
+ }
+
+ /* does the call peer acts as a conference focus ? */
+ if(coin != null)
+ {
+ setConferenceFocus(Boolean.parseBoolean(
+ (String)coin.getAttribute("isfocus")));
+ }
+ }
+ catch(Exception ex)
+ {
+ logger.info("Failed to process an incoming session initiate", ex);
+
+ //send an error response;
+ String reasonText = "Error: " + ex.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ Reason.INCOMPATIBLE_PARAMETERS,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ // If we do not get the info about the remote peer yet. Get it right
+ // now.
+ if(this.getDiscoverInfo() == null)
+ {
+ String calleeURI = sessionInitIQ.getFrom();
+ retrieveDiscoverInfo(calleeURI);
+ }
+
+ //send a ringing response
+ if (logger.isTraceEnabled())
+ logger.trace("will send ringing response: ");
+
+ getProtocolProvider().getConnection().sendPacket(
+ JinglePacketFactory.createRinging(sessionInitIQ));
+
+ synchronized(sessionInitiateSyncRoot)
+ {
+ sessionInitiateProcessed = true;
+ sessionInitiateSyncRoot.notify();
+ }
+ }
+
+ /**
+ * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
+ * reason to the user, if there is one.
+ *
+ * @param jingleIQ the {@link JingleIQ} that's terminating our session.
+ */
+ public void processSessionTerminate(JingleIQ jingleIQ)
+ {
+ String reasonStr = "Call ended by remote side.";
+ ReasonPacketExtension reasonExt = jingleIQ.getReason();
+
+ if(reasonStr != null)
+ {
+ Reason reason = reasonExt.getReason();
+
+ if(reason != null)
+ reasonStr += " Reason: " + reason.toString() + ".";
+
+ String text = reasonExt.getText();
+
+ if(text != null)
+ reasonStr += " " + text;
+ }
+
+ setState(CallPeerState.DISCONNECTED, reasonStr);
+ }
+
+ /**
* Processes a specific "XEP-0251: Jingle Session Transfer"
* <tt>transfer</tt> packet (extension).
*
@@ -750,60 +864,130 @@ public class CallPeerJabberImpl
}
/**
- * Send a <tt>content-add</tt> to add video setup.
+ * Processes the <tt>transport-info</tt> {@link JingleIQ}.
+ *
+ * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process
*/
- private void sendAddVideoContent()
+ public void processTransportInfo(JingleIQ jingleIQ)
{
- List<ContentPacketExtension> contents;
-
+ /*
+ * The transport-info action is used to exchange transport candidates so
+ * it only concerns the mediaHandler.
+ */
try
{
- contents = getMediaHandler().createContentList(MediaType.VIDEO);
+ if(isInitiator())
+ {
+ synchronized(sessionInitiateSyncRoot)
+ {
+ if(!sessionInitiateProcessed)
+ {
+ try
+ {
+ sessionInitiateSyncRoot.wait();
+ }
+ catch(InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ getMediaHandler().processTransportInfo(
+ jingleIQ.getContentList());
}
- catch(Exception exc)
+ catch (OperationFailedException ofe)
{
- logger.warn("Failed to gather content for video type", exc);
+ logger.warn("Failed to process an incoming transport-info", ofe);
+
+ //send an error response
+ String reasonText = "Error: " + ofe.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ Reason.GENERAL_ERROR,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+
return;
}
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentAdd(
- protocolProvider.getOurJID(),
- this.peerJID,
- getJingleSID(),
- contents);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
+ synchronized(candSyncRoot)
+ {
+ candSyncRoot.notify();
+ }
}
/**
- * Send a <tt>content-remove</tt> to remove video setup.
+ * Puts the <tt>CallPeer</tt> represented by this instance on or off hold.
+ *
+ * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold;
+ * <tt>false</tt>, otherwise
+ *
+ * @throws OperationFailedException if we fail to construct or send the
+ * INVITE request putting the remote side on/off hold.
*/
- private void sendRemoveVideoContent()
+ public void putOnHold(boolean onHold)
+ throws OperationFailedException
{
CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- ContentPacketExtension content = new ContentPacketExtension();
- ContentPacketExtension remoteContent
- = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
+ mediaHandler.setLocallyOnHold(onHold);
- content.setName(remoteContent.getName());
- content.setCreator(remoteContent.getCreator());
- content.setSenders(remoteContent.getSenders());
+ SessionInfoType type;
+
+ if(onHold)
+ type = SessionInfoType.hold;
+ else
+ {
+ type = SessionInfoType.unhold;
+ getMediaHandler().reinitAllContents();
+ }
+
+ //we are now on hold and need to realize this before potentially
+ //spoiling it all with an exception while sending the packet :).
+ reevalLocalHoldStatus();
+
+ JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo(
+ getProtocolProvider().getOurJID(),
+ peerJID,
+ getJingleSID(),
+ type);
+
+ getProtocolProvider().getConnection().sendPacket(onHoldIQ);
+ }
+
+ /**
+ * Send a <tt>content-add</tt> to add video setup.
+ */
+ private void sendAddVideoContent()
+ {
+ List<ContentPacketExtension> contents;
+
+ try
+ {
+ contents = getMediaHandler().createContentList(MediaType.VIDEO);
+ }
+ catch(Exception exc)
+ {
+ logger.warn("Failed to gather content for video type", exc);
+ return;
+ }
ProtocolProviderServiceJabberImpl protocolProvider
= getProtocolProvider();
JingleIQ contentIQ
- = JinglePacketFactory.createContentRemove(
+ = JinglePacketFactory.createContentAdd(
protocolProvider.getOurJID(),
this.peerJID,
getJingleSID(),
- Arrays.asList(content));
+ contents);
protocolProvider.getConnection().sendPacket(contentIQ);
- mediaHandler.removeContent(remoteContent.getName());
}
/**
@@ -825,54 +1009,6 @@ public class CallPeerJabberImpl
/**
* Send a <tt>content</tt> message to reflect change in video setup (start
- * or stop).
- */
- public void sendModifyVideoResolutionContent()
- {
- ContentPacketExtension remoteContent = getMediaHandler().
- getRemoteContent(MediaType.VIDEO.toString());
- ContentPacketExtension content;
-
- logger.info("send modify-content to change resolution");
-
- // send content-modify with RTP description
- SendersEnum senders = remoteContent.getSenders();
-
- // create content list with resolution
- try
- {
- content = getMediaHandler().createContentForMedia(MediaType.VIDEO);
- }
- catch(Exception exc)
- {
- logger.warn("Failed to gather content for video type", exc);
- return;
- }
-
- // if we are only receiving video senders is null
- if(senders != null)
- content.setSenders(senders);
-
- JingleIQ contentIQ = JinglePacketFactory
- .createContentModify(getProtocolProvider().getOurJID(),
- this.peerJID, getJingleSID(), content);
-
- getProtocolProvider().getConnection().sendPacket(contentIQ);
-
- try
- {
- getMediaHandler().reinitContent(remoteContent.getName(), content,
- false);
- getMediaHandler().start();
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred when media reinitialization", e);
- }
- }
-
- /**
- * 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.
@@ -881,53 +1017,63 @@ public class CallPeerJabberImpl
*/
public void sendModifyVideoContent(boolean allowed)
{
- ContentPacketExtension ext = new ContentPacketExtension();
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ /*
+ * If the local peer is the focus of a video conference, it should not
+ * remove content and should rather add/modify content in order to
+ * perform RTP translation.
+ */
+ if (!allowed && mediaHandler.isRTPTranslationEnabled())
+ allowed = true;
+
ContentPacketExtension remoteContent
- = getMediaHandler().getRemoteContent(MediaType.VIDEO.toString());
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
- if(remoteContent == null)
+ if (remoteContent == null)
{
- if(allowed)
+ if (allowed)
sendAddVideoContent();
return;
}
- else if(!allowed
- && ((!isInitiator
- && (remoteContent.getSenders()
- == SendersEnum.initiator))
- || (isInitiator
- && (remoteContent.getSenders()
- == SendersEnum.responder))))
- {
- sendRemoveVideoContent();
- return;
- }
SendersEnum senders = remoteContent.getSenders();
+ if (!allowed)
+ {
+ boolean initiator = isInitiator();
+
+ if ((!initiator && (senders == SendersEnum.initiator))
+ || (initiator && (senders == SendersEnum.responder)))
+ {
+ sendRemoveVideoContent();
+ return;
+ }
+ }
+
/* adjust the senders attribute depending on current value and if we
* allowed or not local video streaming
*/
- if(allowed)
+ if (allowed)
{
- if(senders != SendersEnum.none)
- {
- senders = SendersEnum.both;
- }
- else if(senders == SendersEnum.none)
+ if (senders == SendersEnum.none)
{
senders
- = isInitiator
+ = isInitiator()
? SendersEnum.responder
: SendersEnum.initiator;
}
+ else
+ {
+ senders = SendersEnum.both;
+ }
}
else
{
- if(senders == SendersEnum.both || senders == null)
+ if ((senders == SendersEnum.both) || (senders == null))
{
senders
- = isInitiator
+ = isInitiator()
? SendersEnum.initiator
: SendersEnum.responder;
}
@@ -937,9 +1083,12 @@ public class CallPeerJabberImpl
}
}
+ ContentPacketExtension ext = new ContentPacketExtension();
+ String remoteContentName = remoteContent.getName();
+
ext.setSenders(senders);
ext.setCreator(remoteContent.getCreator());
- ext.setName(remoteContent.getName());
+ ext.setName(remoteContentName);
ProtocolProviderServiceJabberImpl protocolProvider
= getProtocolProvider();
@@ -954,9 +1103,7 @@ public class CallPeerJabberImpl
try
{
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- mediaHandler.reinitContent(remoteContent.getName(), ext, false);
+ mediaHandler.reinitContent(remoteContentName, ext, false);
mediaHandler.start();
}
catch(Exception e)
@@ -966,310 +1113,86 @@ public class CallPeerJabberImpl
}
/**
- * Processes the content-add {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be added
+ * Send a <tt>content</tt> message to reflect change in video setup (start
+ * or stop).
*/
- public void processContentAdd(final JingleIQ content)
+ public void sendModifyVideoResolutionContent()
{
CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- List<ContentPacketExtension> contents = content.getContentList();
- Iterable<ContentPacketExtension> answerContents;
- JingleIQ contentIQ;
- boolean noCands = false;
-
- logger.info("nocand " + noCands);
- logger.info("run code");
-
- /*
- * If a remote peer turns her video on in a conference which is hosted
- * by the local peer and the local peer is not streaming her local
- * video, reinvite the other remote peers to enable RTP translation.
- */
- MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO);
-
- try
- {
- if(!contentAddWithNoCands)
- mediaHandler.processOffer(contents);
-
- /* Gingle transport will not put candidate in session-initiate and
- * content-add
- */
- if(contentAddWithNoCands == false)
- {
- for(ContentPacketExtension c : contents)
- {
- if(JingleUtils.getFirstCandidate(c, 1) == null)
- {
- contentAddWithNoCands = true;
- noCands = true;
- }
- }
- }
-
- // if no candidates are present, launch a new Thread which will
- // process and wait for the connectivity establishment (otherwise
- // the existing thread will be blocked and thus cannot receive
- // transport-info with candidates
- if(noCands)
- {
- new Thread()
- {
- public void run()
- {
- try
- {
- synchronized(candSyncRoot)
- {
- candSyncRoot.wait();
- }
- }
- catch(InterruptedException e)
- {
- }
-
- processContentAdd(content);
- contentAddWithNoCands = false;
- }
- }.start();
- logger.info("start thread");
- return;
- }
-
- mediaHandler.getTransportManager().
- wrapupConnectivityEstablishment();
- logger.info("wraping up");
- answerContents = mediaHandler.generateSessionAccept();
- contentIQ = null;
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred", e);
-
- answerContents = null;
- 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);
- mediaHandler.start();
-
- /*
- * If a remote peer turns her video on in a conference which is hosted
- * by the local peer and the local peer is not streaming her local
- * video, reinvite the other remote peers to enable RTP translation.
- */
- if (oldVideoStream == null)
- {
- MediaStream newVideoStream
- = mediaHandler.getStream(MediaType.VIDEO);
+ ContentPacketExtension remoteContent
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
+ ContentPacketExtension content;
- if ((newVideoStream != null)
- && mediaHandler.isRTPTranslationEnabled())
- {
- try
- {
- getCall().modifyVideoContent(true);
- }
- catch (OperationFailedException ofe)
- {
- logger.error("Failed to enable RTP translation", ofe);
- }
- }
- }
- }
+ logger.info("send modify-content to change resolution");
- /**
- * Processes the content-accept {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer has accepted
- */
- public void processContentAccept(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
+ // send content-modify with RTP description
+ // create content list with resolution
try
{
- getMediaHandler().getTransportManager().
- wrapupConnectivityEstablishment();
- getMediaHandler().processAnswer(contents);
+ content = mediaHandler.createContentForMedia(MediaType.VIDEO);
}
- catch(Exception exc)
+ catch (Exception e)
{
- logger.warn("Failed to process a content-accept", 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);
+ logger.warn("Failed to gather content for video type", e);
return;
}
- getMediaHandler().start();
- }
-
- /**
- * Processes the content-modify {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be modified
- */
- public void processContentModify(JingleIQ content)
- {
- ContentPacketExtension ext = content.getContentList().get(0);
-
- try
- {
- boolean modify = false;
- if(ext.getFirstChildOfType(RtpDescriptionPacketExtension.class)
- != null)
- {
- modify = true;
- }
- getMediaHandler().reinitContent(ext.getName(), ext, modify);
- }
- catch(Exception exc)
- {
- logger.info("Failed to process an incoming content-modify", exc);
+ // if we are only receiving video senders is null
+ SendersEnum senders = remoteContent.getSenders();
- //send an error response;
- JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
- "Error: " + exc.getMessage());
+ if (senders != null)
+ content.setSenders(senders);
- setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
- }
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentModify(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getJingleSID(),
+ content);
- /**
- * Processes the content-remove {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be removed
- */
- public void processContentRemove(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
+ protocolProvider.getConnection().sendPacket(contentIQ);
- if (!contents.isEmpty())
+ try
{
- 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).
- */
+ mediaHandler.reinitContent(remoteContent.getName(), content, false);
+ mediaHandler.start();
}
- }
-
- /**
- * Processes the content-reject {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ}
- */
- public void processContentReject(JingleIQ content)
- {
- if(content.getContentList().isEmpty())
+ catch(Exception e)
{
- //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;
+ logger.warn("Exception occurred when media reinitialization", e);
}
}
/**
- * Processes the <tt>transport-info</tt> {@link JingleIQ}.
- *
- * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process
+ * Send a <tt>content-remove</tt> to remove video setup.
*/
- public void processTransportInfo(JingleIQ jingleIQ)
+ private void sendRemoveVideoContent()
{
- /*
- * The transport-info action is used to exchange transport candidates so
- * it only concerns the mediaHandler.
- */
- try
- {
- if(isInitiator)
- {
- synchronized(sessionInitiateSyncRoot)
- {
- if(!sessionInitiateProcessed)
- {
- try
- {
- sessionInitiateSyncRoot.wait();
- }
- catch(InterruptedException e)
- {
- }
- }
- }
- }
-
- getMediaHandler().processTransportInfo(
- jingleIQ.getContentList());
- }
- catch (OperationFailedException ofe)
- {
- logger.warn("Failed to process an incoming transport-info", ofe);
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- //send an error response
- String reasonText = "Error: " + ofe.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.GENERAL_ERROR,
- reasonText);
+ ContentPacketExtension content = new ContentPacketExtension();
+ ContentPacketExtension remoteContent
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
+ String remoteContentName = remoteContent.getName();
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
+ content.setName(remoteContentName);
+ content.setCreator(remoteContent.getCreator());
+ content.setSenders(remoteContent.getSenders());
- return;
- }
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentRemove(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getJingleSID(),
+ Arrays.asList(content));
- synchronized(candSyncRoot)
- {
- candSyncRoot.notify();
- }
+ protocolProvider.getConnection().sendPacket(contentIQ);
+ mediaHandler.removeContent(remoteContentName);
}
/**
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 5469e92..726a883 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -496,8 +496,9 @@ public class CallPeerMediaHandlerJabberImpl
//let's now see what was the format we announced as first and
//configure the stream with it.
+ String contentName = ourContent.getName();
ContentPacketExtension theirContent
- = this.remoteContentMap.get(ourContent.getName());
+ = this.remoteContentMap.get(contentName);
RtpDescriptionPacketExtension theirDescription
= JingleUtils.getRtpDescription(theirContent);
MediaFormat format = null;
@@ -560,7 +561,7 @@ public class CallPeerMediaHandlerJabberImpl
// create the corresponding stream...
initStream(
- ourContent.getName(),
+ contentName,
connector,
dev,
format,
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
index 76f6060..7d06db1 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
@@ -66,7 +66,7 @@ public class OperationSetVideoTelephonyJabberImpl
{
super.setLocalVideoAllowed(call, allowed);
- ((CallJabberImpl)call).modifyVideoContent(allowed);
+ ((AbstractCallJabberGTalkImpl<?>) call).modifyVideoContent(allowed);
}
/**
@@ -171,10 +171,10 @@ public class OperationSetVideoTelephonyJabberImpl
*/
public QualityControl getQualityControl(CallPeer peer)
{
- if(peer instanceof CallPeerJabberImpl)
- return ((CallPeerJabberImpl) peer).getMediaHandler().
- getQualityControl();
- else
- return null;
+ return
+ (peer instanceof CallPeerJabberImpl)
+ ? ((CallPeerJabberImpl) peer).getMediaHandler()
+ .getQualityControl()
+ : null;
}
}