diff --git a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
index 0eb8e2bc4..9ae84ffa8 100644
--- a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
@@ -286,12 +286,14 @@ public int getLastMeasuredAudioLevel(long ssrc)
}
/**
- * Delivers the audioLevels map to whoever's interested.
+ * Delivers the audioLevels map to whoever's interested. This
+ * method is meant for use primarily by the transform engine handling
+ * incoming RTP packets (currently CsrcTransformEngine).
*
- * @param audioLevels a bidimensional array mapping CSRC IDs to audio
+ * @param audioLevels a bi-dimensional array mapping CSRC IDs to audio
* levels.
*/
- private void fireConferenceAudioLevelEvent(final long[][] audioLevels)
+ public void fireConferenceAudioLevelEvent(final long[][] audioLevels)
{
if (this.csrcAudioLevelListener != null)
this.csrcAudioLevelListener.audioLevelsReceived(audioLevels);
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
index 6732fd685..5b4c18312 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
@@ -345,6 +345,9 @@ public void close()
engine.cleanup();
}
+ if(csrcEngine != null)
+ csrcEngine.stop();
+
rtpConnector.removeTargets();
rtpConnectorTarget = null;
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
index fb40d3278..ffe1b196b 100644
--- a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
+++ b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
@@ -6,6 +6,8 @@
*/
package net.java.sip.communicator.impl.neomedia.transform.csrc;
+import javax.media.*;
+
import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.transform.*;
@@ -43,6 +45,11 @@ public class CsrcTransformEngine
*/
private int extensionBuffLen = 0;
+ /**
+ * The dispatcher that is delivering audio levels to the media steam.
+ */
+ private CsrcAudioLevelDispatcher csrcLevelDispatcher = null;
+
/**
* Creates an engine instance that will be adding CSRC lists to the
* specified stream.
@@ -94,6 +101,22 @@ public PacketTransformer getRTPTransformer()
*/
public RawPacket reverseTransform(RawPacket pkt)
{
+ if (csrcAudioLevelExtID > 0)
+ {
+ //extract the audio levels and send them to the dispatcher.
+ long[][] levels = pkt.extractCsrcLevels(csrcAudioLevelExtID);
+
+ if(levels != null)
+ {
+ if (csrcLevelDispatcher == null)
+ csrcLevelDispatcher = new CsrcAudioLevelDispatcher();
+
+ if( ! csrcLevelDispatcher.isRunning )
+ new Thread(csrcLevelDispatcher).start();
+
+ csrcLevelDispatcher.addLevels(levels);
+ }
+ }
return pkt;
}
@@ -133,6 +156,15 @@ public synchronized RawPacket transform(RawPacket pkt)
return pkt;
}
+ /**
+ * Stops threads that this transform engine is using for even delivery.
+ */
+ public void stop()
+ {
+ if(csrcLevelDispatcher != null)
+ csrcLevelDispatcher.stop();
+ }
+
/**
* Sets the ID that this transformer should be using for audio level
* extensions or disables audio level extensions if extID is
@@ -199,4 +231,90 @@ private byte[] getExtensionBuff(int ensureCapacity)
return extensionBuff;
}
+ /**
+ * A simple thread that waits for new levels to be reported from incoming
+ * RTP packets and then delivers them to the AudioMediaStream
+ * associated with this engine. The reason we need to do this in a separate
+ * thread is of course the time sensitive nature of incoming RTP packets.
+ */
+ private class CsrcAudioLevelDispatcher
+ implements Runnable
+ {
+ /** Indicates whether this thread is supposed to be running */
+ private boolean isRunning = true;
+
+ /** The levels that we last received from the reverseTransform thread*/
+ private long[][] lastReportedLevels = null;
+
+ /**
+ * Waits for new levels to be reported via the addLevels()
+ * method and then delivers them to the AudioMediaStream that
+ * we are associated with.
+ */
+ public void run()
+ {
+ isRunning = true;
+
+ //no point in listening if our stream is not an audio one.
+ if(!(mediaStream instanceof AudioMediaStreamImpl))
+ return;
+
+ while(isRunning)
+ {
+ synchronized(this)
+ {
+ if(lastReportedLevels == null)
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException ie) {}
+ }
+ }
+
+ if(lastReportedLevels != null)
+ {
+ //now notify our listener
+ if (mediaStream != null)
+ {
+ ((AudioMediaStreamImpl)mediaStream)
+ .fireConferenceAudioLevelEvent(lastReportedLevels);
+ }
+ }
+ }
+ }
+
+ /**
+ * A level matrix that we should deliver to our media stream and
+ * its listeners in a separate thread.
+ *
+ * @param levels the levels that we'd like to queue for processing.
+ */
+ public void addLevels(long[][] levels)
+ {
+ synchronized(this)
+ {
+ this.lastReportedLevels = levels;
+
+ notifyAll();
+ }
+ }
+
+ /**
+ * Causes our run method to exit so that this thread would stop
+ * handling levels.
+ */
+ public void stop()
+ {
+ synchronized(this)
+ {
+ this.lastReportedLevels = null;
+ isRunning = false;
+
+ notifyAll();
+ }
+ }
+ }
+
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
index ec0662841..fd544f617 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
@@ -16,8 +16,7 @@
import javax.sip.message.*;
import net.java.sip.communicator.impl.protocol.sip.sdp.*;
-import net.java.sip.communicator.service.neomedia.event.ZrtpListener;
-import net.java.sip.communicator.service.neomedia.event.SimpleAudioLevelListener;
+import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
@@ -32,6 +31,7 @@ public class CallPeerSipImpl
extends AbstractCallPeer
implements SimpleAudioLevelListener,
CallPeerConferenceListener,
+ CsrcAudioLevelListener,
ZrtpListener
{
/**
@@ -129,6 +129,15 @@ public class CallPeerSipImpl
private List streamAudioLevelListeners
= new ArrayList();
+ /**
+ * Holds listeners registered for level changes in the audio of participants
+ * that this peer might be mixing and that we are not directly communicating
+ * with.
+ */
+ private List
+ conferenceMemberAudioLevelListeners
+ = new ArrayList();
+
/**
* Creates a new call peer with address peerAddress.
*
@@ -1742,8 +1751,21 @@ public void removeStreamSoundLevelListener(SoundLevelListener listener)
* @param listener the ConferenceMembersSoundLevelListener to add
*/
public void addConferenceMembersSoundLevelListener(
- ConferenceMembersSoundLevelListener listener)
+ ConferenceMembersSoundLevelListener listener)
{
+ synchronized (conferenceMemberAudioLevelListeners)
+ {
+
+ if (conferenceMemberAudioLevelListeners.size() == 0)
+ {
+ // if this is the first listener that's being registered with
+ // us, we also need to register ourselves as a CSRC audio level
+ // listener with the media handler.
+ getMediaHandler().setCsrcAudioLevelListener(this);
+ }
+
+ conferenceMemberAudioLevelListeners.add(listener);
+ }
}
/**
@@ -1757,6 +1779,31 @@ public void addConferenceMembersSoundLevelListener(
public void removeConferenceMembersSoundLevelListener(
ConferenceMembersSoundLevelListener listener)
{
+ synchronized (conferenceMemberAudioLevelListeners)
+ {
+ conferenceMemberAudioLevelListeners.remove(listener);
+
+ if (conferenceMemberAudioLevelListeners.size() == 0)
+ {
+ // if this was the last listener then we also remove ourselves
+ // as a CSRC audio level listener from the handler so that we
+ // don't have to create new events and maps for something no one
+ // is interested in.
+ getMediaHandler().setCsrcAudioLevelListener(null);
+ }
+ }
+ }
+
+ /**
+ * Implements {@link CsrcAudioLevelListener#audioLevelsReceived(long[][])}
+ * so that we could deliver to {@link ConferenceMembersSoundLevelListener}s
+ * the events corresponding to the audio level changes that are being
+ * reported here.
+ */
+ public void audioLevelsReceived(long[][] audioLevels)
+ {
+ // TODO Auto-generated method stub
+
}
/**