Fixes the audio mixing functionality to not break the media of the whole conference when a member leaves it.

cusax-fix
Lyubomir Marinov 16 years ago
parent 71d888ac99
commit 0a3b48d5f3

@ -39,6 +39,12 @@ public class AudioMixerMediaDevice
*/
private final MediaDeviceImpl device;
/**
* The <tt>MediaDeviceSession</tt> of this <tt>AudioMixer</tt> with
* {@link #device}.
*/
private AudioMixerMediaDeviceSession deviceSession;
/**
* Initializes a new <tt>AudioMixerMediaDevice</tt> instance which is to
* enable audio mixing on a specific <tt>MediaDeviceImpl</tt>.
@ -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<MediaFormat> getSupportedFormats()
}
/**
* Represents the <tt>MediaDeviceSession</tt> specific to one of the many
* possible <tt>MediaStream</tt>s using this <tt>MediaDevice</tt> for audio
* mixing.
* Represents the one and only <tt>MediaDeviceSession</tt> with the
* <tt>MediaDevice</tt> of this <tt>AudioMixer</tt>
*/
private class AudioMixerMediaDeviceSession
extends MediaDeviceSession
{
/**
* The list of <tt>MediaDeviceSession</tt>s of <tt>MediaStream</tt>s
* which use this <tt>AudioMixer</tt>.
*/
private final List<MediaStreamMediaDeviceSession>
mediaStreamMediaDeviceSessions
= new LinkedList<MediaStreamMediaDeviceSession>();
/**
* Initializes a new <tt>AudioMixingMediaDeviceSession</tt> which is to
* represent the <tt>MediaDeviceSession</tt> of one of the many possible
* <tt>MediaStream</tt>s using this <tt>MediaDevice</tt> for audio
* mixing.
* represent the <tt>MediaDeviceSession</tt> of this <tt>AudioMixer</tt>
* with its <tt>MediaDevice</tt>
*/
public AudioMixerMediaDeviceSession()
{
super(AudioMixerMediaDevice.this);
}
/**
* Adds a specific <tt>MediaStreamMediaDeviceSession</tt> to the mix
* represented by this instance so that it knows when it is in use.
*
* @param mediaStreamMediaDeviceSession the
* <tt>MediaStreamMediaDeviceSession</tt> 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 <tt>ReceiveStream</tt> to this <tt>MediaDeviceSession</tt> to
* be played back on the associated <tt>MediaDevice</tt> and a specific
* <tt>DataSource</tt> is to be used to access its media data during the
* playback. The <tt>DataSource</tt> is explicitly specified in order to
* allow extenders to override the <tt>DataSource</tt> of the
* <tt>ReceiveStream</tt> (e.g. create a clone of it).
* playback.
*
* @param receiveStream the <tt>ReceiveStream</tt> to be played back by
* this <tt>MediaDeviceSession</tt> 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 <tt>DataSource</tt> that this instance is to read
* captured media from. Since this is the <tt>MediaDeviceSession</tt> of
* this <tt>AudioMixer</tt> with its <tt>MediaDevice</tt>, returns the
* <tt>localOutputDataSource</tt> of the <tt>AudioMixer</tt> i.e. the
* <tt>DataSource</tt> which represents the mix of all
* <tt>ReceiveStream</tt>s and excludes the captured data from the
* <tt>MediaDevice</tt> of the <tt>AudioMixer</tt>.
*
* @return the <tt>DataSource</tt> that this instance is to read
* captured media from
* @see MediaDeviceSession#createCaptureDevice()
*/
@Override
protected DataSource createCaptureDevice()
{
return getAudioMixer().getLocalOutputDataSource();
}
/**
* Removes a specific <tt>MediaStreamMediaDeviceSession</tt> from the
* mix represented by this instance. When the last
* <tt>MediaStreamMediaDeviceSession</tt> is removed from this instance,
* it is no longer in use and closes itself thus signaling to its
* <tt>MediaDevice</tt> that it is no longer in use.
*
* @param mediaStreamMediaDeviceSession the
* <tt>MediaStreamMediaDeviceSession</tt> 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 <tt>MediaStream</tt> with the
* <tt>MediaDevice</tt> of an <tt>AudioMixer</tt> and the contribution of
* that <tt>MediaStream</tt> to the mix.
*/
private static class MediaStreamMediaDeviceSession
extends MediaDeviceSession
{
/**
* The <tt>MediaDeviceSession</tt> of the <tt>AudioMixer</tt> that this
* instance exposes to a <tt>MediaStream</tt>. While there are multiple
* <tt>MediaStreamMediaDeviceSession<tt>s each servicing a specific
* <tt>MediaStream</tt>, they all share and delegate to one and the same
* <tt>AudioMixerMediaDeviceSession</tt> so that they all contribute to
* the mix.
*/
private final AudioMixerMediaDeviceSession audioMixerMediaDeviceSession;
/**
* Initializes a new <tt>MediaStreamMediaDeviceSession</tt> which is to
* represent the work of a <tt>MediaStream</tt> with the
* <tt>MediaDevice</tt> of this <tt>AudioMixer</tt> and its contribution
* to the mix.
*
* @param audioMixerMediaDeviceSession the <tt>MediaDeviceSession</tt>
* of the <tt>AudioMixer</tt> with its <tt>MediaDevice</tt> 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 <tt>ReceiveStream</tt> to this <tt>MediaDeviceSession</tt> to
* be played back on the associated <tt>MediaDevice</tt> and a specific
* <tt>DataSource</tt> is to be used to access its media data during the
* playback.
*
* @param receiveStream the <tt>ReceiveStream</tt> to be played back by
* this <tt>MediaDeviceSession</tt> on its associated
* <tt>MediaDevice</tt>
* @param receiveStreamDataSource the <tt>DataSource</tt> to be used for
* accessing the media data of <tt>receiveStream</tt> 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 <tt>ReceiveStream</tt> from this
* <tt>MediaDeviceSession</tt> so that it no longer plays back on the
* associated <tt>MediaDevice</tt>. Since this is the
* <tt>MediaDeviceSession</tt> of a <tt>MediaStream</tt>, removes the
* specified <tt>ReceiveStream</tt> from the mix.
*
* @param receiveStream the <tt>ReceiveStream</tt> to be removed from
* this <tt>MediaDeviceSession</tt> and playback on the associated
* <tt>MediaDevice</tt>
* @see MediaDeviceSession#removeReceiveStream(ReceiveStream)
*/
@Override
public void removeReceiveStream(ReceiveStream receiveStream)
{
audioMixerMediaDeviceSession.removeReceiveStream(receiveStream);
}
}
}

@ -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 <tt>DataSource</tt> that this instance is to read captured
* media from.
*
* @return the <tt>DataSource</tt> 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;
}

Loading…
Cancel
Save