From dc0f19b8706f0cb04042e3b85b155bb08a49a3d5 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Wed, 8 Sep 2010 16:01:37 +0000 Subject: [PATCH] Use camera to grab avatar image. --- resources/sounds/sounds.properties | 4 +- resources/sounds/webcamSnapshot.wav | Bin 0 -> 73772 bytes .../presence/avatar/SelectAvatarMenu.java | 3 +- .../avatar/imagepicker/ImagePickerDialog.java | 22 +- .../avatar/imagepicker/WebcamDialog.java | 167 +++++------- .../impl/neomedia/MediaConfiguration.java | 255 +++-------------- .../impl/neomedia/MediaServiceImpl.java | 256 ++++++++++++++++++ ...tionSetServerStoredAccountInfoMsnImpl.java | 6 +- .../service/neomedia/MediaService.java | 14 + 9 files changed, 396 insertions(+), 331 deletions(-) create mode 100644 resources/sounds/webcamSnapshot.wav diff --git a/resources/sounds/sounds.properties b/resources/sounds/sounds.properties index e732a0729..28f063600 100644 --- a/resources/sounds/sounds.properties +++ b/resources/sounds/sounds.properties @@ -20,4 +20,6 @@ DIAL=resources/sounds/dial.wav HANG_UP=resources/sounds/hangup.wav CALL_SECURITY_ON=resources/sounds/zrtpSecure.wav -CALL_SECURITY_ERROR=resources/sounds/zrtpAlert.wav \ No newline at end of file +CALL_SECURITY_ERROR=resources/sounds/zrtpAlert.wav + +WEBCAM_SNAPSHOT=resources/sounds/webcamSnapshot.wav diff --git a/resources/sounds/webcamSnapshot.wav b/resources/sounds/webcamSnapshot.wav new file mode 100644 index 0000000000000000000000000000000000000000..03c038d84d7ebff58077526e12bd76b0d1eb5637 GIT binary patch literal 73772 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 literal 0 HcmV?d00001 diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java index dcc51aded..de67720f5 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/SelectAvatarMenu.java @@ -260,7 +260,8 @@ public void run() = GuiActivator.getRegisteredProviderForAccount( accountID); - if(protocolProvider != null) + if(protocolProvider != null + && protocolProvider.isRegistered()) { OperationSetAvatar opSetAvatar = protocolProvider diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java index 2a1eb05e4..02e24dd03 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java +++ b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/ImagePickerDialog.java @@ -96,8 +96,6 @@ private void initComponents(int clipperZoneWidth, int clipperZoneHeight) this.webcamButton = new JButton(GuiActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.TAKE_PHOTO")); - // disable it till we support it - this.webcamButton.setEnabled(false); this.webcamButton.addActionListener(this); this.webcamButton.setName("webcamButton"); @@ -158,17 +156,17 @@ else if (name.equals("resetButton")) { this.editPanel.reset(); } - else + else if (name.equals("webcamButton")) { -// WebcamDialog dialog = new WebcamDialog(this); -// dialog.setVisible(true); -// byte[] bimage = dialog.getGrabbedImage(); -// -// if (bimage != null) -// { -// Image i = new ImageIcon(bimage).getImage(); -// editPanel.setImage(ImageUtils.getBufferedImage(i)); -// } + WebcamDialog dialog = new WebcamDialog(this); + dialog.setVisible(true); + byte[] bimage = dialog.getGrabbedImage(); + + if (bimage != null) + { + Image i = new ImageIcon(bimage).getImage(); + editPanel.setImage(ImageUtils.getBufferedImage(i)); + } } } diff --git a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java index ede148e3c..19ec777ca 100644 --- a/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java +++ b/src/net/java/sip/communicator/impl/gui/main/presence/avatar/imagepicker/WebcamDialog.java @@ -8,36 +8,38 @@ import java.awt.*; import java.awt.event.*; +import java.awt.image.*; import javax.swing.*; import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.service.audionotifier.*; -//import net.java.sip.communicator.service.media.*; -import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.swing.*; /** * A dialog showing the webcam and allowing the user to grap a snapshot * * @author Damien Roth + * @author Damian Minkov */ public class WebcamDialog extends SIPCommDialog implements ActionListener -// , VideoListener { + /** + * The Logger used by the WebcamDialog class and its + * instances for logging output. + */ + private static final Logger logger = Logger.getLogger(WebcamDialog.class); + private VideoContainer videoContainer; - private Component videoComponent = null; - - private JButton cancelButton; + private JButton grabSnapshot; - + private byte[] grabbedImage = null; - private TransparentPanel southPanel; - - private TransparentPanel timerPanel; private TimerImage[] timerImages = new TimerImage[3]; /** @@ -66,40 +68,40 @@ private void init() this.grabSnapshot.addActionListener(this); this.grabSnapshot.setEnabled(false); - this.cancelButton = new JButton(GuiActivator.getResources() + JButton cancelButton = new JButton(GuiActivator.getResources() .getI18NString("service.gui.avatar.imagepicker.CANCEL")); - this.cancelButton.setName("cancel"); - this.cancelButton.addActionListener(this); + cancelButton.setName("cancel"); + cancelButton.addActionListener(this); initAccessWebcam(); - // Timer Panel - this.timerPanel = new TransparentPanel(); - this.timerPanel.setLayout(new GridLayout(0, 3)); + TransparentPanel timerPanel = new TransparentPanel(); + timerPanel.setLayout(new GridLayout(0, timerImages.length)); TransparentPanel tp; for (int i = 0; i < this.timerImages.length; i++) { - this.timerImages[i] = new TimerImage("" + (3-i)); + this.timerImages[i] = new TimerImage("" + (timerImages.length - i)); tp = new TransparentPanel(); tp.add(this.timerImages[i], BorderLayout.CENTER); - this.timerPanel.add(tp); + timerPanel.add(tp); } - TransparentPanel buttonsPanel = new TransparentPanel(new GridLayout(1, 2)); + TransparentPanel buttonsPanel + = new TransparentPanel(new GridLayout(1, 2)); buttonsPanel.add(this.grabSnapshot); - buttonsPanel.add(this.cancelButton); + buttonsPanel.add(cancelButton); // South Panel - this.southPanel = new TransparentPanel(new BorderLayout()); - this.southPanel.add(this.timerPanel, BorderLayout.CENTER); - this.southPanel.add(buttonsPanel, BorderLayout.SOUTH); + TransparentPanel southPanel = new TransparentPanel(new BorderLayout()); + southPanel.add(timerPanel, BorderLayout.CENTER); + southPanel.add(buttonsPanel, BorderLayout.SOUTH); this.add(this.videoContainer, BorderLayout.CENTER); - this.add(this.southPanel, BorderLayout.SOUTH); + this.add(southPanel, BorderLayout.SOUTH); } /** @@ -107,46 +109,39 @@ private void init() */ private void initAccessWebcam() { - Dimension d = new Dimension(320, 240); - - // Create a container for the video - this.videoContainer = new VideoContainer(new JLabel( - GuiActivator.getResources() - .getI18NString("service.gui.avatar.imagepicker.INITIALIZING"), - JLabel.CENTER)); - this.videoContainer.setPreferredSize(d); - this.videoContainer.setMinimumSize(d); - this.videoContainer.setMaximumSize(d); - -// try -// { -// // Call the method in the media service -// GuiActivator.getMediaService().createLocalVideoComponent(this); -// } catch (MediaException e) -// { -// //todo: In what scenarios are exceptions thrown and how to manage them? -// this.videoContainer = new VideoContainer(new JLabel( -// GuiActivator.getResources() -// .getI18NString("service.gui.avatar.imagepicker.WEBCAM_ERROR"))); -// e.printStackTrace(); -// } + //Call the method in the media service + this.videoContainer = + (VideoContainer)GuiActivator.getMediaService() + .getVideoPreviewComponent( + GuiActivator.getMediaService().getDefaultDevice( + MediaType.VIDEO, + MediaUseCase.CALL + ), 320, 240); + this.grabSnapshot.setEnabled(true); } /** - * Grap the current image of the webcam throught the MediaService + * Grab the current image of the webcam through the MediaService */ private void grabSnapshot() { - // Just call the method "grabSnapshot" from the MediaService with the component -// try -// { -// this.grabbedImage = GuiActivator.getMediaService() -// .grabSnapshot(this.videoComponent); -// } -// catch (Exception e) -// { -// e.printStackTrace(); -// } + try + { + Robot robot = new Robot(); + Point location = videoContainer.getLocationOnScreen(); + + BufferedImage bi = robot.createScreenCapture(new Rectangle( + location.x, + location.y, + videoContainer.getWidth(), + videoContainer.getHeight())); + this.grabbedImage = ImageUtils.toByteArray(bi); + } + catch (Throwable e) + { + logger.error("Cannot create snapshot!", e); + } + close(false); this.setVisible(false); } @@ -177,41 +172,8 @@ private void playSound() protected void close(boolean isEscaped) { -// try -// { -// if (this.videoComponent != null) -// { -// GuiActivator.getMediaService() -// .disposeLocalVideoComponent(this.videoComponent); -// this.videoComponent = null; -// } -// } -// catch (MediaException e) -// { -// // Better manager the exception ! -// e.printStackTrace(); -// } - } - - public void videoAdded(VideoEvent event) - { - // Here is the important part. With this event, you get the component - // containing the video ! - - // You need to keep it. The returned componant is the key for all - // the other methods ! - this.videoComponent = event.getVisualComponent(); - - // Add the component in the container - this.videoContainer.add(this.videoComponent); - - this.grabSnapshot.setEnabled(true); - } - - public void videoRemoved(VideoEvent event) - { - // In case of the video is removed elsewhere - this.videoComponent = null; + this.videoContainer = null; + dispose(); } public void actionPerformed(ActionEvent e) @@ -220,21 +182,21 @@ public void actionPerformed(ActionEvent e) if (actName.equals("grab")) { - if (this.videoComponent == null) - return; - this.grabSnapshot.setEnabled(false); new SnapshotTimer().start(); } else { close(false); - this.setVisible(false); + dispose(); } } - + + /** + * This thread grabs the snapshot by counting down. + */ private class SnapshotTimer - extends Thread + extends Thread { @Override public void run() @@ -250,7 +212,7 @@ public void run() } catch (InterruptedException e) { - e.printStackTrace(); + logger.error("", e); } } @@ -262,7 +224,10 @@ public void run() } } - + + /** + * These are the images shown as timer while grabbing the snapshot. + */ private class TimerImage extends JComponent { diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java index 597f8f112..91ac82a52 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java @@ -5,20 +5,21 @@ */ package net.java.sip.communicator.impl.neomedia; +import java.util.*; + import java.awt.*; import java.awt.event.*; import java.io.*; import javax.media.*; import javax.media.MediaException; -import javax.media.control.*; -import javax.media.protocol.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; -import net.java.sip.communicator.impl.neomedia.codec.video.*; import net.java.sip.communicator.impl.neomedia.device.*; +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.swing.*; @@ -58,12 +59,6 @@ public class MediaConfiguration */ private static CaptureDeviceInfo videoDeviceInPreview; - /** - * The Player depicting the preview of the currently selected - * CaptureDeviceInfo. - */ - private static Player videoPlayerInPreview; - /** * Returns the audio configuration panel. * @return the audio configuration panel @@ -82,65 +77,6 @@ public static Component createVideoConfigPanel() return createControls(DeviceConfigurationComboBoxModel.VIDEO); } - /** - * Listens and shows the video in the video container when needed. - * @param event the event when player has ready visual component. - * @param videoContainer the container. - */ - private static void controllerUpdateForPreview(ControllerEvent event, - Container videoContainer) - { - if (event instanceof ConfigureCompleteEvent) - { - Processor player = (Processor) event.getSourceController(); - - /* - * Use SwScaler for the scaling since it produces an image with - * better quality. - */ - TrackControl[] trackControls = player.getTrackControls(); - - if ((trackControls != null) && (trackControls.length != 0)) - try - { - for (TrackControl trackControl : trackControls) - { - SwScaler playerScaler = new SwScaler(); - - trackControl.setCodecChain( - new Codec[] { playerScaler }); - break; - } - } - catch (UnsupportedPlugInException upiex) - { - logger.warn("Failed to add SwScaler to codec chain", upiex); - } - - // Turn the Processor into a Player. - try - { - player.setContentDescriptor(null); - } - catch (NotConfiguredError nce) - { - logger.error( - "Failed to set ContentDescriptor of Processor", - nce); - } - - player.realize(); - } - else if (event instanceof RealizeCompleteEvent) - { - Player player = (Player) event.getSourceController(); - Component video = player.getVisualComponent(); - - showPreview(videoContainer, video, player); - player.start(); - } - } - /** * Creates the ui controls for portaudio. * @param portAudioPanel the panel @@ -439,47 +375,38 @@ else if (source == downButton) * @throws MediaException a problem getting preview. */ private static void createPreview(CaptureDeviceInfo device, - final Container videoContainer) + final JComponent videoContainer) throws IOException, MediaException { videoContainer.removeAll(); - if (videoPlayerInPreview != null) - disposePlayer(videoPlayerInPreview); + + videoContainer.revalidate(); + videoContainer.repaint(); if (device == null) return; - DataSource dataSource = Manager.createDataSource(device.getLocator()); - Dimension size = videoContainer.getPreferredSize(); - - /* - * Don't let the size be uselessly small just because the videoContainer - * has too small a preferred size. - */ - if ((size.width < 128) || (size.height < 96)) + Iterator mDevsIter = + NeomediaActivator.getMediaServiceImpl() + .getDevices(MediaType.VIDEO, MediaUseCase.ANY) + .iterator(); + while(mDevsIter.hasNext()) { - size.width = 128; - size.height = 96; - } - VideoMediaStreamImpl - .selectVideoSize(dataSource, size.width, size.height); - - // A Player is documented to be created on a connected DataSource. - dataSource.connect(); - - Processor player = Manager.createProcessor(dataSource); + MediaDeviceImpl dev = (MediaDeviceImpl)mDevsIter.next(); + if(dev.getCaptureDeviceInfo().equals(device)) + { + Component c = (Component)NeomediaActivator.getMediaServiceImpl() + .getVideoPreviewComponent( + dev, + videoContainer.getSize().width, + videoContainer.getSize().height); - videoPlayerInPreview = player; + videoContainer.add(c); - player.addControllerListener(new ControllerListener() - { - public void controllerUpdate(ControllerEvent event) - { - controllerUpdateForPreview(event, videoContainer); + break; } - }); - player.configure(); + } } /** @@ -490,7 +417,7 @@ public void controllerUpdate(ControllerEvent event) */ private static Component createPreview(int type, final JComboBox comboBox) { - final Container preview; + final JComponent preview; if (type == DeviceConfigurationComboBoxModel.VIDEO) { JLabel noPreview = @@ -523,7 +450,7 @@ public void actionPerformed(ActionEvent event) Exception exception; try - { + { createPreview(device, preview); exception = null; } @@ -566,7 +493,21 @@ public void hierarchyChanged(HierarchyEvent event) & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) && comboBox.isDisplayable()) - comboBoxListener.actionPerformed(null); + { + // let current changes end their execution + // and after that trigger action on combobox + SwingUtilities.invokeLater(new Runnable(){ + public void run() + { + comboBoxListener.actionPerformed(null); + } + }); + } + else + { + if(!comboBox.isDisplayable()) + videoDeviceInPreview = null; + } } }); } else @@ -580,26 +521,11 @@ public void hierarchyChanged(HierarchyEvent event) * @param noVideoComponent the container component. * @return the video container. */ - private static Container createVideoContainer(Component noVideoComponent) + private static JComponent createVideoContainer(Component noVideoComponent) { return new VideoContainer(noVideoComponent); } - /** - * Dispose the player used for the preview. - * @param player the player. - */ - private static void disposePlayer(Player player) - { - player.stop(); - player.deallocate(); - player.close(); - - if ((videoPlayerInPreview != null) - && videoPlayerInPreview.equals(player)) - videoPlayerInPreview = null; - } - /** * The mnemonic for a type. * @param type audio or video type. @@ -661,101 +587,4 @@ private static void move(JTable table, boolean up) .getSelectedRow(), up); table.getSelectionModel().setSelectionInterval(index, index); } - - /** - * Shows the preview panel. - * @param previewContainer the container - * @param preview the preview component. - * @param player the player. - */ - private static 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); - videoDeviceInPreview = null; - - /* - * We've just disposed the player which created the preview - * component so the preview component is of no use - * regardless of whether the Media configuration form will - * be redisplayed or not. And since the preview component - * appears to be a huge object even after its player is - * disposed, make sure to not reference it. - */ - previewContainer.remove(preview); - } - - public void hierarchyChanged(HierarchyEvent event) - { - if ((event.getChangeFlags() - & HierarchyEvent.DISPLAYABILITY_CHANGED) - == 0) - return; - - if (!preview.isDisplayable()) - { - dispose(); - return; - } - - if (windowListener == null) - { - window = SwingUtilities.windowForComponent(preview); - if (window != null) - { - windowListener = new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent event) - { - dispose(); - } - }; - window.addWindowListener(windowListener); - } - } - } - }; - preview.addHierarchyListener(hierarchyListener); - - previewContainer.add(preview); - } - else - disposePlayer(player); - } } diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java index 16414ad3b..45ad7b99a 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java @@ -6,16 +6,25 @@ */ package net.java.sip.communicator.impl.neomedia; +import java.awt.*; +import java.awt.event.*; import java.util.*; +import java.util.List; import javax.media.*; +import javax.media.control.*; +import javax.media.protocol.*; +import javax.swing.*; import net.java.sip.communicator.impl.neomedia.codec.*; +import net.java.sip.communicator.impl.neomedia.codec.video.*; 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.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.swing.*; /** * Implements MediaService for JMF. @@ -26,6 +35,11 @@ public class MediaServiceImpl implements MediaService { + /** + * The logger. + */ + private static final Logger logger + = Logger.getLogger(MediaServiceImpl.class); /** * With this property video support can be disabled (enabled by default). @@ -608,4 +622,246 @@ public Map getDynamicPayloadTypePreferences() } return dynamicPayloadTypePreferences; } + + /** + * Creates a preview component for the specified device(video device) used + * to show video preview from that device. + * + * @param device the video device + * @param preferredWidth the width we prefer for the component + * @param preferredHeight the height we prefer for the component + * @return the preview component. + */ + public Object getVideoPreviewComponent(MediaDevice device + , int preferredWidth, int preferredHeight) + { + JLabel noPreview = + new JLabel(NeomediaActivator.getResources().getI18NString( + "impl.media.configform.NO_PREVIEW")); + noPreview.setHorizontalAlignment(SwingConstants.CENTER); + noPreview.setVerticalAlignment(SwingConstants.CENTER); + final JComponent videoContainer + = new VideoContainer(noPreview); + + videoContainer.setPreferredSize( + new Dimension(preferredWidth, preferredHeight)); + videoContainer.setMaximumSize( + new Dimension(preferredWidth, preferredHeight)); + + try + { + if (device == null || + ((MediaDeviceImpl)device).getCaptureDeviceInfo() == null) + { + return videoContainer; + } + + DataSource dataSource = Manager.createDataSource( + ((MediaDeviceImpl)device).getCaptureDeviceInfo().getLocator()); + + /* + * Don't let the size be uselessly small just because the videoContainer + * has too small a preferred size. + */ + if ((preferredWidth < 128) || (preferredHeight < 96)) + { + preferredHeight = 128; + preferredWidth = 96; + } + VideoMediaStreamImpl + .selectVideoSize(dataSource, preferredWidth, preferredHeight); + + // A Player is documented to be created on a connected DataSource. + dataSource.connect(); + + Processor player = Manager.createProcessor(dataSource); + + player.addControllerListener(new ControllerListener() + { + public void controllerUpdate(ControllerEvent event) + { + controllerUpdateForPreview(event, videoContainer); + } + }); + player.configure(); + + return videoContainer; + } + catch(Exception e) + { + e.printStackTrace(); + } + + return null; + } + + /** + * Listens and shows the video in the video container when needed. + * @param event the event when player has ready visual component. + * @param videoContainer the container. + */ + private static void controllerUpdateForPreview(ControllerEvent event, + JComponent videoContainer) + { + if (event instanceof ConfigureCompleteEvent) + { + Processor player = (Processor) event.getSourceController(); + + /* + * Use SwScaler for the scaling since it produces an image with + * better quality. + */ + TrackControl[] trackControls = player.getTrackControls(); + + if ((trackControls != null) && (trackControls.length != 0)) + try + { + for (TrackControl trackControl : trackControls) + { + trackControl.setCodecChain( + new Codec[] { new SwScaler() }); + break; + } + } + catch (UnsupportedPlugInException upiex) + { + logger.warn("Failed to add SwScaler to codec chain", upiex); + } + + // Turn the Processor into a Player. + try + { + player.setContentDescriptor(null); + } + catch (NotConfiguredError nce) + { + logger.error( + "Failed to set ContentDescriptor of Processor", + nce); + } + + player.realize(); + } + else if (event instanceof RealizeCompleteEvent) + { + Player player = (Player) event.getSourceController(); + Component video = player.getVisualComponent(); + + showPreview(videoContainer, video, player); + } + } + + /** + * Shows the preview panel. + * @param previewContainer the container + * @param preview the preview component. + * @param player the player. + */ + private static void showPreview(final JComponent 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); + + /* + * We've just disposed the player which created the preview + * component so the preview component is of no use + * regardless of whether the Media configuration form will + * be redisplayed or not. And since the preview component + * appears to be a huge object even after its player is + * disposed, make sure to not reference it. + */ + previewContainer.remove(preview); + } + + public void hierarchyChanged(HierarchyEvent event) + { + if ((event.getChangeFlags() + & HierarchyEvent.DISPLAYABILITY_CHANGED) + == 0) + return; + + if (!preview.isDisplayable()) + { + dispose(); + return; + } + else + { + player.start(); + } + + if (windowListener == null) + { + window = SwingUtilities.windowForComponent(preview); + if (window != null) + { + windowListener = new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent event) + { + dispose(); + } + }; + window.addWindowListener(windowListener); + } + } + } + }; + preview.addHierarchyListener(hierarchyListener); + + previewContainer.add(preview); + + previewContainer.revalidate(); + previewContainer.repaint(); + } + else + disposePlayer(player); + } + + /** + * Dispose the player used for the preview. + * @param player the player. + */ + private static void disposePlayer(Player player) + { + player.stop(); + player.deallocate(); + player.close(); + } } diff --git a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java index 014ce4c68..70758ae0b 100644 --- a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetServerStoredAccountInfoMsnImpl.java @@ -34,7 +34,7 @@ public class OperationSetServerStoredAccountInfoMsnImpl Logger.getLogger(OperationSetServerStoredAccountInfoMsnImpl.class); /** - * The jabber provider that created us. + * The msn provider that created us. */ private ProtocolProviderServiceMsnImpl msnProvider = null; /** @@ -499,14 +499,14 @@ private void assertConnected() if (msnProvider == null) { throw new IllegalStateException( - "The jabber provider must be non-null and signed on " + "The msn provider must be non-null and signed on " + "before being able to communicate."); } if (!msnProvider.isRegistered()) { throw new IllegalStateException( - "The jabber provider must be signed on before " + "The msn provider must be signed on before " + "being able to communicate."); } } diff --git a/src/net/java/sip/communicator/service/neomedia/MediaService.java b/src/net/java/sip/communicator/service/neomedia/MediaService.java index 86a6ae5f3..e923fa092 100644 --- a/src/net/java/sip/communicator/service/neomedia/MediaService.java +++ b/src/net/java/sip/communicator/service/neomedia/MediaService.java @@ -6,7 +6,9 @@ */ package net.java.sip.communicator.service.neomedia; +import java.awt.*; import java.util.*; +import java.util.List; import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -178,4 +180,16 @@ public MediaStream createMediaStream(StreamConnector connector, * payload type number. */ public Map getDynamicPayloadTypePreferences(); + + /** + * Creates a preview component for the specified device(video device) used + * to show video preview from it. + * + * @param device the video device + * @param preferredWidth the width we prefer for the component + * @param preferredHeight the height we prefer for the component + * @return the preview component. + */ + public Object getVideoPreviewComponent( + MediaDevice device, int preferredWidth, int preferredHeight); }