diff --git a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
index 240a6420c..e39402aa9 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
@@ -39,6 +39,12 @@ public class AudioMixerMediaDevice
*/
private final MediaDeviceImpl device;
+ /**
+ * The MediaDeviceSession of this AudioMixer with
+ * {@link #device}.
+ */
+ private AudioMixerMediaDeviceSession deviceSession;
+
/**
* Initializes a new AudioMixerMediaDevice instance which is to
* enable audio mixing on a specific MediaDeviceImpl.
@@ -83,9 +89,11 @@ AudioMixingPushBufferDataSource createOutputDataSource()
* @see AbstractMediaDevice#createSession()
*/
@Override
- public MediaDeviceSession createSession()
+ public synchronized MediaDeviceSession createSession()
{
- return new AudioMixerMediaDeviceSession();
+ if (deviceSession == null)
+ deviceSession = new AudioMixerMediaDeviceSession();
+ return new MediaStreamMediaDeviceSession(deviceSession);
}
/**
@@ -155,32 +163,59 @@ public List getSupportedFormats()
}
/**
- * Represents the MediaDeviceSession specific to one of the many
- * possible MediaStreams using this MediaDevice for audio
- * mixing.
+ * Represents the one and only MediaDeviceSession with the
+ * MediaDevice of this AudioMixer
*/
private class AudioMixerMediaDeviceSession
extends MediaDeviceSession
{
+ /**
+ * The list of MediaDeviceSessions of MediaStreams
+ * which use this AudioMixer.
+ */
+ private final List
+ mediaStreamMediaDeviceSessions
+ = new LinkedList();
+
/**
* Initializes a new AudioMixingMediaDeviceSession which is to
- * represent the MediaDeviceSession of one of the many possible
- * MediaStreams using this MediaDevice for audio
- * mixing.
+ * represent the MediaDeviceSession of this AudioMixer
+ * with its MediaDevice
*/
public AudioMixerMediaDeviceSession()
{
super(AudioMixerMediaDevice.this);
}
+ /**
+ * Adds a specific MediaStreamMediaDeviceSession to the mix
+ * represented by this instance so that it knows when it is in use.
+ *
+ * @param mediaStreamMediaDeviceSession the
+ * MediaStreamMediaDeviceSession to be added to the mix
+ * represented by this instance
+ */
+ void addMediaStreamMediaDeviceSession(
+ MediaStreamMediaDeviceSession mediaStreamMediaDeviceSession)
+ {
+ if (mediaStreamMediaDeviceSession == null)
+ throw new NullPointerException("mediaStreamMediaDeviceSession");
+
+ synchronized (mediaStreamMediaDeviceSessions)
+ {
+ if (!mediaStreamMediaDeviceSessions
+ .contains(mediaStreamMediaDeviceSession))
+ mediaStreamMediaDeviceSessions
+ .add(mediaStreamMediaDeviceSession);
+ }
+ }
+
/**
* Adds a ReceiveStream to this MediaDeviceSession to
* be played back on the associated MediaDevice and a specific
* DataSource is to be used to access its media data during the
- * playback. The DataSource is explicitly specified in order to
- * allow extenders to override the DataSource of the
- * ReceiveStream (e.g. create a clone of it).
+ * playback.
*
* @param receiveStream the ReceiveStream to be played back by
* this MediaDeviceSession on its associated
@@ -196,30 +231,168 @@ protected void addReceiveStream(
DataSource receiveStreamDataSource)
{
DataSource captureDevice = getCaptureDevice();
- AudioMixingPushBufferDataSource audioMixingDataSource;
DataSource receiveStreamDataSourceForPlayback;
if (captureDevice instanceof AudioMixingPushBufferDataSource)
- {
- audioMixingDataSource
- = (AudioMixingPushBufferDataSource) captureDevice;
receiveStreamDataSourceForPlayback
- = getAudioMixer().getLocalOutputDataSource();
- }
+ = (AudioMixingPushBufferDataSource) captureDevice;
else
- {
- audioMixingDataSource = null;
receiveStreamDataSourceForPlayback = receiveStreamDataSource;
- }
super
.addReceiveStream(
receiveStream,
receiveStreamDataSourceForPlayback);
+ }
+
+ /**
+ * Creates the DataSource that this instance is to read
+ * captured media from. Since this is the MediaDeviceSession of
+ * this AudioMixer with its MediaDevice, returns the
+ * localOutputDataSource of the AudioMixer i.e. the
+ * DataSource which represents the mix of all
+ * ReceiveStreams and excludes the captured data from the
+ * MediaDevice of the AudioMixer.
+ *
+ * @return the DataSource that this instance is to read
+ * captured media from
+ * @see MediaDeviceSession#createCaptureDevice()
+ */
+ @Override
+ protected DataSource createCaptureDevice()
+ {
+ return getAudioMixer().getLocalOutputDataSource();
+ }
+
+ /**
+ * Removes a specific MediaStreamMediaDeviceSession from the
+ * mix represented by this instance. When the last
+ * MediaStreamMediaDeviceSession is removed from this instance,
+ * it is no longer in use and closes itself thus signaling to its
+ * MediaDevice that it is no longer in use.
+ *
+ * @param mediaStreamMediaDeviceSession the
+ * MediaStreamMediaDeviceSession to be removed from the mix
+ * represented by this instance
+ */
+ void removeMediaStreamMediaDeviceSession(
+ MediaStreamMediaDeviceSession mediaStreamMediaDeviceSession)
+ {
+ if (mediaStreamMediaDeviceSession != null)
+ synchronized (mediaStreamMediaDeviceSessions)
+ {
+ if (mediaStreamMediaDeviceSessions
+ .remove(mediaStreamMediaDeviceSession)
+ && mediaStreamMediaDeviceSessions.isEmpty())
+ close();
+ }
+ }
+ }
+
+ /**
+ * Represents the work of a MediaStream with the
+ * MediaDevice of an AudioMixer and the contribution of
+ * that MediaStream to the mix.
+ */
+ private static class MediaStreamMediaDeviceSession
+ extends MediaDeviceSession
+ {
+
+ /**
+ * The MediaDeviceSession of the AudioMixer that this
+ * instance exposes to a MediaStream. While there are multiple
+ * MediaStreamMediaDeviceSessions each servicing a specific
+ * MediaStream, they all share and delegate to one and the same
+ * AudioMixerMediaDeviceSession so that they all contribute to
+ * the mix.
+ */
+ private final AudioMixerMediaDeviceSession audioMixerMediaDeviceSession;
+
+ /**
+ * Initializes a new MediaStreamMediaDeviceSession which is to
+ * represent the work of a MediaStream with the
+ * MediaDevice of this AudioMixer and its contribution
+ * to the mix.
+ *
+ * @param audioMixerMediaDeviceSession the MediaDeviceSession
+ * of the AudioMixer with its MediaDevice which the
+ * new instance is to delegate to in order to contribute to the mix
+ */
+ public MediaStreamMediaDeviceSession(
+ AudioMixerMediaDeviceSession audioMixerMediaDeviceSession)
+ {
+ super(audioMixerMediaDeviceSession.getDevice());
+
+ this.audioMixerMediaDeviceSession = audioMixerMediaDeviceSession;
+ this.audioMixerMediaDeviceSession
+ .addMediaStreamMediaDeviceSession(this);
+ }
+
+ /**
+ * Adds a ReceiveStream to this MediaDeviceSession to
+ * be played back on the associated MediaDevice and a specific
+ * DataSource is to be used to access its media data during the
+ * playback.
+ *
+ * @param receiveStream the ReceiveStream to be played back by
+ * this MediaDeviceSession on its associated
+ * MediaDevice
+ * @param receiveStreamDataSource the DataSource to be used for
+ * accessing the media data of receiveStream during its
+ * playback
+ * @see MediaDeviceSession#addReceiveStream(ReceiveStream, DataSource)
+ */
+ @Override
+ protected void addReceiveStream(
+ ReceiveStream receiveStream,
+ DataSource receiveStreamDataSource)
+ {
+ audioMixerMediaDeviceSession
+ .addReceiveStream(receiveStream, receiveStreamDataSource);
+
+ DataSource captureDevice = getCaptureDevice();
- if (audioMixingDataSource != null)
- audioMixingDataSource
+ if (captureDevice instanceof AudioMixingPushBufferDataSource)
+ ((AudioMixingPushBufferDataSource) captureDevice)
.addInputDataSource(receiveStreamDataSource);
}
+
+ /**
+ * Releases the resources allocated by this instance in the course of
+ * its execution and prepares it to be garbage collected.
+ *
+ * @see MediaDeviceSession#close()
+ */
+ @Override
+ public void close()
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ audioMixerMediaDeviceSession
+ .removeMediaStreamMediaDeviceSession(this);
+ }
+ }
+
+ /**
+ * Removes a ReceiveStream from this
+ * MediaDeviceSession so that it no longer plays back on the
+ * associated MediaDevice. Since this is the
+ * MediaDeviceSession of a MediaStream, removes the
+ * specified ReceiveStream from the mix.
+ *
+ * @param receiveStream the ReceiveStream to be removed from
+ * this MediaDeviceSession and playback on the associated
+ * MediaDevice
+ * @see MediaDeviceSession#removeReceiveStream(ReceiveStream)
+ */
+ @Override
+ public void removeReceiveStream(ReceiveStream receiveStream)
+ {
+ audioMixerMediaDeviceSession.removeReceiveStream(receiveStream);
+ }
}
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
index 30cf6b46c..5715860fa 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
@@ -253,7 +253,7 @@ protected void addReceiveStream(
// {
// logger.error("The processor does not support effects", ex);
// }
- // to use the processor as player we must se its
+ // to use the processor as player we must set its
// content descriptor to null
player.setContentDescriptor(null);
@@ -398,6 +398,18 @@ private void closeProcessor()
}
}
+ /**
+ * Creates the DataSource that this instance is to read captured
+ * media from.
+ *
+ * @return the DataSource that this instance is to read captured
+ * media from
+ */
+ protected DataSource createCaptureDevice()
+ {
+ return getDevice().createOutputDataSource();
+ }
+
/**
* Makes sure {@link #captureDevice} is disconnected.
*/
@@ -514,7 +526,7 @@ private static Format findFirstMatchingFormat(
protected DataSource getCaptureDevice()
{
if (captureDevice == null)
- captureDevice = getDevice().createOutputDataSource();
+ captureDevice = createCaptureDevice();
return captureDevice;
}