/*
* 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.neomedia;
import java.util.*;
import javax.media.*;
import net.java.sip.communicator.impl.neomedia.codec.*;
import net.java.sip.communicator.impl.neomedia.device.*;
import net.java.sip.communicator.impl.neomedia.format.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
/**
* Implements MediaService for JMF.
*
* @author Lubomir Marinov
*/
public class MediaServiceImpl
implements MediaService
{
/**
* With this property video support can be disabled (enabled by default).
*/
public static final String DISABLE_VIDEO_SUPPORT_PROPERTY_NAME
= "net.java.sip.communicator.service.media.DISABLE_VIDEO_SUPPORT";
/**
* The value of the devices property of MediaServiceImpl
* when no MediaDevices are available. Explicitly defined in order
* to reduce unnecessary allocations.
*/
private static final List EMPTY_DEVICES
= Collections.emptyList();
/**
* The CaptureDevice user choices such as the default audio and
* video capture devices.
*/
private final DeviceConfiguration deviceConfiguration
= new DeviceConfiguration();
/**
* The list of audio MediaDevices reported by this instance when
* its {@link MediaService#getDevices(MediaType)} method is called with an
* argument {@link MediaType#AUDIO}.
*/
private final List audioDevices
= new ArrayList();
/**
* The format-related user choices such as the enabled and disabled codecs
* and the order of their preference.
*/
private final EncodingConfiguration encodingConfiguration
= new EncodingConfiguration();
/**
* The MediaFormatFactory through which MediaFormat
* instances may be created for the purposes of working with the
* MediaStreams created by this MediaService.
*/
private MediaFormatFactory formatFactory;
/**
* The one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to AUDIO.
*/
private MediaDevice nonSendAudioDevice;
/**
* The one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to VIDEO.
*/
private MediaDevice nonSendVideoDevice;
/**
* The list of video MediaDevices reported by this instance when
* its {@link MediaService#getDevices(MediaType)} method is called with an
* argument {@link MediaType#VIDEO}.
*/
private final List videoDevices
= new ArrayList();
/**
* Creates a new MediaStream instance which will use the specified
* MediaDevice for both capture and playback of media exchanged
* via the specified StreamConnector.
*
* @param connector the StreamConnector that the new
* MediaStream instance is to use for sending and receiving media
* @param device the MediaDevice that the new MediaStream
* instance is to use for both capture and playback of media exchanged via
* the specified connector
* @return a new MediaStream instance
* @see MediaService#createMediaStream(StreamConnector, MediaDevice)
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaDevice device)
{
return createMediaStream(connector, device, null);
}
/**
* Creates a new MediaStream instance which will use the specified
* MediaDevice for both capture and playback of media exchanged
* via the specified StreamConnector.
*
* @param connector the StreamConnector that the new
* MediaStream instance is to use for sending and receiving media
* @param device the MediaDevice that the new MediaStream
* instance is to use for both capture and playback of media exchanged via
* the specified connector
* @param zrtpControl a control which is already created, used to control
* the zrtp operations.
*
* @return a new MediaStream instance
* @see MediaService#createMediaStream(StreamConnector, MediaDevice)
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaDevice device,
ZrtpControl zrtpControl)
{
switch (device.getMediaType())
{
case AUDIO:
return new AudioMediaStreamImpl(connector, device,
(ZrtpControlImpl)zrtpControl);
case VIDEO:
return new VideoMediaStreamImpl(connector, device,
(ZrtpControlImpl)zrtpControl);
default:
return null;
}
}
/**
* Creates a new MediaDevice which uses a specific
* MediaDevice to capture and play back media and performs mixing
* of the captured media and the media played back by any other users of the
* returned MediaDevice. For the AUDIO MediaType,
* the returned device is commonly referred to as an audio mixer. The
* MediaType of the returned MediaDevice is the same as
* the MediaType of the specified device.
*
* @param device the MediaDevice which is to be used by the
* returned MediaDevice to actually capture and play back media
* @return a new MediaDevice instance which uses device to
* capture and play back media and performs mixing of the captured media and
* the media played back by any other users of the returned
* MediaDevice instance
* @see MediaService#createMixer(MediaDevice)
*/
public MediaDevice createMixer(MediaDevice device)
{
if (MediaType.AUDIO.equals(device.getMediaType()))
return new AudioMixerMediaDevice((AudioMediaDeviceImpl) device);
/*
* TODO If we do not support mixing, should we return null or rather a
* MediaDevice with INACTIVE MediaDirection?
*/
return null;
}
/**
* Gets the default MediaDevice for the specified
* MediaType.
*
* @param mediaType a MediaType value indicating the type of media
* to be handled by the MediaDevice to be obtained
* @return the default MediaDevice for the specified
* mediaType if such a MediaDevice exists; otherwise,
* null
* @see MediaService#getDefaultDevice(MediaType)
*/
public MediaDevice getDefaultDevice(MediaType mediaType)
{
CaptureDeviceInfo captureDeviceInfo;
switch (mediaType)
{
case AUDIO:
captureDeviceInfo
= getDeviceConfiguration().getAudioCaptureDevice();
break;
case VIDEO:
captureDeviceInfo
= getDeviceConfiguration().getVideoCaptureDevice();
break;
default:
captureDeviceInfo = null;
break;
}
MediaDevice defaultDevice = null;
if (captureDeviceInfo != null)
{
for (MediaDevice device : getDevices(mediaType))
{
if ((device instanceof MediaDeviceImpl)
&& captureDeviceInfo.equals(((MediaDeviceImpl) device)
.getCaptureDeviceInfo()))
{
defaultDevice = device;
break;
}
}
}
if (defaultDevice == null)
switch (mediaType)
{
case AUDIO:
defaultDevice = getNonSendAudioDevice();
break;
case VIDEO:
defaultDevice = getNonSendVideoDevice();
break;
default:
/*
* There is no MediaDevice with direction which does not allow
* sending and mediaType other than AUDIO and VIDEO.
*/
break;
}
//Don't use the device in case the user has disabled all codecs for that
//kind of media.
if (defaultDevice != null
&& defaultDevice.getSupportedFormats().size() == 0)
{
defaultDevice = null;
}
return defaultDevice;
}
/**
* Gets the CaptureDevice user choices such as the default audio
* and video capture devices.
*
* @return the CaptureDevice user choices such as the default audio
* and video capture devices.
*/
public DeviceConfiguration getDeviceConfiguration()
{
return deviceConfiguration;
}
/**
* Gets a list of the MediaDevices known to this
* MediaService and handling the specified MediaType.
*
* @param mediaType the MediaType to obtain the
* MediaDevice list for
* @return a new List of MediaDevices known to this
* MediaService and handling the specified MediaType. The
* returned List is a copy of the internal storage and,
* consequently, modifications to it do not affect this instance. Despite
* the fact that a new List instance is returned by each call to
* this method, the MediaDevice instances are the same if they are
* still known to this MediaService to be available.
* @see MediaService#getDevices(MediaType)
*/
public List getDevices(MediaType mediaType)
{
CaptureDeviceInfo[] captureDeviceInfos;
List privateDevices;
switch (mediaType)
{
case AUDIO:
captureDeviceInfos
= getDeviceConfiguration().getAvailableAudioCaptureDevices();
privateDevices = audioDevices;
break;
case VIDEO:
captureDeviceInfos
= getDeviceConfiguration().getAvailableVideoCaptureDevices();
privateDevices = videoDevices;
break;
default:
/*
* MediaService does not understad MediaTypes other than AUDIO and
* VIDEO.
*/
return EMPTY_DEVICES;
}
List publicDevices = new ArrayList();
synchronized (privateDevices)
{
if ((captureDeviceInfos == null)
|| (captureDeviceInfos.length == 0))
privateDevices.clear();
else
{
Iterator deviceIter
= privateDevices.iterator();
while (deviceIter.hasNext())
{
CaptureDeviceInfo captureDeviceInfo
= deviceIter.next().getCaptureDeviceInfo();
boolean deviceIsFound = false;
for (int i = 0; i < captureDeviceInfos.length; i++)
if (captureDeviceInfo.equals(captureDeviceInfos[i]))
{
deviceIsFound = true;
captureDeviceInfos[i] = null;
break;
}
if (!deviceIsFound)
deviceIter.remove();
}
for (CaptureDeviceInfo captureDeviceInfo : captureDeviceInfos)
{
if (captureDeviceInfo == null)
continue;
MediaDeviceImpl device;
switch (mediaType)
{
case AUDIO:
device = new AudioMediaDeviceImpl(captureDeviceInfo);
break;
case VIDEO:
device
= new MediaDeviceImpl(captureDeviceInfo, mediaType);
break;
default:
device = null;
break;
}
if (device != null)
privateDevices.add(device);
}
}
publicDevices = new ArrayList(privateDevices);
}
/*
* If there are no MediaDevice instances of the specified mediaType,
* make sure that there is at least one MediaDevice which does not allow
* sending.
*/
if (publicDevices.isEmpty())
{
MediaDevice nonSendDevice;
switch (mediaType)
{
case AUDIO:
nonSendDevice = getNonSendAudioDevice();
break;
case VIDEO:
nonSendDevice = getNonSendVideoDevice();
break;
default:
/*
* There is no MediaDevice with direction not allowing sending
* and mediaType other than AUDIO and VIDEO.
*/
nonSendDevice = null;
break;
}
if (nonSendDevice != null)
publicDevices.add(nonSendDevice);
}
return publicDevices;
}
/**
* Gets the format-related user choices such as the enabled and disabled
* codecs and the order of their preference.
*
* @return the format-related user choices such as the enabled and disabled
* codecs and the order of their preference
*/
public EncodingConfiguration getEncodingConfiguration()
{
return encodingConfiguration;
}
/**
* Gets the MediaFormatFactory through which MediaFormat
* instances may be created for the purposes of working with the
* MediaStreams created by this MediaService.
*
* @return the MediaFormatFactory through which
* MediaFormat instances may be created for the purposes of working
* with the MediaStreams created by this MediaService
* @see MediaService#getFormatFactory()
*/
public MediaFormatFactory getFormatFactory()
{
if (formatFactory == null)
formatFactory = new MediaFormatFactoryImpl();
return formatFactory;
}
/**
* Gets the one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to AUDIO.
*
* @return the one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to AUDIO
*/
private MediaDevice getNonSendAudioDevice()
{
if (nonSendAudioDevice == null)
nonSendAudioDevice = new AudioMediaDeviceImpl();
return nonSendAudioDevice;
}
/**
* Gets the one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to VIDEO.
*
* @return the one and only MediaDevice instance with
* MediaDirection not allowing sending and MediaType equal
* to VIDEO
*/
private MediaDevice getNonSendVideoDevice()
{
if (nonSendVideoDevice == null)
nonSendVideoDevice = new MediaDeviceImpl(MediaType.VIDEO);
return nonSendVideoDevice;
}
/**
* Starts this MediaService implementation and thus makes it
* operational.
*/
void start()
{
deviceConfiguration.initialize();
encodingConfiguration.initializeFormatPreferences();
encodingConfiguration.registerCustomPackages();
encodingConfiguration.registerCustomCodecs();
}
/**
* Stops this MediaService implementation and thus signals that its
* utilization should cease.
*/
void stop()
{
}
/**
* Creates ZrtpControl used to control all zrtp options
* on particular stream.
*
* @return ZrtpControl instance.
*/
public ZrtpControl createZrtpControl()
{
return new ZrtpControlImpl();
}
/**
* Get available screens.
*
* @return screens
*/
public List getAvailableScreenDevices()
{
ScreenDevice screens[] = ScreenDeviceImpl.getAvailableScreenDevice();
List screenList;
if (screens != null)
{
screenList = new ArrayList(screens.length);
for (ScreenDevice screen : screens)
screenList.add(screen);
}
else
screenList = new ArrayList();
return screenList;
}
/**
* Get default screen device.
*
* @return default screen device
*/
public ScreenDevice getDefaultScreenDevice()
{
List screens = getAvailableScreenDevices();
int width = 0;
int height = 0;
ScreenDevice best = null;
for (ScreenDevice sc : screens)
{
java.awt.Dimension res = sc.getSize();
if ((res != null)
&& ((width < res.width) || (height < res.height)))
{
width = res.width;
height = res.height;
best = sc;
}
}
return best;
}
}