diff --git a/build.xml b/build.xml index afd43f372..44f2ddba5 100644 --- a/build.xml +++ b/build.xml @@ -777,7 +777,7 @@ bundle-fileaccess-slick,bundle-media,bundle-media-slick, bundle-resource-manager,bundle-resources-defaultpack, bundle-protocol,bundle-icq,bundle-icq-slick,bundle-mock,bundle-smacklib, - bundle-jabber,bundle-jabber-slick,bundle-swing-ui,bundle-ui-service, + bundle-jabber,bundle-jabber-slick,bundle-swing-common,bundle-swing-ui,bundle-ui-service, bundle-msn,bundle-msn-slick,bundle-yahoo,bundle-yahoo-slick, bundle-contactlist,meta-contactlist,meta-contactlist-slick, bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz, @@ -958,6 +958,8 @@ prefix="net/java/sip/communicator/service/media"/> + @@ -979,6 +981,8 @@ prefix="net/java/sip/communicator/service/media"/> + @@ -999,6 +1003,8 @@ prefix="net/java/sip/communicator/service/media"/> + @@ -1301,6 +1307,16 @@ javax.swing.event, javax.swing.border"/> + + + + + + + + diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index f6c5cd598..0ea932baf 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -83,7 +83,8 @@ felix.auto.start.45= \ reference:file:sc-bundles/argdelegation-service.jar \ reference:file:sc-bundles/version.jar \ reference:file:sc-bundles/version-impl.jar \ - reference:file:sc-bundles/branding.jar + reference:file:sc-bundles/branding.jar \ + reference:file:sc-bundles/swing-common.jar felix.auto.start.49= \ reference:file:sc-bundles/zrtp4j.jar \ diff --git a/resources/images/images.properties b/resources/images/images.properties index b1c15842a..c212623f6 100644 --- a/resources/images/images.properties +++ b/resources/images/images.properties @@ -427,3 +427,5 @@ soundIcon=resources/images/plugin/notificationconfiguration/soundIcon.png activatedIcon=resources/images/plugin/notificationconfiguration/activeIcon.png desactivatedIcon=resources/images/plugin/notificationconfiguration/desactivatedIcon.png foldericon=resources/images/plugin/notificationconfiguration/folder.png + +MediaConfigurationForm_icon=resources/images/impl/media/media.png diff --git a/resources/images/impl/media/media.png b/resources/images/impl/media/media.png new file mode 100644 index 000000000..245447821 Binary files /dev/null and b/resources/images/impl/media/media.png differ diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 1be7cc13a..b9c64283d 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -748,3 +748,10 @@ sasUnsecuredAtPeerRequestTooltip=Call unsecured at peer request # Profiler4J profiler=Profiler4j + +MediaConfigurationForm_title=Media +MediaConfigurationPanel_audio=&Audio: +MediaConfigurationPanel_down=&Down +MediaConfigurationPanel_encodings=&Encodings: +MediaConfigurationPanel_up=&Up +MediaConfigurationPanel_video=&Video: diff --git a/src/net/java/sip/communicator/impl/gui/customcontrols/TransparentPanel.java b/src/net/java/sip/communicator/impl/gui/customcontrols/TransparentPanel.java index 1c00a3e6e..d2e89fead 100644 --- a/src/net/java/sip/communicator/impl/gui/customcontrols/TransparentPanel.java +++ b/src/net/java/sip/communicator/impl/gui/customcontrols/TransparentPanel.java @@ -1,22 +1,29 @@ +/* + * 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.gui.customcontrols; import java.awt.*; import javax.swing.*; +/** + * @author Yana Stamcheva + */ public class TransparentPanel extends JPanel { - public TransparentPanel () + public TransparentPanel() { - super(); - this.setOpaque(false); } - public TransparentPanel (LayoutManager layoutManager) + public TransparentPanel(LayoutManager layout) { - super(layoutManager); + super(layout); this.setOpaque(false); } diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java index ed192b8a5..ea33b3b96 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java @@ -18,6 +18,7 @@ import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.swing.*; import net.java.sip.communicator.util.*; /** @@ -227,94 +228,9 @@ public void hierarchyChanged(HierarchyEvent event) * Component at a time and, in the absence of such a * Component, displays noVideoComponent */ - private Container createVideoContainer(final Component noVideoComponent) + private Container createVideoContainer(Component noVideoComponent) { - final ContainerListener containerListener = new ContainerListener() - { - - /* - * Since the videoContainer displays either noVideoComponent or a - * single visual Component which represents video, ensures the last - * Component added to the Container is the only Component it - * contains i.e. noVideoComponent goes away when the video is - * displayed and the video goes away when noVideoComponent is - * displayed. - */ - public void componentAdded(ContainerEvent event) - { - Container container = event.getContainer(); - Component local = - ((VideoLayout) container.getLayout()).getLocal(); - Component added = event.getChild(); - - if ((local != null) && (added == local)) - return; - - Component[] components = container.getComponents(); - boolean validate = false; - - for (int i = 0; i < components.length; i++) - { - Component component = components[i]; - - if ((component != added) && (component != local)) - { - container.remove(component); - validate = true; - } - } - if (validate) - container.validate(); - } - - /* - * Displays noVideoComponent when there is no visual Component which - * represents video to be displayed. - */ - public void componentRemoved(ContainerEvent event) - { - Container container = event.getContainer(); - - if ((container.getComponentCount() <= 0) - || (((VideoLayout) container.getLayout()).getRemote() == null)) - { - container.add(noVideoComponent, VideoLayout.REMOTE); - container.validate(); - } - } - }; - Container videoContainer = new TransparentPanel(new VideoLayout()) - { - - /* - * Ensures noVideoComponent is displayed even when the clients of - * the videoContainer invoke its #removeAll() to remove their - * previous visual Components representing video. Just adding - * noVideoComponent upon ContainerEvent#COMPONENT_REMOVED when there - * is no other Component left in the Container will cause an - * infinite loop because Container#removeAll() will detect that a - * new Component has been added while dispatching the event and will - * then try to remove the new Component. - */ - public void removeAll() - { - removeContainerListener(containerListener); - try - { - super.removeAll(); - } - finally - { - addContainerListener(containerListener); - containerListener.componentRemoved(new ContainerEvent(this, - ContainerEvent.COMPONENT_REMOVED, null)); - } - } - }; - - videoContainer.addContainerListener(containerListener); - videoContainer.add(noVideoComponent, VideoLayout.REMOTE); - return videoContainer; + return new VideoContainer(noVideoComponent); } /** diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf index 546a9080e..17f57605d 100644 --- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf +++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf @@ -39,5 +39,6 @@ Import-Package: org.osgi.framework, javax.swing.undo, javax.swing.border, net.java.sip.communicator.service.audionotifier, + net.java.sip.communicator.swing, org.jdesktop.jdic.desktop, say.swing diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index ffaaf17d7..71554602a 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -11,6 +11,7 @@ import java.net.*; import java.text.*; import java.util.*; + import javax.media.*; import javax.media.control.*; import javax.media.format.*; diff --git a/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java new file mode 100644 index 000000000..dd3a13355 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java @@ -0,0 +1,209 @@ +/* + * 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.media; + +import java.util.*; + +import javax.media.*; +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.impl.media.device.*; + +/** + * @author Lubomir Marinov + */ +public class DeviceConfigurationComboBoxModel + implements ComboBoxModel +{ + public static class CaptureDevice + { + public final CaptureDeviceInfo info; + + public CaptureDevice(CaptureDeviceInfo info) + { + if (info == null) + throw new IllegalArgumentException("info"); + + this.info = info; + } + + public String toString() + { + return info.getName(); + } + } + + public static final int AUDIO = 1; + + private static final CaptureDevice[] NO_CAPTURE_DEVICES = + new CaptureDevice[0]; + + public static final int VIDEO = 2; + + private final DeviceConfiguration deviceConfiguration; + + private CaptureDevice[] devices; + + private final List listeners = + new ArrayList(); + + private final int type; + + public DeviceConfigurationComboBoxModel( + DeviceConfiguration deviceConfiguration, int type) + { + if (deviceConfiguration == null) + throw new IllegalArgumentException("deviceConfiguration"); + if ((type != AUDIO) && (type != VIDEO)) + throw new IllegalArgumentException("type"); + + this.deviceConfiguration = deviceConfiguration; + this.type = type; + } + + public void addListDataListener(ListDataListener listener) + { + if (listener == null) + throw new IllegalArgumentException("listener"); + + if (!listeners.contains(listener)) + listeners.add(listener); + } + + protected void fireContentsChanged(int index0, int index1) + { + ListDataListener[] listeners = + this.listeners.toArray(new ListDataListener[this.listeners.size()]); + ListDataEvent event = + new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index0, + index1); + + for (int i = 0; i < listeners.length; i++) + { + listeners[i].contentsChanged(event); + } + } + + private DeviceConfiguration getDeviceConfiguration() + { + return deviceConfiguration; + } + + private CaptureDevice[] getDevices() + { + if (devices != null) + return devices; + + DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); + CaptureDeviceInfo[] infos; + switch (type) + { + case AUDIO: + infos = deviceConfiguration.getAvailableAudioCaptureDevices(); + break; + case VIDEO: + infos = deviceConfiguration.getAvailableVideoCaptureDevices(); + break; + default: + throw new IllegalStateException("type"); + } + + final int deviceCount = infos.length; + if (deviceCount < 1) + devices = NO_CAPTURE_DEVICES; + else + { + devices = new CaptureDevice[deviceCount]; + for (int i = 0; i < deviceCount; i++) + { + devices[i] = new CaptureDevice(infos[i]); + } + } + return devices; + } + + public Object getElementAt(int index) + { + return getDevices()[index]; + } + + private CaptureDevice getSelectedDevice() + { + DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); + CaptureDeviceInfo info; + switch (type) + { + case AUDIO: + info = deviceConfiguration.getAudioCaptureDevice(); + break; + case VIDEO: + info = deviceConfiguration.getVideoCaptureDevice(); + break; + default: + throw new IllegalStateException("type"); + } + + CaptureDevice[] devices = getDevices(); + for (int i = 0; i < devices.length; i++) + { + CaptureDevice device = devices[i]; + if (device.info.equals(info)) + { + return device; + } + } + return null; + } + + public Object getSelectedItem() + { + return getSelectedDevice(); + } + + public int getSize() + { + return getDevices().length; + } + + public void removeListDataListener(ListDataListener listener) + { + if (listener == null) + throw new IllegalArgumentException("listener"); + + listeners.remove(listener); + } + + private void setSelectedDevice(CaptureDevice device) + { + // We cannot clear the selection of DeviceConfiguration. + if (device == null) + return; + + CaptureDevice selectedDevice = getSelectedDevice(); + if (selectedDevice != device) + { + DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); + switch (type) + { + case AUDIO: + deviceConfiguration.setAudioCaptureDevice(device.info); + break; + case VIDEO: + deviceConfiguration.setVideoCaptureDevice(device.info); + break; + } + + fireContentsChanged(-1, -1); + } + } + + public void setSelectedItem(Object item) + { + setSelectedDevice((CaptureDevice) item); + } +} diff --git a/src/net/java/sip/communicator/impl/media/EncodingConfigurationTableModel.java b/src/net/java/sip/communicator/impl/media/EncodingConfigurationTableModel.java new file mode 100644 index 000000000..eec5d0376 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/EncodingConfigurationTableModel.java @@ -0,0 +1,187 @@ +/* + * 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.media; + +import java.util.*; + +import javax.swing.table.*; + +import net.java.sip.communicator.impl.media.codec.*; + +/** + * @author Lubomir Marinov + */ +public class EncodingConfigurationTableModel + extends AbstractTableModel +{ + public static final int AUDIO = DeviceConfigurationComboBoxModel.AUDIO; + + private static final String[] NO_ENCODINGS = new String[0]; + + public static final int VIDEO = DeviceConfigurationComboBoxModel.VIDEO; + + private final EncodingConfiguration encodingConfiguration; + + private String[] encodings; + + private final int type; + + public EncodingConfigurationTableModel( + EncodingConfiguration encodingConfiguration, int type) + { + if (encodingConfiguration == null) + throw new IllegalArgumentException("encodingConfiguration"); + if ((type != AUDIO) && (type != VIDEO)) + throw new IllegalArgumentException("type"); + + this.encodingConfiguration = encodingConfiguration; + this.type = type; + } + + public Class getColumnClass(int columnIndex) + { + return (columnIndex == 0) ? Boolean.class : super + .getColumnClass(columnIndex); + } + + public int getColumnCount() + { + return 2; + } + + private String[] getEncodings() + { + if (encodings != null) + return encodings; + + String[] availableEncodings; + switch (type) + { + case AUDIO: + availableEncodings = + encodingConfiguration.getAvailableAudioEncodings(); + break; + case VIDEO: + availableEncodings = + encodingConfiguration.getAvailableVideoEncodings(); + break; + default: + throw new IllegalStateException("type"); + } + + final int encodingCount = availableEncodings.length; + if (encodingCount < 1) + encodings = NO_ENCODINGS; + else + { + encodings = new String[encodingCount]; + System + .arraycopy(availableEncodings, 0, encodings, 0, encodingCount); + Arrays.sort(encodings, 0, encodingCount, new Comparator() + { + public int compare(String encoding0, String encoding1) + { + if (encodingConfiguration.getPriority(encoding0) > encodingConfiguration + .getPriority(encoding1)) + return -1; + return encoding0.compareTo(encoding1); + } + }); + } + return encodings; + } + + private int[] getPriorities() + { + String[] encodings = getEncodings(); + final int count = encodings.length; + int[] priorities = new int[count]; + for (int i = 0; i < count; i++) + { + int priority = encodingConfiguration.getPriority(encodings[i]); + priorities[i] = (priority > 0) ? (count - i) : 0; + } + return priorities; + } + + public int getRowCount() + { + return getEncodings().length; + } + + public Object getValueAt(int rowIndex, int columnIndex) + { + String encoding = getEncodings()[rowIndex]; + switch (columnIndex) + { + case 0: + return Boolean + .valueOf(encodingConfiguration.getPriority(encoding) > 0); + case 1: + return MediaUtils.sdpToJmfEncoding(encoding); + default: + return null; + } + } + + public boolean isCellEditable(int rowIndex, int columnIndex) + { + return (columnIndex == 0); + } + + public void move(int rowIndex, boolean up) + { + if (up) + { + if (rowIndex <= 0) + throw new IllegalArgumentException("rowIndex"); + + move(rowIndex - 1, false); + return; + } + + if (rowIndex >= (getRowCount() - 1)) + throw new IllegalArgumentException("rowIndex"); + + int[] priorities = getPriorities(); + final int nextRowIndex = rowIndex + 1; + if (priorities[rowIndex] > 0) + priorities[rowIndex] = priorities.length - nextRowIndex; + if (priorities[nextRowIndex] > 0) + priorities[nextRowIndex] = priorities.length - rowIndex; + setPriorities(priorities); + + String swap = encodings[rowIndex]; + encodings[rowIndex] = encodings[nextRowIndex]; + encodings[nextRowIndex] = swap; + + fireTableRowsUpdated(rowIndex, nextRowIndex); + } + + private void setPriorities(int[] priorities) + { + final int count = encodings.length; + if (priorities.length != count) + throw new IllegalArgumentException("priorities"); + + for (int i = 0; i < count; i++) + encodingConfiguration.setPriority(encodings[i], priorities[i]); + } + + public void setValueAt(Object value, int rowIndex, int columnIndex) + { + if ((columnIndex == 0) && (value instanceof Boolean)) + { + int[] priorities = getPriorities(); + priorities[rowIndex] = + ((Boolean) value) ? (priorities.length - rowIndex) : 0; + setPriorities(priorities); + + fireTableCellUpdated(rowIndex, columnIndex); + } + } +} diff --git a/src/net/java/sip/communicator/impl/media/MediaActivator.java b/src/net/java/sip/communicator/impl/media/MediaActivator.java index f4ab42b55..3599aa37c 100644 --- a/src/net/java/sip/communicator/impl/media/MediaActivator.java +++ b/src/net/java/sip/communicator/impl/media/MediaActivator.java @@ -6,14 +6,15 @@ */ package net.java.sip.communicator.impl.media; -import org.osgi.framework.*; -import net.java.sip.communicator.util.*; import net.java.sip.communicator.service.configuration.*; -import net.java.sip.communicator.service.netaddr.*; -import java.util.*; -import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.fileaccess.*; +import net.java.sip.communicator.service.gui.ConfigurationForm; +import net.java.sip.communicator.service.media.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; +import org.osgi.framework.*; /** * Invoke "Service Binder" to parse the service XML and register @@ -25,7 +26,7 @@ public class MediaActivator implements BundleActivator { - private Logger logger = Logger.getLogger(MediaActivator.class.getName()); + private final Logger logger = Logger.getLogger(MediaActivator.class.getName()); private static MediaServiceImpl mediaServiceImpl = null; @@ -34,6 +35,7 @@ public class MediaActivator private static NetworkAddressManagerService networkAddressManagerService = null; private static FileAccessService fileAccessService = null; + private static ResourceManagementService resources; private ServiceRegistration mediaServiceRegistration = null; @@ -51,22 +53,21 @@ public void start(BundleContext context) throws Exception { logger.debug("Started."); - this.bundleContext = context; - Hashtable hashtable = new Hashtable(); - mediaServiceImpl = new MediaServiceImpl(); + MediaActivator.bundleContext = context; - //load all icq providers + // MediaService + mediaServiceImpl = new MediaServiceImpl(); mediaServiceImpl.start(); - //reg the icq account man. - mediaServiceRegistration = context.registerService( - MediaService.class.getName(), - mediaServiceImpl, - hashtable); - - + mediaServiceRegistration = + context.registerService(MediaService.class.getName(), + mediaServiceImpl, null); logger.debug("Media Service ... [REGISTERED]"); + + // MediaConfigurationForm + context.registerService(ConfigurationForm.class.getName(), + new MediaConfigurationForm(), null); } /** @@ -132,6 +133,13 @@ public static FileAccessService getFileAccessService() return fileAccessService; } + public static ResourceManagementService getResources() + { + if (resources == null) + resources = + ResourceManagementServiceUtils.getService(bundleContext); + return resources; + } /** * Returns a reference to the bundle context that we were started with. diff --git a/src/net/java/sip/communicator/impl/media/MediaConfigurationForm.java b/src/net/java/sip/communicator/impl/media/MediaConfigurationForm.java new file mode 100644 index 000000000..7990273dc --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MediaConfigurationForm.java @@ -0,0 +1,38 @@ +/* + * 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.media; + +import net.java.sip.communicator.service.gui.*; + +/** + * @author Lubomir Marinov + */ +public class MediaConfigurationForm + implements ConfigurationForm +{ + public Object getForm() + { + return new MediaConfigurationPanel(); + } + + public byte[] getIcon() + { + return MediaActivator.getResources().getImageInBytes( + "MediaConfigurationForm_icon"); + } + + public int getIndex() + { + return -1; + } + + public String getTitle() + { + return MediaActivator.getResources().getI18NString( + "MediaConfigurationForm_title"); + } +} diff --git a/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java new file mode 100644 index 000000000..13a94f56c --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java @@ -0,0 +1,429 @@ +/* + * 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.media; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; + +import javax.media.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; + +import net.java.sip.communicator.service.media.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.swing.*; +import net.java.sip.communicator.util.*; + +import org.osgi.framework.*; + +/** + * @author Lubomir Marinov + */ +public class MediaConfigurationPanel + extends JPanel +{ + private static final int HGAP = 5; + + private static final int VGAP = 5; + + private static MediaServiceImpl getMediaService() + { + BundleContext bundleContext = MediaActivator.getBundleContext(); + ServiceReference serviceReference = + bundleContext.getServiceReference(MediaService.class.getName()); + + return (serviceReference == null) ? null + : (MediaServiceImpl) bundleContext.getService(serviceReference); + } + + private final Logger logger = + Logger.getLogger(MediaConfigurationPanel.class); + + private final MediaServiceImpl mediaService = getMediaService(); + + public MediaConfigurationPanel() + { + super(new GridLayout(0, 1, HGAP, VGAP)); + + int[] types = + new int[] + { DeviceConfigurationComboBoxModel.AUDIO, + DeviceConfigurationComboBoxModel.VIDEO }; + for (int i = 0; i < types.length; i++) + add(createControls(types[i])); + } + + private void controllerUpdateForPreview(ControllerEvent event, + Container videoContainer) + { + if (event instanceof RealizeCompleteEvent) + { + Player player = (Player) event.getSourceController(); + Component video = player.getVisualComponent(); + + showPreview(videoContainer, video, player); + } + } + + private Component createControls(int type) + { + final JComboBox comboBox = new JComboBox(); + comboBox.setEditable(false); + + JLabel label = new JLabel(getLabelText(type)); + label.setDisplayedMnemonic(getDisplayedMnemonic(type)); + label.setLabelFor(comboBox); + + final JComponent preview; + ActionListener comboBoxListener = null; + if (type == DeviceConfigurationComboBoxModel.VIDEO) + { + JLabel noPreview = + new JLabel(MediaActivator.getResources().getI18NString( + "MediaConfigurationPanel_noPreview")); + noPreview.setHorizontalAlignment(SwingConstants.CENTER); + noPreview.setVerticalAlignment(SwingConstants.CENTER); + + preview = createVideoContainer(noPreview); + Dimension previewSize = new Dimension(150, 150); + preview.setMaximumSize(previewSize); + preview.setMinimumSize(previewSize); + preview.setPreferredSize(previewSize); + + comboBoxListener = new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + Object selection = comboBox.getSelectedItem(); + CaptureDeviceInfo device = null; + if (selection instanceof DeviceConfigurationComboBoxModel.CaptureDevice) + { + device = + ((DeviceConfigurationComboBoxModel.CaptureDevice) selection).info; + } + + createPreview(device, preview); + } + }; + comboBox.addActionListener(comboBoxListener); + } else + preview = null; + + Container deviceContainer = new JPanel(new GridBagLayout()); + GridBagConstraints deviceConstraints = new GridBagConstraints(); + deviceConstraints.anchor = GridBagConstraints.NORTHWEST; + deviceConstraints.gridx = 0; + deviceConstraints.gridy = 0; + deviceConstraints.weightx = 0; + deviceContainer.add(label, deviceConstraints); + deviceConstraints.gridx = 1; + deviceConstraints.weightx = 1; + deviceContainer.add(comboBox, deviceConstraints); + if (preview != null) + { + deviceConstraints.gridx = 2; + deviceConstraints.weightx = 0; + deviceContainer.add(preview, deviceConstraints); + } + + comboBox.setModel(new DeviceConfigurationComboBoxModel(mediaService + .getDeviceConfiguration(), type)); + if (comboBoxListener != null) + comboBoxListener.actionPerformed(null); + + Container container = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 1; + constraints.weighty = 0; + container.add(deviceContainer, constraints); + constraints.fill = GridBagConstraints.BOTH; + constraints.gridy = 1; + constraints.weighty = 1; + container.add(createEncodingControls(type), constraints); + return container; + } + + private Component createEncodingControls(int type) + { + ResourceManagementService resources = MediaActivator.getResources(); + String key; + + final JTable table = new JTable(); + table.setFillsViewportHeight(true); + table.setShowGrid(false); + table.setTableHeader(null); + + key = "MediaConfigurationPanel_encodings"; + JLabel label = new JLabel(resources.getI18NString(key)); + label.setDisplayedMnemonic(resources.getI18nMnemonic(key)); + label.setLabelFor(table); + + key = "MediaConfigurationPanel_up"; + final JButton upButton = new JButton(resources.getI18NString(key)); + upButton.setMnemonic(resources.getI18nMnemonic(key)); + + key = "MediaConfigurationPanel_down"; + final JButton downButton = new JButton(resources.getI18NString(key)); + downButton.setMnemonic(resources.getI18nMnemonic(key)); + + Container buttonBar = new JPanel(new GridLayout(0, 1)); + buttonBar.add(upButton); + buttonBar.add(downButton); + + Container container = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.NORTHWEST; + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridwidth = 2; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0; + constraints.weighty = 0; + container.add(label, constraints); + constraints.anchor = GridBagConstraints.CENTER; + constraints.fill = GridBagConstraints.BOTH; + constraints.gridwidth = 1; + constraints.gridx = 0; + constraints.gridy = 1; + constraints.weightx = 1; + constraints.weighty = 1; + container.add(new JScrollPane(table), constraints); + constraints.anchor = GridBagConstraints.NORTHEAST; + constraints.fill = GridBagConstraints.NONE; + constraints.gridwidth = 1; + constraints.gridx = 1; + constraints.gridy = 1; + constraints.weightx = 0; + constraints.weighty = 0; + container.add(buttonBar, constraints); + + table.setModel(new EncodingConfigurationTableModel(mediaService + .getEncodingConfiguration(), type)); + + /* + * The first column contains the check boxes which enable/disable their + * associated encodings and it doesn't make sense to make it wider than + * the check boxes. + */ + TableColumnModel tableColumnModel = table.getColumnModel(); + TableColumn tableColumn = tableColumnModel.getColumn(0); + tableColumn.setMaxWidth(tableColumn.getMinWidth()); + + ListSelectionListener tableSelectionListener = + new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent event) + { + if (table.getSelectedRowCount() == 1) + { + int selectedRow = table.getSelectedRow(); + if (selectedRow > -1) + { + upButton.setEnabled(selectedRow > 0); + downButton.setEnabled(selectedRow < (table + .getRowCount() - 1)); + return; + } + } + upButton.setEnabled(false); + downButton.setEnabled(false); + } + }; + table.getSelectionModel().addListSelectionListener( + tableSelectionListener); + tableSelectionListener.valueChanged(null); + + ActionListener buttonListener = new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + Object source = event.getSource(); + boolean up; + if (source == upButton) + up = true; + else if (source == downButton) + up = false; + else + return; + + ((EncodingConfigurationTableModel) table.getModel()).move(table + .getSelectedRow(), up); + } + }; + upButton.addActionListener(buttonListener); + downButton.addActionListener(buttonListener); + + return container; + } + + private void createPreview(CaptureDeviceInfo device, + final Container videoContainer) + { + videoContainer.removeAll(); + + if (device == null) + return; + + Player player = null; + Exception exception = null; + try + { + player = Manager.createPlayer(device.getLocator()); + } + catch (IOException ex) + { + exception = ex; + } + catch (NoPlayerException ex) + { + exception = ex; + } + if (exception != null) + { + logger.error("Failed to create preview for device " + device, + exception); + return; + } + + player.addControllerListener(new ControllerListener() + { + public void controllerUpdate(ControllerEvent event) + { + controllerUpdateForPreview(event, videoContainer); + } + }); + player.start(); + } + + private JComponent createVideoContainer(Component noVideoComponent) + { + return new VideoContainer(noVideoComponent); + } + + private void disposePlayer(Player player) + { + player.stop(); + player.deallocate(); + player.close(); + } + + private char getDisplayedMnemonic(int type) + { + switch (type) + { + case DeviceConfigurationComboBoxModel.AUDIO: + return MediaActivator.getResources().getI18nMnemonic( + "MediaConfigurationPanel_audio"); + case DeviceConfigurationComboBoxModel.VIDEO: + return MediaActivator.getResources().getI18nMnemonic( + "MediaConfigurationPanel_video"); + default: + throw new IllegalArgumentException("type"); + } + } + + private String getLabelText(int type) + { + switch (type) + { + case DeviceConfigurationComboBoxModel.AUDIO: + return MediaActivator.getResources().getI18NString( + "MediaConfigurationPanel_audio"); + case DeviceConfigurationComboBoxModel.VIDEO: + return MediaActivator.getResources().getI18NString( + "MediaConfigurationPanel_video"); + default: + throw new IllegalArgumentException("type"); + } + } + + private void showPreview(final Container previewContainer, + final Component preview, final Player player) + { + if (!SwingUtilities.isEventDispatchThread()) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + showPreview(previewContainer, preview, player); + } + }); + return; + } + + previewContainer.removeAll(); + + if (preview != null) + { + HierarchyListener hierarchyListener = new HierarchyListener() + { + private Window window; + + private WindowListener windowListener; + + public void dispose() + { + if (windowListener != null) + { + if (window != null) + { + window.removeWindowListener(windowListener); + window = null; + } + windowListener = null; + } + + preview.removeHierarchyListener(this); + + disposePlayer(player); + } + + public void hierarchyChanged(HierarchyEvent event) + { + if ((event.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) + { + if (preview.isDisplayable()) + { + if (windowListener == null) + { + window = + SwingUtilities.windowForComponent(preview); + if (window != null) + { + windowListener = new WindowAdapter() + { + public void windowClosing( + WindowEvent event) + { + dispose(); + } + }; + window.addWindowListener(windowListener); + } + } + } + else + { + dispose(); + } + } + } + }; + preview.addHierarchyListener(hierarchyListener); + + previewContainer.add(preview); + } + else + disposePlayer(player); + } +} diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java index e786ce453..6eab840a1 100644 --- a/src/net/java/sip/communicator/impl/media/MediaControl.java +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -14,11 +14,9 @@ import javax.media.control.*; import javax.media.format.*; import javax.media.protocol.*; -import javax.sdp.*; import net.java.sip.communicator.impl.media.codec.*; import net.java.sip.communicator.impl.media.device.*; -import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.media.MediaException; import net.java.sip.communicator.util.*; diff --git a/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java b/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java index bcb36b9ca..c2fc130fa 100644 --- a/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java +++ b/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java @@ -1,3 +1,9 @@ +/* + * 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.media.codec; import java.io.*; @@ -25,7 +31,7 @@ public class EncodingConfiguration /** * SDP Codes of all video formats that JMF supports. */ - private String[] availableVideoEncodings = new String[] + private final String[] availableVideoEncodings = new String[] { Integer.toString(Constants.H264_RTP_SDP), // javax.media.format.VideoFormat.H263_RTP Integer.toString(SdpConstants.H263), @@ -37,7 +43,7 @@ public class EncodingConfiguration /** * SDP Codes of all audio formats that JMF supports. */ - private String[] availableAudioEncodings = new String[] + private final String[] availableAudioEncodings = new String[] { // ILBC Integer.toString(97), @@ -60,10 +66,10 @@ public class EncodingConfiguration // Integer.toString(SdpConstants.G729) }; - private TreeSet suportedVideoEncodings = + private final Set suportedVideoEncodings = new TreeSet(new EncodingComparator()); - private TreeSet suportedAudioEncodings = + private final Set suportedAudioEncodings = new TreeSet(new EncodingComparator()); /** @@ -73,13 +79,10 @@ public class EncodingConfiguration * would be decorelated and other components (such as the UI) should present * them separately. */ - private final Hashtable encodingPreferences = + private final Map encodingPreferences = new Hashtable(); - /** - * - */ - private static String[] customCodecs = + private static final String[] customCodecs = new String[] { FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.alaw.Encoder" @@ -101,7 +104,7 @@ public class EncodingConfiguration /** * Custom Packages provided by Sip-Communicator */ - private static String[] customPackages = new String[] + private static final String[] customPackages = new String[] { // datasource for low latency ALSA input "net.java.sip.communicator.impl", "net.sf.fmj" }; @@ -144,10 +147,8 @@ private void initializeFormatPreferences() List sdpPreferences = confService.getPropertyNamesByPrefix(PROP_SDP_PREFERENCE, false); - Iterator sdpPreferencesIter = sdpPreferences.iterator(); - while (sdpPreferencesIter.hasNext()) + for (String pName : sdpPreferences) { - String pName = sdpPreferencesIter.next(); String prefStr = confService.getString(pName); String fmtName = pName.substring(pName.lastIndexOf('.') + 1).replaceAll("sdp", @@ -206,19 +207,16 @@ private void updateSupportedEncodings() suportedVideoEncodings.remove(ac); } } - + /** * Updates the codecs in the set according preferences in * encodingPreferences. If value is "0" the codec is disabled. */ public String[] updateEncodings(List encs) { - TreeSet result = new TreeSet(new EncodingComparator()); - Iterator iter = encs.iterator(); - while (iter.hasNext()) - { - String c = (String) iter.next(); - + Set result = new TreeSet(new EncodingComparator()); + for (String c : encs) + { Integer pref1 = encodingPreferences.get(c); int pref1IntValue = (pref1 == null) ? 0 : pref1.intValue(); @@ -279,6 +277,11 @@ public void setPriority(String encoding, int priority) updateSupportedEncodings(); } + public int getPriority(String encoding) + { + return encodingPreferences.get(encoding); + } + /** * Register in JMF the custom codecs we provide */ @@ -360,7 +363,7 @@ private void registerCustomPackages() // list is always short if (!currentPackagePrefix.contains(className)) { - currentPackagePrefix.addElement(className); + currentPackagePrefix.add(className); logger.debug("Adding package : " + className); } } @@ -426,6 +429,5 @@ public int compare(String s1, String s2) { return compareEncodingPreferences(s1, s2); } - } } diff --git a/src/net/java/sip/communicator/impl/media/media.manifest.mf b/src/net/java/sip/communicator/impl/media/media.manifest.mf index 7f195c9bb..69ab1b37b 100644 --- a/src/net/java/sip/communicator/impl/media/media.manifest.mf +++ b/src/net/java/sip/communicator/impl/media/media.manifest.mf @@ -4,35 +4,37 @@ Bundle-Description: A bundle that offers Media capture and presentation capabili Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 Import-Package: org.osgi.framework, - net.java.sip.communicator.service.configuration, - net.java.sip.communicator.service.configuration.event, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event, - net.java.sip.communicator.util, - net.java.sip.communicator.service.netaddr, + org.bouncycastle.jce.provider, + org.xml.sax, + javax.crypto, + javax.crypto.interfaces, + javax.crypto.spec, + javax.imageio, + javax.sound, + javax.sound.sampled, javax.swing, - javax.swing.event, javax.swing.border, - javax.imageio, + javax.swing.event, + javax.swing.table, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.configuration.event, net.java.sip.communicator.service.fileaccess, net.java.sip.communicator.service.gui, + net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.event, net.java.sip.communicator.service.resources, - javax.sound, - javax.sound.sampled, + net.java.sip.communicator.swing, + net.java.sip.communicator.util, quicktime, quicktime.std.sg, quicktime.qd, quicktime.util, quicktime.std.image, - javax.crypto, - javax.crypto.spec, - javax.crypto.interfaces, - org.xml.sax, - org.bouncycastle.jce.provider, gnu.java.zrtp, - gnu.java.zrtp.zidfile, + gnu.java.zrtp.packets, gnu.java.zrtp.utils, - gnu.java.zrtp.packets + gnu.java.zrtp.zidfile Export-Package: net.java.sip.communicator.service.media, net.java.sip.communicator.service.media.event, net.java.sip.communicator.impl.media, diff --git a/src/net/java/sip/communicator/impl/gui/main/call/FitLayout.java b/src/net/java/sip/communicator/swing/FitLayout.java similarity index 96% rename from src/net/java/sip/communicator/impl/gui/main/call/FitLayout.java rename to src/net/java/sip/communicator/swing/FitLayout.java index 8f2434571..e9661c665 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/FitLayout.java +++ b/src/net/java/sip/communicator/swing/FitLayout.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.call; +package net.java.sip.communicator.swing; import java.awt.*; diff --git a/src/net/java/sip/communicator/swing/VideoContainer.java b/src/net/java/sip/communicator/swing/VideoContainer.java new file mode 100644 index 000000000..2ad47154f --- /dev/null +++ b/src/net/java/sip/communicator/swing/VideoContainer.java @@ -0,0 +1,114 @@ +/* + * 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.swing; + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +/** + * @author Lubomir Marinov + */ +public class VideoContainer + extends JPanel +{ + private final ContainerListener containerListener = new ContainerListener() + { + + /* + * Since the videoContainer displays either noVideoComponent or a single + * visual Component which represents video, ensures the last Component + * added to the Container is the only Component it contains i.e. + * noVideoComponent goes away when the video is displayed and the video + * goes away when noVideoComponent is displayed. + */ + public void componentAdded(ContainerEvent event) + { + Container container = event.getContainer(); + Component local = ((VideoLayout) container.getLayout()).getLocal(); + Component added = event.getChild(); + + if ((local != null) && (added == local)) + return; + + Component[] components = container.getComponents(); + boolean validate = false; + + for (int i = 0; i < components.length; i++) + { + Component component = components[i]; + + if ((component != added) && (component != local)) + { + container.remove(component); + validate = true; + } + } + if (validate) + container.validate(); + }; + + /* + * Displays noVideoComponent when there is no visual Component which + * represents video to be displayed. + */ + public void componentRemoved(ContainerEvent event) + { + Container container = event.getContainer(); + + if ((container.getComponentCount() <= 0) + || (((VideoLayout) container.getLayout()).getRemote() == null)) + { + container.add(noVideoComponent, VideoLayout.REMOTE); + container.validate(); + } + } + }; + + private final Component noVideoComponent; + + public VideoContainer(Component noVideoComponent) + { + super(new VideoLayout()); + + this.noVideoComponent = noVideoComponent; + + addContainerListener(containerListener); + add(noVideoComponent, VideoLayout.REMOTE); + } + + public Component add(Component comp) + { + add(comp, VideoLayout.REMOTE); + return comp; + } + + /* + * Ensures noVideoComponent is displayed even when the clients of the + * videoContainer invoke its #removeAll() to remove their previous visual + * Components representing video. Just adding noVideoComponent upon + * ContainerEvent#COMPONENT_REMOVED when there is no other Component left in + * the Container will cause an infinite loop because Container#removeAll() + * will detect that a new Component has been added while dispatching the + * event and will then try to remove the new Component. + */ + public void removeAll() + { + removeContainerListener(containerListener); + try + { + super.removeAll(); + } + finally + { + addContainerListener(containerListener); + containerListener.componentRemoved(new ContainerEvent(this, + ContainerEvent.COMPONENT_REMOVED, null)); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/main/call/VideoLayout.java b/src/net/java/sip/communicator/swing/VideoLayout.java similarity index 93% rename from src/net/java/sip/communicator/impl/gui/main/call/VideoLayout.java rename to src/net/java/sip/communicator/swing/VideoLayout.java index 56ba39106..70d2568df 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/VideoLayout.java +++ b/src/net/java/sip/communicator/swing/VideoLayout.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.impl.gui.main.call; +package net.java.sip.communicator.swing; import java.awt.*; diff --git a/src/net/java/sip/communicator/swing/swing.common.manifest.mf b/src/net/java/sip/communicator/swing/swing.common.manifest.mf new file mode 100644 index 000000000..f98555eb6 --- /dev/null +++ b/src/net/java/sip/communicator/swing/swing.common.manifest.mf @@ -0,0 +1,6 @@ +Bundle-Name: Swing Common +Bundle-Description: Common/generic Swing additions not specific to SIP Communicator +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: javax.swing +Export-Package: net.java.sip.communicator.swing