You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jitsi/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java

528 lines
18 KiB

/*
* 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 <tt>MediaService</tt> 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 <tt>devices</tt> property of <tt>MediaServiceImpl</tt>
* when no <tt>MediaDevice</tt>s are available. Explicitly defined in order
* to reduce unnecessary allocations.
*/
private static final List<MediaDevice> EMPTY_DEVICES
= Collections.emptyList();
/**
* The <tt>CaptureDevice</tt> user choices such as the default audio and
* video capture devices.
*/
private final DeviceConfiguration deviceConfiguration
= new DeviceConfiguration();
/**
* The list of audio <tt>MediaDevice</tt>s reported by this instance when
* its {@link MediaService#getDevices(MediaType)} method is called with an
* argument {@link MediaType#AUDIO}.
*/
private final List<MediaDeviceImpl> audioDevices
= new ArrayList<MediaDeviceImpl>();
/**
* 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 <tt>MediaFormatFactory</tt> through which <tt>MediaFormat</tt>
* instances may be created for the purposes of working with the
* <tt>MediaStream</tt>s created by this <tt>MediaService</tt>.
*/
private MediaFormatFactory formatFactory;
/**
* The one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>AUDIO</tt>.
*/
private MediaDevice nonSendAudioDevice;
/**
* The one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>VIDEO</tt>.
*/
private MediaDevice nonSendVideoDevice;
/**
* The list of video <tt>MediaDevice</tt>s reported by this instance when
* its {@link MediaService#getDevices(MediaType)} method is called with an
* argument {@link MediaType#VIDEO}.
*/
private final List<MediaDeviceImpl> videoDevices
= new ArrayList<MediaDeviceImpl>();
/**
* Creates a new <tt>MediaStream</tt> instance which will use the specified
* <tt>MediaDevice</tt> for both capture and playback of media exchanged
* via the specified <tt>StreamConnector</tt>.
*
* @param connector the <tt>StreamConnector</tt> that the new
* <tt>MediaStream</tt> instance is to use for sending and receiving media
* @param device the <tt>MediaDevice</tt> that the new <tt>MediaStream</tt>
* instance is to use for both capture and playback of media exchanged via
* the specified <tt>connector</tt>
* @return a new <tt>MediaStream</tt> instance
* @see MediaService#createMediaStream(StreamConnector, MediaDevice)
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaDevice device)
{
return createMediaStream(connector, device, null);
}
/**
* Creates a new <tt>MediaStream</tt> instance which will use the specified
* <tt>MediaDevice</tt> for both capture and playback of media exchanged
* via the specified <tt>StreamConnector</tt>.
*
* @param connector the <tt>StreamConnector</tt> that the new
* <tt>MediaStream</tt> instance is to use for sending and receiving media
* @param device the <tt>MediaDevice</tt> that the new <tt>MediaStream</tt>
* instance is to use for both capture and playback of media exchanged via
* the specified <tt>connector</tt>
* @param zrtpControl a control which is already created, used to control
* the zrtp operations.
*
* @return a new <tt>MediaStream</tt> 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 <tt>MediaDevice</tt> which uses a specific
* <tt>MediaDevice</tt> 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 <tt>MediaDevice</tt>. For the <tt>AUDIO</tt> <tt>MediaType</tt>,
* the returned device is commonly referred to as an audio mixer. The
* <tt>MediaType</tt> of the returned <tt>MediaDevice</tt> is the same as
* the <tt>MediaType</tt> of the specified <tt>device</tt>.
*
* @param device the <tt>MediaDevice</tt> which is to be used by the
* returned <tt>MediaDevice</tt> to actually capture and play back media
* @return a new <tt>MediaDevice</tt> instance which uses <tt>device</tt> 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
* <tt>MediaDevice</tt> 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 <tt>MediaDevice</tt> for the specified
* <tt>MediaType</tt>.
*
* @param mediaType a <tt>MediaType</tt> value indicating the type of media
* to be handled by the <tt>MediaDevice</tt> to be obtained
* @return the default <tt>MediaDevice</tt> for the specified
* <tt>mediaType</tt> if such a <tt>MediaDevice</tt> exists; otherwise,
* <tt>null</tt>
* @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 <tt>CaptureDevice</tt> user choices such as the default audio
* and video capture devices.
*
* @return the <tt>CaptureDevice</tt> user choices such as the default audio
* and video capture devices.
*/
public DeviceConfiguration getDeviceConfiguration()
{
return deviceConfiguration;
}
/**
* Gets a list of the <tt>MediaDevice</tt>s known to this
* <tt>MediaService</tt> and handling the specified <tt>MediaType</tt>.
*
* @param mediaType the <tt>MediaType</tt> to obtain the
* <tt>MediaDevice</tt> list for
* @return a new <tt>List</tt> of <tt>MediaDevice</tt>s known to this
* <tt>MediaService</tt> and handling the specified <tt>MediaType</tt>. The
* returned <tt>List</tt> is a copy of the internal storage and,
* consequently, modifications to it do not affect this instance. Despite
* the fact that a new <tt>List</tt> instance is returned by each call to
* this method, the <tt>MediaDevice</tt> instances are the same if they are
* still known to this <tt>MediaService</tt> to be available.
* @see MediaService#getDevices(MediaType)
*/
public List<MediaDevice> getDevices(MediaType mediaType)
{
CaptureDeviceInfo[] captureDeviceInfos;
List<MediaDeviceImpl> 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<MediaDevice> publicDevices = new ArrayList<MediaDevice>();
synchronized (privateDevices)
{
if ((captureDeviceInfos == null)
|| (captureDeviceInfos.length == 0))
privateDevices.clear();
else
{
Iterator<MediaDeviceImpl> 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<MediaDevice>(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 <tt>MediaFormatFactory</tt> through which <tt>MediaFormat</tt>
* instances may be created for the purposes of working with the
* <tt>MediaStream</tt>s created by this <tt>MediaService</tt>.
*
* @return the <tt>MediaFormatFactory</tt> through which
* <tt>MediaFormat</tt> instances may be created for the purposes of working
* with the <tt>MediaStream</tt>s created by this <tt>MediaService</tt>
* @see MediaService#getFormatFactory()
*/
public MediaFormatFactory getFormatFactory()
{
if (formatFactory == null)
formatFactory = new MediaFormatFactoryImpl();
return formatFactory;
}
/**
* Gets the one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>AUDIO</tt>.
*
* @return the one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>AUDIO</tt>
*/
private MediaDevice getNonSendAudioDevice()
{
if (nonSendAudioDevice == null)
nonSendAudioDevice = new AudioMediaDeviceImpl();
return nonSendAudioDevice;
}
/**
* Gets the one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>VIDEO</tt>.
*
* @return the one and only <tt>MediaDevice</tt> instance with
* <tt>MediaDirection</tt> not allowing sending and <tt>MediaType</tt> equal
* to <tt>VIDEO</tt>
*/
private MediaDevice getNonSendVideoDevice()
{
if (nonSendVideoDevice == null)
nonSendVideoDevice = new MediaDeviceImpl(MediaType.VIDEO);
return nonSendVideoDevice;
}
/**
* Starts this <tt>MediaService</tt> implementation and thus makes it
* operational.
*/
void start()
{
deviceConfiguration.initialize();
encodingConfiguration.initializeFormatPreferences();
encodingConfiguration.registerCustomPackages();
encodingConfiguration.registerCustomCodecs();
}
/**
* Stops this <tt>MediaService</tt> implementation and thus signals that its
* utilization should cease.
*/
void stop()
{
}
/**
* Creates <tt>ZrtpControl</tt> 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<ScreenDevice> getAvailableScreenDevices()
{
ScreenDevice screens[] = ScreenDeviceImpl.getAvailableScreenDevice();
List<ScreenDevice> screenList;
if (screens != null)
{
screenList = new ArrayList<ScreenDevice>(screens.length);
for (ScreenDevice screen : screens)
screenList.add(screen);
}
else
screenList = new ArrayList<ScreenDevice>();
return screenList;
}
/**
* Get default screen device.
*
* @return default screen device
*/
public ScreenDevice getDefaultScreenDevice()
{
List<ScreenDevice> 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;
}
}