/* * 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; } }