aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Ivov <emcho@jitsi.org>2007-08-27 17:28:46 +0000
committerEmil Ivov <emcho@jitsi.org>2007-08-27 17:28:46 +0000
commit4809ba5d41cba22e291343d493f05cf00d958d8f (patch)
treec9d9c2020cce91664809a757edd3fa173fdd84e6
parent241c038fe69e7617369fd16173a4fab9f362af1e (diff)
downloadjitsi-4809ba5d41cba22e291343d493f05cf00d958d8f.zip
jitsi-4809ba5d41cba22e291343d493f05cf00d958d8f.tar.gz
jitsi-4809ba5d41cba22e291343d493f05cf00d958d8f.tar.bz2
Committing support for Jingle. (By Symphorien Wanko)
-rw-r--r--ide/nbproject/project.xml61
-rw-r--r--lib/installer-exclude/smack.manifest.mf6
-rw-r--r--src/net/java/sip/communicator/impl/media/MediaServiceImpl.java72
-rw-r--r--src/net/java/sip/communicator/impl/media/RtpFlowImpl.java435
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepository.java187
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java292
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallParticipantJabberImpl.java346
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java53
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java720
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java170
-rwxr-xr-xsrc/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf7
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/AudioMediaSession.java218
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/JingleScMediaManager.java100
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/MediaUtils.java168
-rw-r--r--src/net/java/sip/communicator/impl/systray/jdic/SystrayServiceJdicImpl.java4
-rw-r--r--src/net/java/sip/communicator/service/media/MediaService.java59
-rw-r--r--src/net/java/sip/communicator/service/media/RtpFlow.java43
17 files changed, 2870 insertions, 71 deletions
diff --git a/ide/nbproject/project.xml b/ide/nbproject/project.xml
index fe195ef..cc3ce68 100644
--- a/ide/nbproject/project.xml
+++ b/ide/nbproject/project.xml
@@ -3,15 +3,13 @@
<type>org.netbeans.modules.ant.freeform</type>
<configuration>
<general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
- <!-- Created by Brian Burch on September 27, 2006
- Do NOT use the Netbeans Project Properties customizer
- because this file is maintained manually! The master
- version is held in CVS, as /ide/nbproject/project.xml.
- You should copy it to /nbproject/project.xml (when
- NetBeans is not running!) to make it active on your
- own system.
- -->
- <name>sip-communicator-1.0</name>
+ <!-- Created by Brian Burch on September 27, 2006
+ The master version is held in CVS, as
+ /ide/nbproject/project.xml. You should copy it to
+ /nbproject/project.xml (when NetBeans is not running!)
+ to make it active on your own system.
+ -->
+ <name>SIP Communicator</name>
<properties/>
<folders>
<source-folder>
@@ -27,7 +25,7 @@
</folders>
<ide-actions>
<action name="build">
- <target>make</target>
+ <target>rebuild</target>
</action>
<action name="clean">
<target>clean</target>
@@ -36,40 +34,29 @@
<target>javadoc</target>
</action>
<action name="run">
- <!-- run has NO dependents, but we have to protect
- users from not picking up the latest changes
- by doing an explicit incremental make.
- -->
- <target>make</target>
<target>run</target>
</action>
<action name="test">
<target>test</target>
</action>
<action name="rebuild">
+ <target>clean</target>
<target>rebuild</target>
</action>
- <action name="run.single">
- <target>test</target>
- <context>
- <property>test.name</property>
- <folder>test</folder>
- <pattern>\.java$</pattern>
- <format>relative-path</format>
- <arity>
- <one-file-only/>
- </arity>
- </context>
+ <action name="debug">
+ <script>nbproject/ide-targets.xml</script>
+ <target>debug-nb</target>
</action>
- <action name="debug.single">
- <target>debug-selected-file</target>
+ <action name="compile.single">
+ <script>nbproject/ide-file-targets.xml</script>
+ <target>compile-selected-files-in-src</target>
<context>
- <property>test.name</property>
- <folder>test</folder>
+ <property>files</property>
+ <folder>src</folder>
<pattern>\.java$</pattern>
<format>relative-path</format>
<arity>
- <one-file-only/>
+ <separated-files>,</separated-files>
</arity>
</context>
</action>
@@ -95,10 +82,7 @@
<ide-action name="run"/>
<ide-action name="test"/>
<ide-action name="rebuild"/>
- <action>
- <label>Safe clean/build of all targets</label>
- <target>rebuild</target>
- </action>
+ <ide-action name="debug"/>
</context-menu>
</view>
<subprojects/>
@@ -106,17 +90,12 @@
<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
<compilation-unit>
<package-root>src</package-root>
- <classpath mode="compile">
-lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar:lib/installer-exclude/bcprov-jdk14-130.jar:lib/installer-exclude/cindy.jar:lib/installer-exclude/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/JainSipApi1.2.jar:lib/installer-exclude/JainSipRi1.2.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b1.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/retroweaver-rt-2.0Beta2.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/Stun4J.jar:lib/installer-exclude/ymsg_network_v0_61.jar:lib/servicebinder.jar:lib/felix.jar:lib/jdic-all.jar:lib/kxml-min.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/AppleJavaExtensions.jar:lib/os-specific/mac/growl.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/os-specific/windows/jdic_stub.jar
- </classpath>
+ <classpath mode="compile">lib/felix.jar:lib/jdic-all.jar:lib/kxml-min.jar:lib/servicebinder.jar:lib/bundle/junit.jar:lib/bundle/log4j.jar:lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar:lib/installer-exclude/aclibico-2.1.jar:lib/installer-exclude/backport-util-concurrent.jar:lib/installer-exclude/bcprov-jdk14-130.jar:lib/installer-exclude/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/dnsjava-2.0.3.jar:lib/installer-exclude/JainSipApi1.2.jar:lib/installer-exclude/JainSipRi1.2.jar:lib/installer-exclude/jcalendar-1.3.2.jar:lib/installer-exclude/jdom.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b1.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/junit.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/retroweaver-rt-2.0.jar:lib/installer-exclude/rome-0.9.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx-jingle.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/Stun4J.jar:lib/installer-exclude/xalan-2.6.0.jar.ant:lib/installer-exclude/ymsg_network_v0_61.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/AppleJavaExtensions.jar:lib/os-specific/mac/growl.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar</classpath>
<source-level>1.4</source-level>
</compilation-unit>
<compilation-unit>
<package-root>test</package-root>
<unit-tests/>
- <classpath mode="compile">
-lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar:lib/installer-exclude/bcprov-jdk14-130.jar:lib/installer-exclude/cindy.jar:lib/installer-exclude/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/JainSipApi1.2.jar:lib/installer-exclude/JainSipRi1.2.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b1.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/retroweaver-rt-2.0Beta2.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/Stun4J.jar:lib/installer-exclude/ymsg_network_v0_61.jar:lib/servicebinder.jar:lib/felix.jar:lib/jdic-all.jar:lib/kxml-min.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/AppleJavaExtensions.jar:lib/os-specific/mac/growl.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/os-specific/windows/jdic_stub.jar:classes:lib/bundle/junit.jar
- </classpath>
<source-level>1.4</source-level>
</compilation-unit>
</java-data>
diff --git a/lib/installer-exclude/smack.manifest.mf b/lib/installer-exclude/smack.manifest.mf
index bf85252..c35d8b9 100644
--- a/lib/installer-exclude/smack.manifest.mf
+++ b/lib/installer-exclude/smack.manifest.mf
@@ -19,5 +19,11 @@ Export-Package: org.jivesoftware.smack,
org.jivesoftware.smackx,
org.jivesoftware.smackx.muc,
org.jivesoftware.smackx.packet,
+ org.jivesoftware.smackx.jingle,
+ org.jivesoftware.smackx.jingle.nat,
+ org.jivesoftware.smackx.jingle.media,
+ org.jivesoftware.smackx.jingle.packet,
+ org.jivesoftware.smackx.jingle.provider,
+ org.jivesoftware.smackx.jingle.listeners,
org.xmlpull.v1,
org.xmlpull.mxp1
diff --git a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
index 7bc3f84..05aa1e3 100644
--- a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
@@ -29,10 +29,14 @@ import net.java.sip.communicator.util.*;
* @author Emil Ivov
* @author Martin Andre
* @author Ryan Ricard
+ * @author Symphorien Wanko
*/
public class MediaServiceImpl
implements MediaService
{
+ /**
+ * Our logger.
+ */
private Logger logger = Logger.getLogger(MediaServiceImpl.class);
/**
@@ -74,7 +78,7 @@ public class MediaServiceImpl
* control mapping.
*/
private MediaControl defaultMediaControl = new MediaControl();
-
+
/**
* Mappings of calls to instances of <tt>MediaControl</tt>. In case a call
* has been mapped to a media control instance, it is going to be used for
@@ -84,18 +88,18 @@ public class MediaServiceImpl
* as their sound source.
*/
private Map callMediaControlMappings = new Hashtable();
-
+
/**
* Mappings of calls to custom data sinks. Used by mailbox plug-ins for
* sending audio/video flows to a file instead of the sound card or the
* screen.
*/
- private Hashtable callDataSinkMappings = new Hashtable();
+ private Map callDataSinkMappings = new Hashtable();
/**
* Currently open call sessions.
*/
- private Hashtable activeCallSessions = new Hashtable();
+ private Map activeCallSessions = new Hashtable();
/**
* Default constructor
@@ -105,6 +109,31 @@ public class MediaServiceImpl
}
/**
+ * Implements <tt>getSupportedAudioEncodings</tt> from interface
+ * <tt>MediaService</tt>
+ *
+ * @return an array of Strings containing audio formats in the order of
+ * preference.
+ */
+ public String[] getSupportedAudioEncodings()
+ {
+ return defaultMediaControl.getSupportedAudioEncodings();
+ }
+
+ /**
+ * Implements <tt>getSupportedVideoEncodings</tt> from interface
+ * <tt>MediaService</tt>
+ *
+ * @return an array of Strings containing video formats in the order of
+ * preference.
+ */
+ public String[] getSupportedVideoEncodings()
+ {
+ return defaultMediaControl.getSupportedAudioEncodings();
+ }
+
+
+ /**
* Creates a call session for <tt>call</tt>. The method allocates audio
* and video ports which won't be released until the corresponding call
* gets into a DISCONNECTED state. If a session already exists for call,
@@ -134,6 +163,36 @@ public class MediaServiceImpl
}
/**
+ * A <tt>RtpFlow</tt> is an object which role is to handle media data
+ * transfer, capture and playback. It's build between two points, a local
+ * and a remote. The media transfered will be in a format specified by the
+ * <tt>mediaEncodings</tt> parameter.
+ *
+ * @param localIP local address of this RtpFlow
+ * @param localPort local port of this RtpFlow
+ * @param remoteIP remote address of this RtpFlow
+ * @param remotePort remote port of this RtpFlow
+ * @param mediaEncodings format used to encode data on this flow
+ * @return rtpFlow the newly created <tt>RtpFlow</tt>
+ * @throws MediaException if operation fails
+ */
+ public RtpFlow createRtpFlow(String localIP,
+ int localPort,
+ String remoteIP,
+ int remotePort,
+ Map mediaEncodings)
+ throws MediaException
+ {
+ waitUntilStarted();
+ assertStarted();
+
+ RtpFlowImpl rtpFlow = new RtpFlowImpl(this, localIP, remoteIP,
+ localPort, remotePort, new Hashtable(mediaEncodings));
+ return rtpFlow;
+ }
+
+
+ /**
* Adds a listener that will be listening for incoming media and changes
* in the state of the media listener.
*
@@ -191,8 +250,11 @@ public class MediaServiceImpl
}
/**
+ * Verifies whether the media service is started and ready for use and
+ * throws an exception otherwise.
*
- * @throws MediaException
+ * @throws MediaException if the media service is not started and ready for
+ * use.
*/
protected void assertStarted()
throws MediaException
diff --git a/src/net/java/sip/communicator/impl/media/RtpFlowImpl.java b/src/net/java/sip/communicator/impl/media/RtpFlowImpl.java
new file mode 100644
index 0000000..44e15b7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/RtpFlowImpl.java
@@ -0,0 +1,435 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.media;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.media.*;
+import javax.media.rtp.*;
+import javax.media.control.*;
+import javax.media.protocol.*;
+import javax.media.rtp.event.*;
+
+import net.java.sip.communicator.service.media.*;
+import net.java.sip.communicator.service.media.MediaException;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implementation of a <tt>RtpFlow</tt> which is a bridge for data transmission
+ * between two end points. Data which transit on this flow are encoded with
+ * a specified format.
+ *
+ * @author Symphorien Wanko
+ */
+public class RtpFlowImpl
+ implements RtpFlow,
+ ReceiveStreamListener,
+ SessionListener,
+ ControllerListener
+{
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger
+ = Logger.getLogger(RtpFlowImpl.class);
+
+ /**
+ * Our IP address
+ */
+ private String localAddress = null;
+
+ /**
+ * IP to which this <tt>flow send media</tt>
+ */
+ private String remoteAddress = null;
+
+ /**
+ * The local port used by this <tt>RtpFlow</tt>
+ */
+ private int localPort = -1;
+
+ /**
+ * Port fort the remote endpoint
+ */
+ private int remotePort = -1;
+
+ /**
+ * Collection of <tt>RtpManager</tt>s used by this <tt>RtpFlow</tt>
+ */
+ private RTPManager rtpMgrs[] = null;
+
+ /**
+ * Data source used for media capture
+ */
+ private DataSource dataSource = null;
+
+ /**
+ * Collection of send streams used by this <tt>RtpFlow</tt>
+ */
+ private List sendStreams = new ArrayList();
+
+ /**
+ * The media on which this <tt>RtpFlow</tt> depend,
+ * the one which created us
+ */
+ private MediaServiceImpl mediaService = null;
+
+ /**
+ * Used for controlling media
+ */
+ private MediaControl mediaControl = null;
+
+ /**
+ * Media encoding passed to JMF via the media control
+ */
+ private Hashtable mediaEncoding = new Hashtable();
+
+ /**
+ * Creates an instance of <tt>RtpFlowImpl</tt> for media transmission.
+ * A flow do not care about session or anything other than transmitting
+ * media data between two end points, using the given media encoding.
+ *
+ * @param mediaServie the media service which created us
+ * @param localAddress local IP address
+ * @param localPort local port number
+ * @param remoteAddress remote IP address
+ * @param remotePort remote port number
+ * @param mediaEncoding media encoding used for data
+ *
+ * @throws MediaException if initializing the flow fails.
+ */
+ public RtpFlowImpl(MediaServiceImpl mediaServie,
+ String localIpAddress,
+ String remoteIpAddress,
+ int localPort,
+ int remotePort,
+ Hashtable mediaEncoding)
+ throws MediaException
+ {
+ this.localAddress = localIpAddress;
+ this.remoteAddress = remoteIpAddress;
+ this.localPort = localPort;
+ this.remotePort = remotePort;
+ this.mediaService = mediaServie;
+ this.mediaControl = mediaService.getMediaControl();
+ this.mediaEncoding.putAll( mediaEncoding );
+
+ initialize();
+ }
+
+ /**
+ * Returns the local port used by this flow.
+ *
+ * @return localPort the local port used by this flow.
+ */
+ public int getLocalPort()
+ {
+ return localPort;
+ }
+
+ /**
+ * Returns the local address used by this flow.
+ *
+ * @return localAddress the local address port used by this flow.
+ */
+ public String getLocalAddress()
+ {
+ return localAddress;
+ }
+
+ /**
+ * Returns the remote port used by this flow.
+ *
+ * @return remotePort the remote port used by this flow.
+ */
+ public int getRemotePort()
+ {
+ return remotePort;
+ }
+
+ /**
+ * Returns the remote address used by this flow.
+ *
+ * @return remoteAddress the remote address port used by this flow.
+ */
+ public String getRemoteAddress()
+ {
+ return remoteAddress;
+ }
+
+ /**
+ * This method initializes the <tt>RtpFlow</tt> by creating
+ * datasource and associated transmitter. We also create session for
+ * each JMF track here.
+ *
+ * @throws MediaException if initialization fails
+ */
+ private void initialize() throws MediaException
+ {
+ dataSource = mediaControl.createDataSourceForEncodings(mediaEncoding);
+
+ PushBufferDataSource pbds = (PushBufferDataSource) dataSource;
+ PushBufferStream pbss[] = pbds.getStreams();
+
+ rtpMgrs = new RTPManager[pbss.length];
+ SessionAddress localAddr, destAddr;
+ InetAddress ipAddr;
+ SendStream sendStream;
+
+ int port;
+
+ for (int i = 0; i < pbss.length; i++)
+ {
+ try
+ {
+ rtpMgrs[i] = RTPManager.newInstance();
+
+ port = remotePort + 2 * i;
+ ipAddr = InetAddress.getByName(remoteAddress);
+
+ localAddr = new SessionAddress(InetAddress.
+ getByName(this.localAddress), localPort);
+
+ destAddr = new SessionAddress(ipAddr, port);
+
+ rtpMgrs[i].addReceiveStreamListener(this);
+ rtpMgrs[i].addSessionListener(this);
+
+ BufferControl bc = (BufferControl) rtpMgrs[i]
+ .getControl("javax.media.control.BufferControl");
+ if (bc != null)
+ {
+ int bl = 160;
+ bc.setBufferLength(bl);
+ }
+
+ try
+ {
+ rtpMgrs[i].initialize(localAddr);
+ }
+ catch (InvalidSessionAddressException e)
+ {
+ // In case the local address is not allowed to read,
+ // we user another local address
+ SessionAddress sessAddr = new SessionAddress();
+ localAddr = new SessionAddress(sessAddr.getDataAddress(),
+ localPort);
+ rtpMgrs[i].initialize(localAddr);
+ }
+
+ rtpMgrs[i].addTarget(destAddr);
+ logger.info("Created RTP session at " + localPort +
+ " to: " + remoteAddress + " " + port);
+ sendStream = rtpMgrs[i].createSendStream(dataSource, i);
+ sendStreams.add(sendStream);
+ sendStream.start();
+ }
+ catch (Exception e)
+ {
+ throw new MediaException("Failed to create transmitter"
+ , MediaException.INTERNAL_ERROR, e);
+ }
+ }
+ }
+
+ /**
+ * Implementation of <tt>startTransmission</tt> start to send media data.
+ */
+ public void start()
+ {
+ mediaControl.startProcessingMedia(this);
+ }
+
+ /**
+ * Stops the transmission if already started.
+ * Stops receiving also.
+ */
+ public void stop()
+ {
+ RTPManager rtpMgr;
+ for (int i = 0; i < rtpMgrs.length; i++)
+ {
+ rtpMgr = rtpMgrs[i];
+ rtpMgr.removeReceiveStreamListener(this);
+ rtpMgr.removeSessionListener(this);
+ rtpMgr.removeTargets("Session ended.");
+ rtpMgr.dispose();
+ }
+ sendStreams.clear();
+ mediaControl.stopProcessingMedia(this);
+ }
+
+ /**
+ * Resume media transmission on this flow
+ */
+ public void resume()
+ {
+ Iterator it = sendStreams.iterator();
+ SendStream sendStream;
+
+ logger.info("pausing transmission... ");
+
+ while (it.hasNext())
+ {
+ sendStream = (SendStream) it.next();
+ try
+ {
+ sendStream.start();
+ }
+ catch (IOException ex)
+ {
+ logger.warn("Exception when pausing transmission ", ex);
+ }
+ }
+ }
+
+ /**
+ * Pause media transmission on this flow
+ */
+ public void pause()
+ {
+ Iterator it = sendStreams.iterator();
+ SendStream sendStream;
+
+ logger.info("pausing transmission... ");
+
+ while (it.hasNext())
+ {
+ sendStream = (SendStream) it.next();
+ try
+ {
+ sendStream.stop();
+ }
+ catch (IOException ex)
+ {
+ logger.warn("Exception when pausing transmission ", ex);
+ }
+ }
+ }
+
+ /**
+ * Implements update from javax.media.rtp.SessionListener
+ *
+ * @param evt received event
+ */
+ public synchronized void update(SessionEvent evt)
+ {
+ if (evt instanceof NewParticipantEvent)
+ {
+ Participant p = ((NewParticipantEvent) evt).getParticipant();
+ logger.info("A new participant had just joined: " + p.getCNAME());
+ }
+ }
+
+ /**
+ * Implements update from javax.media.rtp.ReceiveStreamListener
+ *
+ * @param evt received event
+ */
+ public synchronized void update(ReceiveStreamEvent evt)
+ {
+ Participant participant = evt.getParticipant(); // could be null.
+ ReceiveStream stream = evt.getReceiveStream(); // could be null.
+
+ if (evt instanceof RemotePayloadChangeEvent)
+ {
+ logger.warn("Received an RTP PayloadChangeEvent," +
+ " not supported cannot handle payload change.");
+ }
+ else if (evt instanceof NewReceiveStreamEvent)
+ {
+
+ try
+ {
+ stream = evt.getReceiveStream();
+ DataSource ds = stream.getDataSource();
+
+ // Find out the formats.
+ RTPControl ctl =
+ (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
+ if (ctl != null)
+ {
+ logger.info("Recevied new RTP stream: " + ctl.getFormat());
+ }
+ else
+ logger.info("Recevied new RTP stream");
+
+ if (participant == null)
+ logger.info("The sender of this stream" +
+ "had yet to be identified.");
+ else
+ {
+ logger.info("The stream comes from: " +
+ participant.getCNAME());
+ }
+
+ // create a player by passing datasource to the Media Manager
+ Player p = javax.media.Manager.createPlayer(ds);
+ if (p == null)
+ return;
+
+ p.addControllerListener(this);
+ p.realize();
+ }
+ catch (Exception e)
+ {
+ logger.warn("NewReceiveStreamEvent exception ", e);
+ return;
+ }
+
+ }
+ else if (evt instanceof StreamMappedEvent)
+ {
+
+ if (stream != null && stream.getDataSource() != null)
+ {
+ DataSource ds = stream.getDataSource();
+ // Find out the formats.
+ RTPControl ctl =
+ (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
+ logger.info("The previously unidentified stream ");
+ if (ctl != null)
+ logger.info(": " + ctl.getFormat());
+ logger.info(" had now been identified as sent by: "
+ + participant.getCNAME());
+ }
+ }
+ else if (evt instanceof ByeEvent)
+ {
+ logger.info("Got \"bye\" from: " + participant.getCNAME());
+ }
+
+ }
+
+ /**
+ * Implements controllerUpdate from javax.media.rtp.ControllerListener
+ *
+ * @param ce received event
+ */
+ public synchronized void controllerUpdate(ControllerEvent ce)
+ {
+
+ Player p = (Player) ce.getSourceController();
+
+ if (p == null)
+ return;
+
+ // Get this when the internal players are realized.
+ if (ce instanceof RealizeCompleteEvent)
+ {
+ p.start();
+ }
+
+ if (ce instanceof ControllerErrorEvent)
+ {
+ p.removeControllerListener(this);
+ logger.warn("Receiver internal error " + ce);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepository.java b/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepository.java
new file mode 100644
index 0000000..5d74b35
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepository.java
@@ -0,0 +1,187 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import net.java.sip.communicator.util.*;
+import java.util.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.*;
+import org.jivesoftware.smackx.jingle.*;
+
+/**
+ * Keeps a list of all calls currently active and maintained by this protocol
+ * provider. Offers methods for finding a call by its ID, participant session
+ * and others.
+ *
+ * @author Emil Ivov
+ */
+public class ActiveCallsRepository
+ implements CallChangeListener
+{
+ /**
+ * logger of this class
+ */
+ private static final Logger logger
+ = Logger.getLogger(ActiveCallsRepository.class);
+
+ /**
+ * The operation set that created us. Instance is mainly used for firing
+ * events when necessary.
+ */
+ private OperationSetBasicTelephonyJabberImpl parentOperationSet = null;
+
+ /**
+ * A table mapping call ids against call instances.
+ */
+ private Hashtable activeCalls = new Hashtable();
+
+ /**
+ * It's where we store all active calls
+ * @param opSet the <tt>OperationSetBasicTelphony</tt> instance which has
+ * been used to create calls in this repository
+ */
+ public ActiveCallsRepository(OperationSetBasicTelephonyJabberImpl opSet)
+ {
+ this.parentOperationSet = opSet;
+ }
+
+ /**
+ * Adds the specified call to the list of calls tracked by this repository.
+ * @param call CallJabberImpl
+ */
+ public void addCall(CallJabberImpl call)
+ {
+ activeCalls.put(call.getCallID(), call);
+ call.addCallChangeListener(this);
+ }
+
+ /**
+ * A dummy implementation of the CallChangeListener method that we don't
+ * use.
+ * @param evt unused.
+ */
+ public void callParticipantAdded(CallParticipantEvent evt)
+ {}
+
+ /**
+ * A dummy implementation of the CallChangeListener method that we don't
+ * use.
+ * @param evt unused.
+ */
+ public void callParticipantRemoved(CallParticipantEvent evt)
+ {}
+
+ /**
+ * If <tt>evt</tt> indicates that the call has been ended we remove it from
+ * the repository.
+ * @param evt the <tt>CallChangeEvent</tt> instance containing the source
+ * calls and its old and new state.
+ */
+ public void callStateChanged(CallChangeEvent evt)
+ {
+ if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
+ && ((CallState)evt.getNewValue()).equals(CallState.CALL_ENDED))
+ {
+ CallJabberImpl sourceCall = (CallJabberImpl)this.activeCalls
+ .remove(evt.getSourceCall().getCallID());
+
+ logger.trace( "Removing call " + sourceCall + " from the list of "
+ + "active calls because it entered an ENDED state");
+
+ this.parentOperationSet.fireCallEvent(
+ CallEvent.CALL_ENDED, sourceCall);
+ }
+ }
+
+ /**
+ * Returns an iterator over all currently active (non-ended) calls.
+ *
+ * @return an iterator over all currently active (non-ended) calls.
+ */
+ public Iterator getActiveCalls()
+ {
+ return new LinkedList(activeCalls.values()).iterator();
+ }
+
+ /**
+ * Returns the call that contains the specified session (i.e. it is
+ * established between us and one of the other call participants).
+ * <p>
+ * @param session the <tt>jingleSession</tt> whose containing call we're
+ * looking for.
+ * @return the <tt>CallJabberImpl</tt> containing <tt>session</tt> or null
+ * if no call contains the specified session.
+ */
+ public CallJabberImpl findCall(JingleSession session)
+ {
+ Iterator activeCalls = getActiveCalls();
+
+ if(session == null)
+ {
+ logger.debug("Cannot find a participant with a null session. "
+ +"Returning null");
+ return null;
+ }
+
+ if(logger.isTraceEnabled())
+ {
+ logger.trace("Looking for participant with session: " + session
+ + " among " + this.activeCalls.size() + " calls");
+ }
+
+
+ while(activeCalls.hasNext())
+ {
+ CallJabberImpl call = (CallJabberImpl)activeCalls.next();
+ if(call.contains(session))
+ return call;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the call participant whose associated jingle session matches
+ * <tt>session</tt>.
+ *
+ * @param session the jingle session whose corresponding participant we're
+ * looking for.
+ * @return the call participant whose jingle session is the same as the
+ * specified or null if no such call participant was found.
+ */
+ public CallParticipantJabberImpl findCallParticipant(JingleSession session)
+ {
+ Iterator activeCalls = getActiveCalls();
+
+ if(session == null)
+ {
+ logger.debug("Cannot find a participant with a null session. "
+ +"Returning null");
+ return null;
+ }
+
+ if(logger.isTraceEnabled())
+ {
+ logger.trace("Looking for participant with session: " + session
+ + " among " + this.activeCalls.size() + " calls");
+ }
+
+ while(activeCalls.hasNext())
+ {
+ CallJabberImpl call = (CallJabberImpl)activeCalls.next();
+ CallParticipantJabberImpl callParticipant
+ = call.findCallParticipant(session);
+ if(callParticipant != null)
+ {
+ logger.trace("Returning participant " + callParticipant);
+ return callParticipant;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
new file mode 100644
index 0000000..9f8e33a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
@@ -0,0 +1,292 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.service.media.*;
+import org.jivesoftware.smackx.jingle.*;
+
+/**
+ * A Jabber implementation of the Call abstract class encapsulating Jabber
+ * jingle sessions.
+ *
+ * @author Emil Ivov
+ */
+public class CallJabberImpl
+ extends Call
+ implements CallParticipantListener
+{
+ /**
+ * Logger of this class
+ */
+ private static final Logger logger = Logger.getLogger(CallJabberImpl.class);
+ /**
+ * A list containing all <tt>CallParticipant</tt>s of this call.
+ */
+ private Vector callParticipants = new Vector();
+
+ /**
+ * The state that this call is currently in.
+ */
+ private CallState callState = CallState.CALL_INITIALIZATION;
+
+ /**
+ * The <tt>CallSession</tt> that the media service has created for this
+ * call.
+ */
+ private CallSession mediaCallSession = null;
+
+ /**
+ * Crates a CallJabberImpl instance belonging to <tt>sourceProvider</tt> and
+ * initiated by <tt>CallCreator</tt>.
+ *
+ * @param sourceProvider the ProtocolProviderServiceJabberImpl instance in the
+ * context of which this call has been created.
+ */
+ protected CallJabberImpl(ProtocolProviderServiceJabberImpl sourceProvider)
+ {
+ super(sourceProvider);
+ }
+
+ /**
+ * Adds <tt>callParticipant</tt> to the list of participants in this call.
+ * If the call participant is already included in the call, the method has
+ * no effect.
+ *
+ * @param callParticipant the new <tt>CallParticipant</tt>
+ */
+ public void addCallParticipant(CallParticipantJabberImpl callParticipant)
+ {
+ if(callParticipants.contains(callParticipant))
+ return;
+
+ callParticipant.addCallParticipantListener(this);
+
+ this.callParticipants.add(callParticipant);
+ fireCallParticipantEvent(
+ callParticipant, CallParticipantEvent.CALL_PARTICIPANT_ADDED);
+ }
+
+ /**
+ * Removes <tt>callParticipant</tt> from the list of participants in this
+ * call. The method has no effect if there was no such participant in the
+ * call.
+ *
+ * @param callParticipant the <tt>CallParticipant</tt> leaving the call;
+ */
+ public void removeCallParticipant(CallParticipantJabberImpl callParticipant)
+ {
+ if(!callParticipants.contains(callParticipant))
+ return;
+
+ this.callParticipants.remove(callParticipant);
+ callParticipant.setCall(null);
+ callParticipant.removeCallParticipantListener(this);
+
+ fireCallParticipantEvent(
+ callParticipant, CallParticipantEvent.CALL_PARTICIPANT_REMVOVED);
+
+ if(callParticipants.size() == 0)
+ setCallState(CallState.CALL_ENDED);
+ }
+
+ /**
+ * Sets the state of this call and fires a call change event notifying
+ * registered listenres for the change.
+ *
+ * @param newState a reference to the <tt>CallState</tt> instance that
+ * the call is to enter.
+ */
+ public void setCallState(CallState newState)
+ {
+ CallState oldState = getCallState();
+
+ if(oldState == newState)
+ return;
+
+ this.callState = newState;
+
+ fireCallChangeEvent(
+ CallChangeEvent.CALL_STATE_CHANGE, oldState, newState);
+ }
+
+ /**
+ * Returns the state that this call is currently in.
+ *
+ * @return a reference to the <tt>CallState</tt> instance that the call is
+ * currently in.
+ */
+ public CallState getCallState()
+ {
+ return callState;
+ }
+
+ /**
+ * Returns an iterator over all call participants.
+ * @return an Iterator over all participants currently involved in the call.
+ */
+ public Iterator getCallParticipants()
+ {
+ return new LinkedList(callParticipants).iterator();
+ }
+
+ /**
+ * Returns the number of participants currently associated with this call.
+ * @return an <tt>int</tt> indicating the number of participants currently
+ * associated with this call.
+ */
+ public int getCallParticipantsCount()
+ {
+ return callParticipants.size();
+ }
+
+ /**
+ * Dummy implementation of a method (inherited from CallParticipantListener)
+ * that we don't need.
+ *
+ * @param evt unused.
+ */
+ public void participantImageChanged(CallParticipantChangeEvent evt)
+ {}
+
+ /**
+ * Dummy implementation of a method (inherited from CallParticipantListener)
+ * that we don't need.
+ *
+ * @param evt unused.
+ */
+ public void participantAddressChanged(CallParticipantChangeEvent evt)
+ {}
+
+ /**
+ * Dummy implementation of a method (inherited from CallParticipantListener)
+ * that we don't need.
+ *
+ * @param evt unused.
+ */
+ public void participantTransportAddressChanged(
+ CallParticipantChangeEvent evt)
+ {}
+
+
+ /**
+ * Dummy implementation of a method (inherited from CallParticipantListener)
+ * that we don't need.
+ *
+ * @param evt unused.
+ */
+ public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
+ {}
+
+ /**
+ * Verifies whether the call participant has entered a state.
+ *
+ * @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
+ * the source event as well as its previous and its new status.
+ */
+ public void participantStateChanged(CallParticipantChangeEvent evt)
+ {
+ if(((CallParticipantState)evt.getNewValue())
+ == CallParticipantState.DISCONNECTED
+ || ((CallParticipantState)evt.getNewValue())
+ == CallParticipantState.FAILED)
+ {
+ removeCallParticipant(
+ (CallParticipantJabberImpl)evt.getSourceCallParticipant());
+ }
+ else if (((CallParticipantState)evt.getNewValue())
+ == CallParticipantState.CONNECTED
+ && getCallState().equals(CallState.CALL_INITIALIZATION))
+ {
+ setCallState(CallState.CALL_IN_PROGRESS);
+ }
+ }
+
+ /**
+ * Returns <tt>true</tt> if <tt>session</tt> matches the jingle session
+ * established with one of the participants in this call.
+ *
+ * @param session the session whose corresponding participant we're looking
+ * for.
+ * @return true if this call contains a call participant whose jingleSession
+ * session is the same as the specified and false otherwise.
+ */
+ public boolean contains(JingleSession session)
+ {
+ return findCallParticipant(session) != null;
+ }
+
+ /**
+ * Returns the call participant whose associated jingle session matches
+ * <tt>session</tt>.
+ *
+ * @param session the jingle session whose corresponding participant we're
+ * looking for.
+ * @return the call participant whose jingle session is the same as the
+ * specified or null if no such call participant was found.
+ */
+ public CallParticipantJabberImpl findCallParticipant(JingleSession session)
+ {
+ Iterator callParticipants = this.getCallParticipants();
+
+ if(logger.isTraceEnabled())
+ {
+ logger.trace("Looking for participant with session: " + session
+ + "among " + this.callParticipants.size() + " calls");
+ }
+
+
+ while (callParticipants.hasNext())
+ {
+ CallParticipantJabberImpl cp
+ = (CallParticipantJabberImpl)callParticipants.next();
+
+ if( cp.getJingleSession() == session)
+ {
+ logger.trace("Returing cp="+cp);
+ return cp;
+ }
+ else
+ {
+ logger.trace("Ignoring cp="+cp
+ + " because cp.jingleSession="+cp.getJingleSession()
+ + " while session="+session);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the <tt>CallSession</tt> that the media service has created for this
+ * call.
+ *
+ * @param callSession the <tt>CallSession</tt> that the media service has
+ * created for this call.
+ */
+ public void setMediaCallSession(CallSession callSession)
+ {
+ this.mediaCallSession = callSession;
+ }
+
+ /**
+ * Sets the <tt>CallSession</tt> that the media service has created for this
+ * call.
+ *
+ * @return the <tt>CallSession</tt> that the media service has
+ * created for this call or null if no call session has been created so
+ * far.
+ */
+ public CallSession getMediaCallSession()
+ {
+ return this.mediaCallSession;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallParticipantJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallParticipantJabberImpl.java
new file mode 100644
index 0000000..ed1669a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallParticipantJabberImpl.java
@@ -0,0 +1,346 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+//import java.text.*;
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+import org.jivesoftware.smackx.jingle.*;
+
+/**
+ * Our Jabber implementation of the default CallParticipant;
+ *
+ * @author Emil Ivov
+ */
+public class CallParticipantJabberImpl
+ extends AbstractCallParticipant
+{
+ /**
+ * logger of this class
+ */
+ private static final Logger logger
+ = Logger.getLogger(CallParticipantJabberImpl.class);
+
+ /**
+ * The jabber address of this participant
+ */
+ private String participantAddress = null;
+
+ /**
+ * The state of the call participant.
+ */
+ protected CallParticipantState callParticipantState =
+ CallParticipantState.UNKNOWN;
+ /**
+ * Indicates the date when is call participant passed into its current state.
+ */
+ protected Date currentStateStartDate = new Date();
+
+ /**
+ * A byte array containing the image/photo representing the call participant.
+ */
+ private byte[] image;
+
+ /**
+ * A string uniquely identifying the participant.
+ */
+ private String participantID;
+
+ /**
+ * The call this participant belongs to.
+ */
+ private CallJabberImpl call;
+
+ /**
+ * The jingle session that has been created by the application for
+ * communication with this call participant.
+ */
+ private JingleSession jingleSession = null;
+
+ /**
+ * Creates a new call participant with address <tt>participantAddress</tt>.
+ *
+ * @param participantAddress the Jabber address of the new call
+ * participant.
+ * @param owningCall the call that contains this call participant.
+ */
+ public CallParticipantJabberImpl(String participantAddress,
+ CallJabberImpl owningCall)
+ {
+ this.participantAddress = participantAddress;
+ this.call = owningCall;
+ call.addCallParticipant(this);
+
+ //create the uid
+ this.participantID = String.valueOf( System.currentTimeMillis())
+ + String.valueOf(hashCode());
+ }
+
+ /**
+ * Returns a String locator for that participant.
+ *
+ * @return the participant's address or phone number.
+ */
+ public String getAddress()
+ {
+ return participantAddress;
+ }
+
+ /**
+ * Specifies the address, phone number, or other protocol specific
+ * identifier that represents this call participant. This method is to be
+ * used by service users and MUST NOT be called by the implementation.
+ *
+ * @param address The address of this call participant.
+ */
+ public void setAddress(String address)
+ {
+ String oldAddress = getAddress();
+
+ if(participantAddress.equals(address))
+ return;
+
+ this.participantAddress = address;
+ //Fire the Event
+ fireCallParticipantChangeEvent(
+ CallParticipantChangeEvent.CALL_PARTICIPANT_ADDRESS_CHANGE,
+ oldAddress,
+ address.toString());
+ }
+
+ /**
+ * Returns an object representing the current state of that participant.
+ *
+ * @return a CallParticipantState instance representing the participant's
+ * state.
+ */
+ public CallParticipantState getState()
+ {
+ return callParticipantState;
+ }
+
+ /**
+ * Causes this CallParticipant to enter the specified state. The method also
+ * sets the currentStateStartDate field and fires a
+ * CallParticipantChangeEvent.
+ *
+ * @param newState the state this call participant should enter.
+ * @param reason a string that could be set to contain a human readable
+ * explanation for the transition (particularly handy when moving into a
+ * FAILED state).
+ */
+ protected void setState(CallParticipantState newState, String reason)
+ {
+ CallParticipantState oldState = getState();
+
+ if(oldState == newState)
+ return;
+
+ this.callParticipantState = newState;
+ this.currentStateStartDate = new Date();
+ fireCallParticipantChangeEvent(
+ CallParticipantChangeEvent.CALL_PARTICIPANT_STATE_CHANGE,
+ oldState,
+ newState);
+ }
+
+ /**
+ * Causes this CallParticipant to enter the specified state. The method also
+ * sets the currentStateStartDate field and fires a
+ * CallParticipantChangeEvent.
+ *
+ * @param newState the state this call participant should enter.
+ */
+ protected void setState(CallParticipantState newState)
+ {
+ setState(newState, null);
+ }
+
+
+
+ /**
+ * Returns the date (time) when this call participant acquired its
+ * current status.
+ *
+ * @return a java.util.Date object containing the date when this call
+ * participant entered its current state.
+ */
+ public Date getCurrentStateStartDate()
+ {
+ return currentStateStartDate;
+ }
+
+ /**
+ * Returns a human readable name representing this participant.
+ *
+ * @return a String containing a name for that participant.
+ */
+ public String getDisplayName()
+ {
+ int atIndex = participantAddress.indexOf("@");
+ if (atIndex > 0) {
+ return participantAddress.substring(0, atIndex);
+ } else {
+ return participantAddress;
+ }
+ }
+
+ /**
+ * Sets a human readable name representing this participant.
+ *
+ * @param displayName the participant's display name
+ */
+ protected void setDisplayName(String displayName)
+ {
+ String oldName = getDisplayName();
+ /*try
+ {
+ //this.participantAddress.setDisplayName(displayName);
+ }
+ catch (ParseException ex)
+ {
+ //couldn't happen
+ logger.error(ex.getMessage(), ex);
+ throw new IllegalArgumentException(ex.getMessage());
+ }*/
+
+ //Fire the Event
+ fireCallParticipantChangeEvent(
+ CallParticipantChangeEvent.CALL_PARTICIPANT_DISPLAY_NAME_CHANGE,
+ oldName,
+ displayName);
+ }
+
+ /**
+ * The method returns an image representation of the call participant
+ * (e.g.
+ *
+ * @return byte[] a byte array containing the image or null if no image
+ * is available.
+ */
+ public byte[] getImage()
+ {
+ return image;
+ }
+
+ /**
+ * Sets the byte array containing an image representation (photo or picture)
+ * of the call participant.
+ *
+ * @param image a byte array containing the image
+ */
+ protected void setImage(byte[] image)
+ {
+ byte[] oldImage = getImage();
+ this.image = image;
+
+ //Fire the Event
+ fireCallParticipantChangeEvent(
+ CallParticipantChangeEvent.CALL_PARTICIPANT_IMAGE_CHANGE,
+ oldImage,
+ image);
+ }
+
+ /**
+ * Returns a unique identifier representing this participant.
+ *
+ * @return an identifier representing this call participant.
+ */
+ public String getParticipantID()
+ {
+ return participantID;
+ }
+
+ /**
+ * Returns the latest sdp description that this participant sent us.
+ * @return the latest sdp description that this participant sent us.
+ */
+ /*public String getSdpDescription()
+ {
+ return sdpDescription;
+ }*/
+
+ /**
+ * Sets the String that serves as a unique identifier of this
+ * CallParticipant.
+ * @param participantID the ID of this call participant.
+ */
+ protected void setParticipantID(String participantID)
+ {
+ this.participantID = participantID;
+ }
+
+ /**
+ * Returns a reference to the call that this participant belongs to. Calls
+ * are created by underlying telephony protocol implementations.
+ *
+ * @return a reference to the call containing this participant.
+ */
+ public Call getCall()
+ {
+ return call;
+ }
+
+ /**
+ * Sets the call containing this participant.
+ * @param call the call that this call participant is
+ * partdicipating in.
+ */
+ protected void setCall(CallJabberImpl call)
+ {
+ this.call = call;
+ }
+
+ /**
+ * Sets the jingle session that has been created by the application for
+ * communication with this call participant.
+ * @param session the jingle session that has been created by the
+ * application for this call.
+ */
+ public void setJingleSession(JingleSession session)
+ {
+ this.jingleSession = session;
+ }
+
+ /**
+ * Returns the jingle session that has been created by the application for
+ * communication with this call participant.
+ *
+ * @return the jingle session that has been created by the application for
+ * communication with this call participant.
+ */
+
+ public JingleSession getJingleSession()
+ {
+ return jingleSession;
+ }
+
+ /**
+ * Returns the protocol provider that this participant belongs to.
+ * @return a reference to the ProtocolProviderService that this participant
+ * belongs to.
+ */
+ public ProtocolProviderService getProtocolProvider()
+ {
+ return this.getCall().getProtocolProvider();
+ }
+
+ /**
+ * Returns the contact corresponding to this participant or null if no
+ * particular contact has been associated.
+ * <p>
+ * @return the <tt>Contact</tt> corresponding to this participant or null
+ * if no particular contact has been associated.
+ */
+ public Contact getContact()
+ {
+ return null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
index 0928a22..e42404b 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
@@ -1,3 +1,9 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
package net.java.sip.communicator.impl.protocol.jabber;
import java.util.*;
@@ -5,20 +11,41 @@ import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.media.*;
/**
* Loads the Jabber provider factory and registers it with service in the OSGI
* bundle context.
*
* @author Damian Minkov
+ * @author Symphorien Wanko
*/
public class JabberActivator
implements BundleActivator
{
- private ServiceRegistration jabberPpFactoryServReg = null;
- private static BundleContext bundleContext = null;
- private static ConfigurationService configurationService = null;
+ /**
+ * Service reference for the currently valid Jabber provider factory.
+ */
+ private ServiceRegistration jabberPpFactoryServReg = null;
+
+ /**
+ * Bundle context from OSGi.
+ */
+ private static BundleContext bundleContext = null;
+
+ /**
+ * Configuration service.
+ */
+ private static ConfigurationService configurationService = null;
+
+ /**
+ * Media service.
+ */
+ private static MediaService mediaService = null;
+ /**
+ * The jabber protocol provider factory.
+ */
private static ProtocolProviderFactoryJabberImpl jabberProviderFactory = null;
/**
@@ -92,6 +119,26 @@ public class JabberActivator
}
/**
+ * Returns a reference to a MediaService implementation currently registered
+ * in the bundle context or null if no such implementation was found.
+ *
+ * @return a reference to a MediaService implementation currently registered
+ * in the bundle context or null if no such implementation was found.
+ */
+ public static MediaService getMediaService()
+ {
+ if(mediaService == null)
+ {
+ ServiceReference mediaServiceReference
+ = bundleContext.getServiceReference(
+ MediaService.class.getName());
+ mediaService = (MediaService)bundleContext
+ .getService(mediaServiceReference);
+ }
+ return mediaService;
+ }
+
+ /**
* Called when this bundle is stopped so the Framework can perform the
* bundle-specific activities necessary to stop the bundle.
*
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
new file mode 100644
index 0000000..775d69a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
@@ -0,0 +1,720 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.text.*;
+import java.util.*;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smackx.jingle.*;
+import org.jivesoftware.smackx.jingle.media.*;
+import org.jivesoftware.smackx.jingle.listeners.*;
+import org.jivesoftware.smackx.jingle.nat.*;
+
+import net.java.sip.communicator.service.media.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.impl.protocol.jabber.mediamgr.*;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+
+/**
+ * Implements all call management logic and exports basic telephony support by
+ * implementing OperationSetBasicTelephony.
+ *
+ * @author Symphorien Wanko
+ */
+public class OperationSetBasicTelephonyJabberImpl
+ implements OperationSetBasicTelephony,
+ RegistrationStateChangeListener,
+ JingleMediaListener,
+ JingleTransportListener,
+ JingleSessionRequestListener,
+ CreatedJingleSessionListener,
+ JingleSessionStateListener,
+ JingleSessionListener
+{
+
+ /**
+ * the logger used by this class
+ */
+ private static final Logger logger
+ = Logger.getLogger(OperationSetBasicTelephonyJabberImpl.class);
+
+ /**
+ * A reference to the <tt>ProtocolProviderServiceJabberImpl</tt> instance
+ * that created us.
+ */
+ private ProtocolProviderServiceJabberImpl protocolProvider = null;
+
+ /**
+ * A list of listeners registered for call events.
+ */
+ private Vector callListeners = new Vector();
+
+ /**
+ * Contains references for all currently active (non ended) calls.
+ */
+ private ActiveCallsRepository activeCallsRepository
+ = new ActiveCallsRepository(this);
+
+ /**
+ * The manager we use to initiate, receive and ... manage jingle session.
+ */
+ private JingleManager jingleManager = null;
+
+ /**
+ * The transport manager is used by the <tt>jingleManager</tt> to handle transport
+ * method.
+ */
+ private JingleTransportManager transportManager = null;
+
+ /**
+ * The media manager is used by the <tt>jingleManager</tt> to handle media
+ * session.
+ */
+ private JingleMediaManager mediaManager = null;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param protocolProvider a reference to the
+ * <tt>ProtocolProviderServiceJabberImpl</tt> instance that created us.
+ */
+ public OperationSetBasicTelephonyJabberImpl(
+ ProtocolProviderServiceJabberImpl protocolProvider)
+ {
+
+ this.protocolProvider = protocolProvider;
+ protocolProvider.addRegistrationStateChangeListener(this);
+ transportManager = new BasicTransportManager();
+ mediaManager = new JingleScMediaManager();
+ }
+
+ /**
+ * Implementation of method <tt>registrationStateChange</tt> from
+ * interface RegistrationStateChangeListener for setting up (or down)
+ * our <tt>JingleManager</tt> when an <tt>XMPPConnection</tt> is available
+ *
+ * @param evt the event received
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent evt)
+ {
+ if ((evt.getNewState() == RegistrationState.REGISTERED))
+ {
+ // NOTE: a null stun server is provided here _intentionally_
+ // it may cause some exceptions, it's temporary
+ logger.warn("warning: we will use a null stun server ***");
+ transportManager = new ICETransportManager(
+ protocolProvider.getConnection(),
+ null, 3478);
+
+ jingleManager = new JingleManager(
+ protocolProvider.getConnection(),
+ transportManager,
+ mediaManager);
+
+ jingleManager.addCreationListener(this);
+ jingleManager.addJingleSessionRequestListener(this);
+ logger.info("Jingle : ON ");
+ }
+ else if ((evt.getNewState() == RegistrationState.UNREGISTERED))
+ {
+ if (jingleManager != null)
+ {
+ jingleManager.removeCreationListener(this);
+ jingleManager.removeJingleSessionRequestListener(this);
+ jingleManager = null;
+ logger.info("Jingle : OFF ");
+ }
+ }
+ }
+
+ /**
+ * Registers <tt>listener</tt> with this provider so that it
+ * could be notified when incoming calls are received.
+ *
+ * @param listener the listener to register with this provider.
+ */
+ public void addCallListener(CallListener listener)
+ {
+ synchronized(callListeners)
+ {
+ if (!callListeners.contains(listener))
+ callListeners.add(listener);
+ }
+ }
+
+ /**
+ * Create a new call and invite the specified CallParticipant to it.
+ *
+ * @param callee the jabber address of the callee that we should invite to a
+ * new call.
+ * @return CallParticipant the CallParticipant that will represented by
+ * the specified uri. All following state change events will be
+ * delivered through that call participant. The Call that this
+ * participant is a member of could be retrieved from the
+ * CallParticipatn instance with the use of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the call.
+ */
+ public Call createCall(String callee)
+ throws OperationFailedException
+ {
+
+ return createOutgoingCall(callee);
+ }
+
+ /**
+ * Create a new call and invite the specified CallParticipant to it.
+ *
+ * @param callee the address of the callee that we should invite to a
+ * new call.
+ * @return CallParticipant the CallParticipant that will represented by
+ * the specified uri. All following state change events will be
+ * delivered through that call participant. The Call that this
+ * participant is a member of could be retrieved from the
+ * CallParticipatn instance with the use of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the call.
+ */
+ public Call createCall(Contact callee)
+ throws OperationFailedException
+ {
+
+ return createOutgoingCall(callee.getAddress());
+ }
+
+ /**
+ * Init and establish the specified call.
+ *
+ * @param calleeAddress the address of the callee that we'd like to connect
+ * with.
+ *
+ * @return CallParticipant the CallParticipant that represented by
+ * the specified uri. All following state change events will be
+ * delivered through that call participant. The Call that this
+ * participant is a member of could be retrieved from the
+ * CallParticipatn instance with the use of the corresponding method.
+ *
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the call.
+ */
+ private CallJabberImpl createOutgoingCall(String calleeAddress)
+ throws OperationFailedException
+ {
+ OutgoingJingleSession outJS;
+
+ logger.info("creating outgoing call...");
+ if (protocolProvider.getConnection() == null) {
+ throw new OperationFailedException(
+ "Failed to create OutgoingJingleSession.\n"
+ + "we don't have a valid XMPPConnection."
+ , OperationFailedException.INTERNAL_ERROR);
+ }
+ String resource = protocolProvider.getConnection().
+ getRoster().getPresence(calleeAddress).getFrom();
+ String[] parts = resource.split("/");
+ if (parts.length != 2)
+ {
+ throw new OperationFailedException(
+ "Failed to create OutgoingJingleSession.\n"
+ + "user " + calleeAddress + " is unknwon to us."
+ , OperationFailedException.INTERNAL_ERROR);
+ }
+ resource = "/" + parts[1];
+ try
+ {
+ outJS = jingleManager.createOutgoingJingleSession(calleeAddress.
+ concat(resource));
+ }
+ catch (XMPPException ex)
+ {
+ throw new OperationFailedException(
+ "Failed to create OutgoingJingleSession.\n"
+ + "This is most probably a network connection error."
+ , OperationFailedException.INTERNAL_ERROR
+ , ex);
+ }
+ CallJabberImpl call = new CallJabberImpl(protocolProvider);
+ CallParticipantJabberImpl callParticipant =
+ new CallParticipantJabberImpl(calleeAddress, call);
+ callParticipant.setJingleSession(outJS);
+
+ callParticipant.setState(CallParticipantState.INITIATING_CALL);
+ fireCallEvent(CallEvent.CALL_INITIATED, call);
+
+ activeCallsRepository.addCall(call);
+ outJS.start();
+ return (CallJabberImpl) callParticipant.getCall();
+ }
+
+ /**
+ * Creates and dispatches a <tt>CallEvent</tt> notifying registered
+ * listeners that an event with id <tt>eventID</tt> has occurred on
+ * <tt>sourceCall</tt>.
+ *
+ * @param eventID the ID of the event to dispatch
+ * @param sourceCall the call on which the event has occurred.
+ */
+ protected void fireCallEvent( int eventID,
+ CallJabberImpl sourceCall)
+ {
+ CallEvent cEvent = new CallEvent(sourceCall, eventID);
+
+ logger.debug("Dispatching a CallEvent to "
+ + callListeners.size()
+ +" listeners. event is: " + cEvent.toString());
+ Iterator listeners = null;
+ synchronized(callListeners)
+ {
+ listeners = new ArrayList(callListeners).iterator();
+ }
+
+ while(listeners.hasNext())
+ {
+ CallListener listener = (CallListener)listeners.next();
+ if(eventID == CallEvent.CALL_INITIATED)
+ listener.outgoingCallCreated(cEvent);
+ else if(eventID == CallEvent.CALL_RECEIVED)
+ listener.incomingCallReceived(cEvent);
+ else if(eventID == CallEvent.CALL_ENDED)
+ listener.callEnded(cEvent);
+ }
+ }
+
+ /**
+ * Returns an iterator over all currently active calls.
+ *
+ * @return an iterator over all currently active calls.
+ */
+ public Iterator getActiveCalls()
+ {
+ return activeCallsRepository.getActiveCalls();
+ }
+
+ /**
+ * Resumes communication with a call participant previously put on hold.
+ *
+ * @param participant the call participant to put on hold.
+ */
+ public void putOffHold(CallParticipant participant)
+ {
+ /** @todo implement putOffHold() */
+ ((CallParticipantJabberImpl) participant).getJingleSession().
+ getJingleMediaSession().setTrasmit(true);
+ }
+
+ /**
+ * Puts the specified CallParticipant "on hold".
+ *
+ * @param participant the participant that we'd like to put on hold.
+ */
+ public void putOnHold(CallParticipant participant)
+ {
+ /** @todo implement putOnHold() */
+ ((CallParticipantJabberImpl) participant).getJingleSession().
+ getJingleMediaSession().setTrasmit(false);
+ }
+
+ /**
+ * Removes the <tt>listener</tt> from the list of call listeners.
+ *
+ * @param listener the listener to unregister.
+ */
+ public void removeCallListener(CallListener listener)
+ {
+ synchronized(callListeners)
+ {
+ callListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Implements method <tt>hangupCallParticipant</tt>
+ * from <tt>OperationSetBasicTelephony</tt>.
+ *
+ * @param participant the participant that we'd like to hang up on.
+ * @throws ClassCastException if participant is not an instance of
+ * CallParticipantJabberImpl.
+ *
+ * @throws OperationFailedException if we fail to terminate the call.
+ *
+ * // TODO: ask for suppression of OperationFailedException from the interface.
+ * // what happens if hangup fails ? are we forced to continue to talk ? :o)
+ */
+ public void hangupCallParticipant(CallParticipant participant)
+ throws ClassCastException, OperationFailedException
+ {
+ CallParticipantJabberImpl callParticipant
+ = (CallParticipantJabberImpl)participant;
+ try
+ {
+ callParticipant.getJingleSession().terminate();
+ }
+ catch (XMPPException ex)
+ {
+ ex.printStackTrace();
+ }
+ finally
+ {
+ callParticipant.setState(CallParticipantState.DISCONNECTED);
+ }
+ }
+
+
+
+ /**
+ * Implements method <tt>answerCallParticipant</tt>
+ * from <tt>OperationSetBasicTelephony</tt>.
+ *
+ * @param participant the call participant that we want to answer
+ * @throws OperationFailedException if we fails to answer
+ */
+ public void answerCallParticipant(CallParticipant participant)
+ throws OperationFailedException
+ {
+ CallParticipantJabberImpl callParticipant
+ = (CallParticipantJabberImpl)participant;
+ try
+ {
+ ((IncomingJingleSession)callParticipant.getJingleSession()).
+ start();
+ }
+ catch (XMPPException ex)
+ {
+ throw new OperationFailedException(
+ "Failed to answer an incoming call"
+ , OperationFailedException.INTERNAL_ERROR);
+ }
+ }
+
+ /**
+ * Closes all active calls. And releases resources.
+ */
+ public void shutdown()
+ {
+ logger.trace("Ending all active calls.");
+ Iterator activeCalls = this.activeCallsRepository.getActiveCalls();
+
+ // this is fast, but events aren't triggered ...
+ //jingleManager.disconnectAllSessions();
+
+ //go through all active calls.
+ while(activeCalls.hasNext())
+ {
+ CallJabberImpl call = (CallJabberImpl)activeCalls.next();
+
+ Iterator callParticipants = call.getCallParticipants();
+
+ //go through all call participants and say bye to every one.
+ while(callParticipants.hasNext())
+ {
+ CallParticipant participant
+ = (CallParticipant) callParticipants.next();
+ try
+ {
+ this.hangupCallParticipant(participant);
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Failed to properly hangup participant "
+ + participant
+ , ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * Implements method sessionRequested from JingleSessionRequestListener.
+ *
+ * @param jingleSessionRequest the session requested
+ */
+ public void sessionRequested(JingleSessionRequest jingleSessionRequest)
+ {
+ IncomingJingleSession inJS;
+ logger.info("session requested ");
+ try
+ {
+ inJS = jingleSessionRequest.accept();
+ }
+ catch (XMPPException ex)
+ {
+ logger.error("Failed to accept inoming jingle request : " + ex);
+ return;
+ }
+ CallJabberImpl call = new CallJabberImpl(protocolProvider);
+ String from = jingleSessionRequest.getFrom();
+ if (from.indexOf("/") > 0)
+ {
+ from = from.substring(0, from.indexOf("/"));
+ }
+ CallParticipantJabberImpl callParticipant =
+ new CallParticipantJabberImpl(from, call);
+ callParticipant.setJingleSession(inJS);
+ callParticipant.setState(CallParticipantState.INCOMING_CALL);
+ activeCallsRepository.addCall(call);
+ fireCallEvent(CallEvent.CALL_RECEIVED, call);
+ }
+
+ /**
+ * Implements method sessionCreated from CreatedJingleSessionListener.
+ *
+ * @param jingleSession the newly created jingle session
+ */
+ public void sessionCreated(JingleSession jingleSession)
+ {
+ logger.info("session created : " + jingleSession);
+ jingleSession.addListener(this);
+ jingleSession.addMediaListener(this);
+ jingleSession.addStateListener(this);
+ jingleSession.addTransportListener(this);
+ }
+
+ /**
+ * Implements method mediaClosed from JingleMediaListener.
+ *
+ * @param payloadType payload supported by the closed media
+ */
+ public void mediaClosed(PayloadType payloadType)
+ {
+ logger.info(" media losed ");
+ }
+
+ /**
+ * Implements method <tt>mediaEstablished</tt> from JingleMediaListener.
+ *
+ * @param payloadType payload used by the established media
+ */
+ public void mediaEstablished(PayloadType payloadType)
+ {
+ logger.info("media established ");
+ }
+
+ /**
+ * Implements method <tt>transportClosed</tt> from JingleTransportListener.
+ *
+ * @param transportCandidate <tt>transportCandiate</tt> with which
+ * we were dealing
+ */
+ public void transportClosed(TransportCandidate transportCandidate)
+ {
+ logger.info("transport closed\n");
+ }
+
+ /**
+ * Implements method <tt>transportClosedOnError</tt> from JingleTransportListener.
+ *
+ * @param ex the exception accompagning this error
+ */
+ public void transportClosedOnError(XMPPException ex)
+ {
+ logger.error("transport closed on error ", ex);
+ }
+
+ /**
+ * Implements method <tt>transportEstablished</tt> from JingleTransportListener.
+ *
+ * @param local local <tt>TransportCandidate</tt> for this transport link
+ * @param remote remote <tt>TransportCandidate</tt> for this transport link
+ */
+ public void transportEstablished(TransportCandidate local,
+ TransportCandidate remote)
+ {
+ logger.info("transport established " + local + " -:- " + remote);
+ }
+
+ /**
+ * Implements method <tt>beforeChange</tt> from JingleSessionStateListener.
+ * This method is called before the change occurs in the session.
+ * We can cancel the change by throwing a <tt>JingleException</tt>
+ *
+ * @param oldState old state of the session
+ * @param newState state in which we will go
+ *
+ * @throws JingleException we have the ability to cancel a state change by
+ * throwing a <tt>JingleException</tt>
+ */
+ public void beforeChange(JingleNegotiator.State oldState
+ , JingleNegotiator.State newState)
+ throws JingleNegotiator.JingleException
+ {
+ if (newState instanceof IncomingJingleSession.Accepting)
+ {}
+ else if (newState instanceof IncomingJingleSession.Pending)
+ {}
+ else if (newState instanceof IncomingJingleSession.Active)
+ {
+ JingleSession session = (JingleSession) newState.getNegotiator();
+ CallParticipantJabberImpl callParticipant =
+ activeCallsRepository.findCallParticipant(session);
+ if (callParticipant == null)
+ {
+ return;
+ }
+ callParticipant.setState(CallParticipantState.CONNECTED);
+ }
+ else if (newState instanceof OutgoingJingleSession.Inviting)
+ {
+ JingleSession session = (JingleSession) newState.getNegotiator();
+ CallParticipantJabberImpl callParticipant =
+ activeCallsRepository.findCallParticipant(session);
+ if (callParticipant == null)
+ {
+ return;
+ }
+ callParticipant.setState(CallParticipantState.CONNECTING);
+ }
+ else if (newState instanceof OutgoingJingleSession.Pending)
+ {
+ JingleSession session = (JingleSession) newState.getNegotiator();
+ CallParticipantJabberImpl callParticipant =
+ activeCallsRepository.findCallParticipant(session);
+ if (callParticipant == null)
+ {
+ return;
+ }
+ callParticipant.setState(CallParticipantState.ALERTING_REMOTE_SIDE);
+ }
+ else if (newState instanceof OutgoingJingleSession.Active)
+ {
+ JingleSession session = (JingleSession) newState.getNegotiator();
+ CallParticipantJabberImpl callParticipant =
+ activeCallsRepository.findCallParticipant(session);
+ if (callParticipant == null)
+ {
+ return;
+ }
+ callParticipant.setState(CallParticipantState.CONNECTED);
+ }
+// else if (newState instanceof MediaNegotiator.Inviting)
+// {}
+// else if (newState instanceof MediaNegotiator.Pending)
+// {}
+// else if (newState instanceof MediaNegotiator.Accepting)
+// {}
+// else if (newState instanceof MediaNegotiator.Active)
+// {}
+// else if (newState instanceof TransportNegotiator.Inviting)
+// {}
+// else if (newState instanceof TransportNegotiator.Pending)
+// {}
+// else if (newState instanceof TransportNegotiator.Accepting)
+// {}
+// else if (newState instanceof TransportNegotiator.Active)
+// {}
+
+ if ((newState == null) && (oldState != null))
+ { //hanging
+ JingleSession session = (JingleSession) oldState.getNegotiator();
+ CallParticipantJabberImpl callParticipant =
+ activeCallsRepository.findCallParticipant(session);
+ if (callParticipant == null)
+ {
+ logger.debug("Received a stray trying response.");
+ return;
+ }
+ try
+ {
+ hangupCallParticipant(callParticipant);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Implements method <tt>afterChanged</tt> from JingleSessionStateListener.
+ * called when we are effectivly in the <tt>newState</tt>
+ *
+ * @param oldState old session state
+ * @param newState new session state
+ */
+ public void afterChanged(JingleNegotiator.State oldState,
+ JingleNegotiator.State newState)
+ {
+ logger.info("session state changed : " + oldState + " => " + newState);
+ }
+
+ /**
+ * Implements <tt>sessionEstablished</tt> from <tt>JingleSessionListener</tt>
+ *
+ *
+ * @param payloadType the <tt>payloadType</tt> used for media in thi session
+ * @param remoteCandidate the remote end point of thi session
+ * @param localCandidate the local end point of this session
+ * @param jingleSession the session which is now fully established
+ */
+ public void sessionEstablished(PayloadType payloadType,
+ TransportCandidate remoteCandidate,
+ TransportCandidate localCandidate, JingleSession jingleSession)
+ {
+ logger.info("session established ");
+ }
+
+ /**
+ * Implements <tt>sessionDeclined</tt> from <tt>JingleSessionListener</tt>
+ *
+ * @param reason why the session has been declined
+ * @param jingleSession the declined session
+ */
+ public void sessionDeclined(String reason, JingleSession jingleSession)
+ {
+ logger.info("session declined : " + reason);
+ }
+
+ /**
+ * Implements <tt>sessionRedirected</tt> from <tt>JingleSessionListener</tt>
+ *
+ * @param redirection redirection information
+ * @param jingleSession the session which redirected
+ */
+ public void sessionRedirected(String redirection,
+ JingleSession jingleSession)
+ {
+ logger.info("session redirected : " + redirection);
+ }
+
+ /**
+ * Implements <tt>sessionClosed</tt> from <tt>JingleSessionListener</tt>
+ *
+ * @param reason why the session has been closed
+ * @param jingleSession the session which is closed
+ */
+ public void sessionClosed(String reason, JingleSession jingleSession)
+ {
+ logger.info("session closed : " + reason);
+ }
+
+ /**
+ * Implements <tt>sessionClosedOnError</tt> from <tt>JingleSessionListener</tt>
+ *
+ * @param ex execption which caused the error
+ * @param jingleSession the session which is closed
+ */
+ public void sessionClosedOnError(XMPPException ex,
+ JingleSession jingleSession)
+ {
+ logger.error("session closed on error ", ex);
+ }
+
+ /**
+ * Implements <tt>sessionMediaReceived</tt> from <tt>JingleSessionListener</tt>
+ *
+ * @param jingleSession the session where the media is established
+ * @param participant the participant for this media session
+ */
+ public void sessionMediaReceived(JingleSession jingleSession,
+ String participant)
+ {
+ logger.info("session media received ");
+ }
+}
+
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
index 2dae79d..5f51215 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
@@ -18,14 +18,22 @@ import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.util.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smackx.packet.*;
+
/**
* An implementation of the protocol provider service over the Jabber protocol
*
* @author Damian Minkov
+ * @author Symphorien Wanko
*/
public class ProtocolProviderServiceJabberImpl
implements ProtocolProviderService
{
+ /**
+ * Logger of this class
+ */
private static final Logger logger =
Logger.getLogger(ProtocolProviderServiceJabberImpl.class);
@@ -34,6 +42,9 @@ public class ProtocolProviderServiceJabberImpl
*/
private Hashtable supportedOperationSets = new Hashtable();
+ /**
+ * Used to connect to a XMPP server.
+ */
private XMPPConnection connection = null;
/**
@@ -62,6 +73,9 @@ public class ProtocolProviderServiceJabberImpl
*/
private SecurityAuthority authority = null;
+ /**
+ * True if we are reconnecting, false otherwise.
+ */
private boolean reconnecting = false;
/**
@@ -71,6 +85,26 @@ public class ProtocolProviderServiceJabberImpl
= new ProtocolIconJabberImpl();
/**
+ * A set of features supported by our Jabber implementation.
+ * In general, we add new feature(s) when we add new operation sets.
+ * (see xep-0030 : http://www.xmpp.org/extensions/xep-0030.html#info).
+ * Example : to tell the world that we support jingle, we simply have
+ * to do :
+ * supportedFeatures.add("http://www.xmpp.org/extensions/xep-0166.html#ns");
+ * Beware there is no canonical mapping between op set and jabber features
+ * (op set is a SC "concept"). This means that one op set in SC can
+ * correspond to many jabber features. It is also possible that there is no
+ * jabber feature corresponding to a SC op set or again,
+ * we can currently support some features wich do not have a specific
+ * op set in SC (the mandatory feature :
+ * http://jabber.org/protocol/disco#info is one example).
+ * We can find features corresponding to op set in the xep(s) related
+ * to implemented functionality.
+ */
+ private List supportedFeatures = new ArrayList();
+
+
+ /**
* Returns the state of the registration of this protocol provider
* @return the <tt>RegistrationState</tt> that this provider is
* currently in or null in case it is in a unknown state.
@@ -351,6 +385,55 @@ public class ProtocolProviderServiceJabberImpl
OperationFailedException.INVALID_ACCOUNT_PROPERTIES, ex);
}
}
+
+ // we listen for discovery info request and send an answer
+ // wich advetise all features supported by our jabber implementation
+ if (getRegistrationState() == RegistrationState.REGISTERED)
+ {
+ connection.addPacketListener( new PacketListener()
+ {
+ public void processPacket(Packet packet)
+ {
+ DiscoverInfo infos = new DiscoverInfo();
+
+ infos.setType(IQ.Type.RESULT);
+ infos.setFrom(packet.getTo());
+ infos.setTo(packet.getFrom());
+ infos.setPacketID(packet.getPacketID());
+
+ DiscoverInfo.Identity id
+ = new DiscoverInfo.Identity("account", "sc");
+ id.setType("registered");
+ infos.addIdentity(id);
+
+ Iterator it = supportedFeatures.iterator();
+ while (it.hasNext())
+ {
+ infos.addFeature((String) it.next());
+ }
+ connection.sendPacket(infos);
+ }
+ }, new PacketFilter()
+ {
+ public boolean accept(Packet packet)
+ {
+ // we are interested only for packets related to
+ // disovery info here.
+ String s = packet.toXML();
+ if (s.indexOf("type=\"get\"") != -1
+ && s.indexOf(
+ "http://jabber.org/protocol/disco#info") != -1)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ });
+ }
+
}
/**
@@ -423,7 +506,7 @@ public class ProtocolProviderServiceJabberImpl
* @param opsetClass the <tt>Class</tt> of the operation set that we're
* looking for.
* @return returns an OperationSet of the specified <tt>Class</tt> if the
- * undelying implementation supports it or null otherwise.
+ * underlying implementation supports it or null otherwise.
*/
public OperationSet getOperationSet(Class opsetClass)
{
@@ -433,7 +516,7 @@ public class ProtocolProviderServiceJabberImpl
/**
* Initialized the service implementation, and puts it in a sate where it
- * could interoperate with other services. It is strongly recomended that
+ * could interoperate with other services. It is strongly recommended that
* properties in this Map be mapped to property names as specified by
* <tt>AccountProperties</tt>.
*
@@ -451,6 +534,10 @@ public class ProtocolProviderServiceJabberImpl
{
this.accountID = accountID;
+ //this feature is mandatory to be compliant with Service Discovery
+ supportedFeatures.add("http://jabber.org/protocol/disco#info");
+
+
String keepAliveStrValue = (String)accountID.getAccountProperties().
get("SEND_KEEP_ALIVE");
@@ -465,11 +552,21 @@ public class ProtocolProviderServiceJabberImpl
{
persistentPresence.setResourcePriority(
new Integer(resourcePriority).intValue());
+ // TODO : is this resource priority related to xep-0168
+ // (Resource Application Priority) ?
+ // see http://www.xmpp.org/extensions/xep-0168.html
+ // If the answer is no, comment the following lines please
+ supportedFeatures.add(
+ "http://www.xmpp.org/extensions/xep-0168.html#ns");
}
-
+
supportedOperationSets.put(
OperationSetPersistentPresence.class.getName(),
persistentPresence);
+ // TODO: add the feature, if any, corresponding to persistent
+ // presence, if someone knows
+ // supportedFeatures.add(_PRESENCE_);
+
//register it once again for those that simply need presence
supportedOperationSets.put( OperationSetPresence.class.getName(),
@@ -488,6 +585,11 @@ public class ProtocolProviderServiceJabberImpl
OperationSetBasicInstantMessaging.class.getName(),
basicInstantMessaging);
+ // TODO: add the feature, if any, corresponding to IM if someone
+ // knows
+ // supportedFeatures.add(_IM_);
+
+
//initialize the typing notifications operation set
OperationSetTypingNotifications typingNotifications =
new OperationSetTypingNotificationsJabberImpl(this);
@@ -495,9 +597,10 @@ public class ProtocolProviderServiceJabberImpl
supportedOperationSets.put(
OperationSetTypingNotifications.class.getName(),
typingNotifications);
+ supportedFeatures.add("http://jabber.org/protocol/chatstates");
- //initialize the multi user chat operation set
+ //initialize the multi user chat operation set
OperationSetMultiUserChat multiUserChat =
new OperationSetMultiUserChatJabberImpl(this);
@@ -505,6 +608,29 @@ public class ProtocolProviderServiceJabberImpl
OperationSetMultiUserChat.class.getName(),
multiUserChat);
+ // TODO: this is the "main" feature to advertise when a client
+ // support muc. We have to add some features for
+ // specific functionnality we support in muc.
+ // see http://www.xmpp.org/extensions/xep-0045.html
+ supportedFeatures.add("http://jabber.org/protocol/muc");
+
+ //initialize the telephony opset
+ OperationSetBasicTelephony opSetBasicTelephony
+ = new OperationSetBasicTelephonyJabberImpl(this);
+
+ supportedOperationSets.put(
+ OperationSetBasicTelephony.class.getName(),
+ opSetBasicTelephony);
+
+ supportedFeatures.add(
+ "http://www.xmpp.org/extensions/xep-0166.html#ns");
+ // TODO: we have commented out this line since it breaks
+ // compatibility with spark and since spark is the only other
+ // client supporting jingle ... it's worth it. let's hope
+ // they fix it soon.'
+ //supportedFeatures.add(
+ // "http://www.xmpp.org/extensions/xep-0167.html#ns");
+
isInitialized = true;
}
}
@@ -515,9 +641,17 @@ public class ProtocolProviderServiceJabberImpl
* shutdown/garbage collection.
*/
public void shutdown()
- {
+ {
synchronized(initializationLock)
{
+ logger.trace("Killing the Jabber Protocol Provider.");
+
+ //kill all active calls
+ OperationSetBasicTelephonyJabberImpl telephony
+ = (OperationSetBasicTelephonyJabberImpl)getOperationSet(
+ OperationSetBasicTelephony.class);
+ telephony.shutdown();
+
if(connection != null)
{
connection.disconnect();
@@ -635,9 +769,15 @@ public class ProtocolProviderServiceJabberImpl
logger.trace("Done.");
}
+ /**
+ * Enable to listen for jabber connection events
+ */
private class JabberConnectionListener
implements ConnectionListener
{
+ /**
+ * Implements <tt>connectionClosed</tt> from <tt>ConnectionListener</tt>
+ */
public void connectionClosed()
{
OperationSetPersistentPresenceJabberImpl opSetPersPresence =
@@ -650,6 +790,12 @@ public class ProtocolProviderServiceJabberImpl
JabberStatusEnum.OFFLINE);
}
+ /**
+ * Implements <tt>connectionClosedOnError</tt> from
+ * <tt>ConnectionListener</tt>.
+ *
+ * @param exception contains information on the error.
+ */
public void connectionClosedOnError(Exception exception)
{
logger.error("connectionClosedOnError " +
@@ -661,16 +807,30 @@ public class ProtocolProviderServiceJabberImpl
reconnecting = false;
}
+ /**
+ * Implements <tt>reconnectingIn</tt> from <tt>ConnectionListener</tt>
+ *
+ * @param i delay in seconds for reconnection.
+ */
public void reconnectingIn(int i)
{
logger.info("reconnectingIn " + i);
}
+ /**
+ * Implements <tt>reconnectingIn</tt> from <tt>ConnectionListener</tt>
+ */
public void reconnectionSuccessful()
{
logger.info("reconnectionSuccessful");
}
+ /**
+ * Implements <tt>reconnectionFailed</tt> from
+ * <tt>ConnectionListener</tt>.
+ *
+ * @param exception description of the failure
+ */
public void reconnectionFailed(Exception exception)
{
logger.info("reconnectionFailed ", exception);
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
index 90f7d67..eae402c 100755
--- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
@@ -12,11 +12,18 @@ Import-Package: org.osgi.framework,
org.jivesoftware.smackx,
org.jivesoftware.smackx.muc,
org.jivesoftware.smackx.packet,
+ org.jivesoftware.smackx.jingle,
+ org.jivesoftware.smackx.jingle.listeners,
+ org.jivesoftware.smackx.jingle.media,
+ org.jivesoftware.smackx.jingle.nat,
+ org.jivesoftware.smackx.jingle.packet,
+ org.jivesoftware.smackx.jingle.provider,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.util,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.jabberconstants,
net.java.sip.communicator.service.protocol.event,
+ net.java.sip.communicator.service.media,
org.xmlpull.v1,
org.xmlpull.mxp1
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/AudioMediaSession.java b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/AudioMediaSession.java
new file mode 100644
index 0000000..b1857b1
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/AudioMediaSession.java
@@ -0,0 +1,218 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+package net.java.sip.communicator.impl.protocol.jabber.mediamgr;
+
+import net.java.sip.communicator.util.*;
+import org.jivesoftware.smackx.jingle.*;
+import org.jivesoftware.smackx.jingle.media.*;
+import org.jivesoftware.smackx.jingle.nat.*;
+
+import net.java.sip.communicator.service.media.*;
+import net.java.sip.communicator.impl.protocol.jabber.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * This Class implements a JingleMediaSession. which serve as a wrapper
+ * for a RtpFlow, allowing us to receive and transmit audio.
+ *
+ * based on Thiago Camargo's JingleScMediaManager from jingle
+ *
+ * @author Symphorien Wanko
+ */
+public class AudioMediaSession
+ extends JingleMediaSession
+{
+ /**
+ * the logger used by this class
+ */
+ private static final Logger logger
+ = Logger.getLogger(AudioMediaSession.class);
+
+ /**
+ * An audio media session encapsulate a RtpFlow which handle media transfer
+ */
+ private RtpFlow rtpFlow = null;
+
+ /**
+ * Creates an AudioMediaSession with defined payload type,
+ * remote and local candidates
+ *
+ * @param payloadType Payload used by jmf
+ * @param remote the remote information.
+ * @param local the local information.
+ * @param locator media locator
+ */
+ public AudioMediaSession(PayloadType payloadType, TransportCandidate remote
+ , TransportCandidate local)
+ {
+ this(payloadType, remote, local, null);
+ initialize();
+ }
+
+ /**
+ * This constructor is used only to match the one of our super class.
+ * In SC, An audioSession don't need the media locator asked by
+ * <tt>jingleMediaSession</tt> because the locator is handled
+ * by the media service.
+ *
+ * @param payloadType Payload used by jmf
+ * @param remote the remote information.
+ * @param local the local information.
+ * @param locator media locator
+ */
+ private AudioMediaSession(PayloadType payloadType, TransportCandidate remote
+ , TransportCandidate local, String locator)
+ {
+ //super(payloadType, remote, local, locator==null?"dsound://":locator, jingleSession);
+ super(payloadType, remote, local, locator);
+ }
+
+ /**
+ * Initialize the RtpFlow to make us able to send and receive audio media
+ */
+ public void initialize()
+ {
+ String remoteIp;
+ String localIp;
+ int localPort;
+ int remotePort;
+
+// if (getLocal().getSymmetric() != null)
+// {
+// remoteIp = getLocal().getIp();
+// localIp = getLocal().getLocalIp();
+// localPort = getFreePort();
+// remotePort = getLocal().getSymmetric().getPort();
+// }
+// else
+// {
+ if (getLocal().getSymmetric() != null)
+ {
+ logger.warn("oops : unexpected situation ... ");
+ // TODO : if this situation happens only one
+ // un comment the above code wich is meant to handle this
+ }
+
+ remoteIp = getRemote().getIp();
+ localIp = getLocal().getLocalIp();
+ localPort = getLocal().getPort();
+ remotePort = getRemote().getPort();
+ logger.info("AudioMediaSession : " + localIp + ":" + localPort +
+ " <-> " + remoteIp + ":" + remotePort);
+// }
+
+ Hashtable mediaEncoding = MediaUtils.getAudioEncoding(
+ getPayloadType().getId());
+ try
+ {
+ rtpFlow = JabberActivator.getMediaService().
+ createRtpFlow(
+ localIp, localPort,
+ remoteIp, remotePort,
+ mediaEncoding);
+ }
+ catch (MediaException ex)
+ {
+ logger.error("failed to create a RtpFlow between " +
+ localIp + ":" + localPort + " and " +
+ remoteIp + ":" + remotePort, ex);
+ rtpFlow = null;
+ throw new RuntimeException("failed to create a RtpFlow between "
+ + localIp + ":" + localPort + " and "
+ + remoteIp + ":" + remotePort,
+ ex);
+ }
+ }
+
+ /**
+ * Implements <tt>startTransmit</tt> from <tt>JingleMediaSession.</tt>
+ */
+ public void startTrasmit()
+ {
+ rtpFlow.start();
+ }
+
+ /**
+ * Implements <tt>startReceive</tt> from <tt>JingleMediaSession.</tt>
+ */
+ public void startReceive()
+ {}
+
+ /**
+ * Implements <tt>setTrasmit</tt> from <tt>JingleMediaSession.</tt>
+ *
+ * @param active pause the transmission if false, resume otherwise
+ */
+ public void setTrasmit(boolean active)
+ {
+ if (active)
+ rtpFlow.resume();
+ else
+ rtpFlow.pause();
+ }
+
+ /**
+ * Implements <tt>stopTrasmit</tt> from <tt>JingleMediaSession.</tt>
+ */
+ public void stopTrasmit()
+ {
+ //if (rtpFlow != null)
+ rtpFlow.stop();
+ }
+
+ /**
+ * Implements <tt>stopReceive</tt> from <tt>JingleMediaSession.</tt>
+ */
+ public void stopReceive()
+ {}
+
+// /**
+// * Obtain a free port we can use.
+// *
+// * @return A free port number.
+// */
+// protected int getFreePort()
+// {
+// //perhaps this method will be better in an utily class
+//
+// ServerSocket ss;
+// int freePort = 0;
+//
+// for (int i = 0; i < 10; i++)
+// {
+// freePort = (int) (10000 + Math.round(Math.random() * 10000));
+// freePort = freePort % 2 == 0 ? freePort : freePort + 1;
+// try
+// {
+// ss = new ServerSocket(freePort);
+// freePort = ss.getLocalPort();
+// ss.close();
+// return freePort;
+// }
+// catch (IOException e)
+// {
+// e.printStackTrace();
+// }
+// }
+// try
+// {
+// ss = new ServerSocket(0);
+// freePort = ss.getLocalPort();
+// ss.close();
+// }
+// catch (IOException e)
+// {
+// e.printStackTrace();
+// }
+//
+// return freePort;
+// }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/JingleScMediaManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/JingleScMediaManager.java
new file mode 100644
index 0000000..85b360f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/JingleScMediaManager.java
@@ -0,0 +1,100 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.mediamgr;
+
+import java.util.*;
+import net.java.sip.communicator.impl.protocol.jabber.*;
+import net.java.sip.communicator.util.*;
+
+import org.jivesoftware.smackx.jingle.*;
+import org.jivesoftware.smackx.jingle.media.*;
+import org.jivesoftware.smackx.jingle.nat.*;
+
+/**
+ * Implements a JingleMediaManager backed by JMF.
+ *
+ * based on Thiago Camargo's JingleScMediaManager from jingle
+ *
+ * @author Symphorien Wanko
+ */
+public class JingleScMediaManager extends JingleMediaManager
+{
+ /**
+ * Logger for this class
+ */
+ private Logger logger = Logger.getLogger(JingleScMediaManager.class);
+
+ /**
+ * List of payload that this media manager supports.
+ * This list is based on the one reported by the media service.
+ */
+ private List payloads = new ArrayList();
+
+ /**
+ * Creates a new instance of JingleScMediaManager
+ */
+ public JingleScMediaManager()
+ {
+ setupPayloads();
+ }
+
+ /**
+ * Returns a new jingleMediaSession
+ *
+ * @param payloadType payloadType
+ * @param remote remote Candidate
+ * @param local local Candidate
+ * @return JingleMediaSession
+ */
+ public JingleMediaSession createMediaSession(
+ PayloadType payloadType,
+ TransportCandidate remote,
+ TransportCandidate local)
+ {
+ return new AudioMediaSession(payloadType, remote, local);
+ }
+
+ /**
+ * Setup API supported Payloads
+ *
+ * http://tools.ietf.org/html/rfc3551#page-32 to view the
+ *corresponsdance between PayloadType and codec
+ */
+ private void setupPayloads() {
+
+ String[] audioEnc = JabberActivator.getMediaService().
+ getSupportedAudioEncodings();
+ for (int i = 0; i < audioEnc.length; i++)
+ {
+ int payloadType = Integer.parseInt(audioEnc[i]);
+ if (MediaUtils.getPayloadName(payloadType) != null)
+ {
+ payloads.add(new PayloadType.Audio(payloadType
+ , MediaUtils.getPayloadName(payloadType)));
+ }
+ else
+ {
+ payloads.add(new PayloadType.Audio(payloadType, audioEnc[i]));
+ }
+ }
+ if (payloads.isEmpty())
+ {
+ logger.warn("The list of payloads supported" +
+ " by JmfMediaManager is empty ");
+ }
+ }
+
+ /**
+ * Return all supported Payloads for this Manager
+ *
+ * @return The Payload List
+ */
+ public List getPayloads()
+ {
+ return payloads;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/MediaUtils.java b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/MediaUtils.java
new file mode 100644
index 0000000..9b17aac
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/mediamgr/MediaUtils.java
@@ -0,0 +1,168 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.mediamgr;
+
+import java.util.*;
+import javax.media.format.*;
+import javax.sdp.*;
+import net.java.sip.communicator.util.*;
+
+//
+// TODO: merge this MediaUtils with the one in impl/media
+// There is a MediaUtils class in package impl.media
+// which isn't exposed by the media service. It will
+// be a good idea to find a mean to merge this two
+// class somewhere
+//
+/**
+ * Media utils
+ * A collection of static methods related to media.
+ */
+public abstract class MediaUtils
+{
+
+ /**
+ * logger of this class
+ */
+ private static final Logger logger =
+ Logger.getLogger(MediaUtils.class);
+
+ /** Creates a new instance of MediaUtils */
+ public MediaUtils()
+ {
+ }
+
+ /**
+ * <tt>getAudioEncoding</tt> return an <tt>Hashtable</tt>
+ * with one entry in a format wich can be directly passed
+ * to the media service for creating a <tt>RtpFlow</tt>
+ * based on this encoding.
+ *
+ * @param payloadType the payload type
+ * @return an hashtable ready to be passed to media service
+ */
+ public static Hashtable getAudioEncoding(int payloadType)
+ {
+ Hashtable ht = new Hashtable();
+ List list = new ArrayList();
+ ht.put("audio", list);
+ // most of the format aren't handled by SC now
+ // but itsn't a problem. it will facilitates
+ // updates when more codecs will become available
+ // in SC.
+ switch (payloadType)
+ {
+ case SdpConstants.PCMU:
+ list.add(AudioFormat.ULAW_RTP);
+ return ht;
+ case SdpConstants.GSM:
+ list.add(AudioFormat.GSM_RTP);
+ return ht;
+ case SdpConstants.G723:
+ list.add(AudioFormat.G723_RTP);
+ return ht;
+ case SdpConstants.DVI4_8000:
+ list.add(AudioFormat.DVI_RTP);
+ return ht;
+ case SdpConstants.DVI4_16000:
+ list.add(AudioFormat.DVI_RTP);
+ return ht;
+ //case SdpConstants.LPC:
+ // list.add(AudioFormat...);
+ // return ht;
+ case SdpConstants.PCMA:
+ list.add(AudioFormat.ALAW);
+ return ht;
+ //case SdpConstants.G722:
+ // list.add(AudioFormat...);
+ // return ht;
+ //case SdpConstants.L16_1CH:
+ // list.add(AudioFormat...);
+ // return ht;
+ //case SdpConstants.L16_2CH:
+ // list.add(AudioFormat...);
+ // return ht;
+ //case SdpConstants.QCELP:
+ // list.add(AudioFormat...);
+ // return ht;
+ //case SdpConstants.CN:
+ // list.add(AudioFormat...);
+ // return ht;
+ //case SdpConstants.MPA:
+ // list.add(AudioFormat...);
+ // return ht;
+ case SdpConstants.G728:
+ list.add(AudioFormat.G728_RTP);
+ return ht;
+ case SdpConstants.DVI4_11025:
+ list.add(AudioFormat.DVI_RTP);
+ return ht;
+ case SdpConstants.DVI4_22050:
+ list.add(AudioFormat.DVI_RTP);
+ return ht;
+ case SdpConstants.G729:
+ list.add(AudioFormat.G729_RTP);
+ return ht;
+ default:
+ //throw new IllegalStateException("Unknown payload type");
+ logger.warn("unknown payload type : " + payloadType);
+ return null;
+ }
+ }
+
+ /**
+ * This method gives the name wich correspond to a payload type
+ *
+ * @param payloadType the type of the payload
+ * @return the string corresponding to the payload
+ */
+ public static String getPayloadName(int payloadType)
+ {
+ // for update, seee http://tools.ietf.org/html/rfc3551#page-32
+ switch (payloadType)
+ {
+ case SdpConstants.PCMU:
+ return "PCMU";
+ case SdpConstants.GSM:
+ return "GSM";
+ case SdpConstants.G723:
+ return "G723";
+ case SdpConstants.DVI4_8000:
+ return "DVI4_8000";
+ case SdpConstants.DVI4_16000:
+ return "DVI4_16000";
+ case SdpConstants.LPC:
+ return "LPC";
+ case SdpConstants.PCMA:
+ return "PCMA";
+ case SdpConstants.G722:
+ return "G722";
+ case SdpConstants.L16_1CH:
+ return "L16_1CH";
+ case SdpConstants.L16_2CH:
+ return "L16_2CH";
+ case SdpConstants.QCELP:
+ return "QCELP";
+ case SdpConstants.CN:
+ return "CN";
+ case SdpConstants.MPA:
+ return "MPA";
+ case SdpConstants.G728:
+ return "G728";
+ case SdpConstants.DVI4_11025:
+ return "DVI4_11025";
+ case SdpConstants.DVI4_22050:
+ return "DVI4_22050";
+ case SdpConstants.G729:
+ return "G729";
+ default:
+ //throw new IllegalStateException("Unknown payload type");
+ logger.warn("unknown payload type : " + payloadType);
+ return null;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/systray/jdic/SystrayServiceJdicImpl.java b/src/net/java/sip/communicator/impl/systray/jdic/SystrayServiceJdicImpl.java
index e24c20f..b656d22 100644
--- a/src/net/java/sip/communicator/impl/systray/jdic/SystrayServiceJdicImpl.java
+++ b/src/net/java/sip/communicator/impl/systray/jdic/SystrayServiceJdicImpl.java
@@ -118,7 +118,7 @@ public class SystrayServiceJdicImpl
public void actionPerformed(ActionEvent e)
{
UIService uiService = SystrayActivator.getUIService();
-
+
boolean isVisible;
isVisible = ! uiService.isVisible();
@@ -141,7 +141,7 @@ public class SystrayServiceJdicImpl
public void actionPerformed(ActionEvent e)
{
UIService uiService = SystrayActivator.getUIService();
-
+
firePopupMessageEvent(e.getSource());
ExportedWindow chatWindow
diff --git a/src/net/java/sip/communicator/service/media/MediaService.java b/src/net/java/sip/communicator/service/media/MediaService.java
index 2183951..0be0478 100644
--- a/src/net/java/sip/communicator/service/media/MediaService.java
+++ b/src/net/java/sip/communicator/service/media/MediaService.java
@@ -6,6 +6,7 @@
*/
package net.java.sip.communicator.service.media;
+import java.util.*;
import net.java.sip.communicator.service.media.event.*;
import net.java.sip.communicator.service.protocol.*;
import java.net.*;
@@ -15,12 +16,13 @@ import java.net.*;
* (J)FFMPEG, JMFPAPI, and others. It takes care of all media play and capture
* as well as media transport (e.g. over RTP).
*
- * Before being able to use this service calles would have to make sure that
+ * Before being able to use this service calls would have to make sure that
* it is initialized (i.e. consult the isInitialized() method).
*
* @author Emil Ivov
* @author Martin Andre
* @author Ryan Ricard
+ * @author Symphorien Wanko
*/
public interface MediaService
{
@@ -34,14 +36,14 @@ public interface MediaService
/**
* The name of the property that contains the minimum port number that we'd
- * like our rtp managers to bind upon.
+ * like our RTP managers to bind upon.
*/
public static final String MIN_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.media.MIN_PORT_NUMBER";
/**
* The name of the property that contains the maximum port number that we'd
- * like our rtp managers to bind upon.
+ * like our RTP managers to bind upon.
*/
public static final String MAX_PORT_NUMBER_PROPERTY_NAME
= "net.java.sip.communicator.service.media.MAX_PORT_NUMBER";
@@ -54,13 +56,31 @@ public interface MediaService
public static final int BIND_RETRIES_DEFAULT_VALUE = 50;
/**
+ * Give an array of Strings containing audio formats in the order of
+ * preference.
+ *
+ * @return an array of Strings containing audio formats in the order of
+ * preference.
+ */
+ public String[] getSupportedAudioEncodings();
+
+ /**
+ * Give an array of Strings containing video formats in the order of
+ * preference.
+ *
+ * @return an array of Strings containing video formats in the order of
+ * preference.
+ */
+ public String[] getSupportedVideoEncodings();
+
+ /**
* Creates a call session for <tt>call</tt>. The method allocates audio
* and video ports which won't be released until the corresponding call
* gets into a DISCONNECTED state. If a session already exists for call,
* it is returned and no new session is created. Once created a session
* follows the state changes of the call it encapsulates and automatically
* adapts to them by starting or stopping transmission and/or reception of
- * data. A CallSession would autodestroy when the <tt>Call</tt> it
+ * data. A CallSession would auto destroy when the <tt>Call</tt> it
* encapsulates enters the CALL_ENDED <tt>CallState</tt>.
* <p>
*
@@ -74,8 +94,29 @@ public interface MediaService
throws MediaException;
/**
+ * Create a RtpFlow which will manage media data transfer on the specified
+ * addresses and ports, using the specified codec.
+ *
+ * @param localIP local IP of this flow
+ * @param localPort local port of for this flow
+ * @param remoteIP remote IP of this flow
+ * @param remotePort remote port of for this flow
+ * @param mediaEncodings encoding used for media on this flow
+ * @return a <tt>RtpFlow</tt> with the corresponding parameters
+ * @throws MediaException throw a media exception if we fail to create the
+ * flow
+ */
+ public RtpFlow createRtpFlow(String localIP,
+ int localPort,
+ String remoteIP,
+ int remotePort,
+ Map mediaEncodings)
+ throws MediaException;
+
+ /**
* Adds a listener that will be listening for incoming media and changes
- * in the state of the media listener
+ * in the state of the media listener.
+ *
* @param listener the listener to register
*/
public void addMediaListener(MediaListener listener);
@@ -116,7 +157,7 @@ public interface MediaService
* it on the speakers/screen
*
* @param call the call whose data destination will be changed
- * @param dataSinkURL the URL of the new data Desintation
+ * @param dataSinkURL the URL of the new data sink.
*
* @throws MediaException if we fail to initialize the data sink
*/
@@ -136,7 +177,7 @@ public interface MediaService
* the given call. If the data source is not time-based, i.e. a microphone,
* the method returns -1.
*
- * @param call the call whose data source duration will be retreived
+ * @param call the call whose data source duration will be retrieved
* @return the duration of the data currently available in the <tt>call</tt>
* specific data source or -1 if we are using a microphone/webcam.
**/
@@ -144,8 +185,8 @@ public interface MediaService
/**
- * Returns true if the media service implementation is initialized and ready
- * for use by other services, and false otherwise.
+ * Returns true if the media service implementation is initialized and
+ * ready for use by other services, and false otherwise.
*
* @return true if the service implementation is initialized and ready for
* use and false otherwise.
diff --git a/src/net/java/sip/communicator/service/media/RtpFlow.java b/src/net/java/sip/communicator/service/media/RtpFlow.java
index 050dc48..6d56b84 100644
--- a/src/net/java/sip/communicator/service/media/RtpFlow.java
+++ b/src/net/java/sip/communicator/service/media/RtpFlow.java
@@ -9,10 +9,10 @@ package net.java.sip.communicator.service.media;
/**
* RtpFlow Interface.
*
- * The role of a RtpFlow is simply to handle media data
- * transfert between two end points
+ * The role of a RtpFlow is simply to handle media data transfer between two
+ * end points as well as playback and capture.
*
- * @author Symphorien Wanko-Tchuente
+ * @author Symphorien Wanko
*/
public interface RtpFlow
{
@@ -27,9 +27,40 @@ public interface RtpFlow
public void stop();
/**
- * Allow to pause or resume media data transmission
+ * Gives the local port used by this flow
*
- * @param active pause transmission if false. Otherwise, resume.
+ * @return the local port used by this flow
*/
- public void setTransmit(boolean active);
+ public int getLocalPort();
+
+ /**
+ * Gives the local address used by this flow
+ *
+ * @return the local address used by this flow
+ */
+ public String getLocalAddress();
+
+ /**
+ * Gives the remote port used by this flow
+ *
+ * @return the remote port used by this flow
+ */
+ public int getRemotePort();
+
+ /**
+ * Gives the remote address used by this flow
+ *
+ * @return the remote address used by this flow
+ */
+ public String getRemoteAddress();
+
+ /**
+ * Pause transmission on this flow
+ */
+ public void pause();
+
+ /**
+ * Resume transmission on this flow
+ */
+ public void resume();
}