From ebc08c20750cd3f5401f8d689f0b02d2ecfcfab6 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Tue, 21 Feb 2012 20:23:19 +0000 Subject: [PATCH] Fixes issues with video conferences such as adding peers and allowing non-focus peers to start their video first. --- build.xml | 2 +- .../main/call/AbstractCallToggleButton.java | 21 ++- .../impl/gui/main/call/CallManager.java | 42 +++--- .../impl/gui/main/call/CallPanel.java | 70 ++++----- .../impl/gui/main/call/UIVideoHandler.java | 10 +- .../call/conference/ConferenceCallPanel.java | 43 ++++-- .../impl/neomedia/AbstractRTPConnector.java | 8 +- .../impl/neomedia/AudioMediaStreamImpl.java | 7 +- .../impl/neomedia/MediaStreamImpl.java | 136 +++++++++--------- .../impl/neomedia/RTPTranslatorImpl.java | 36 +++-- .../device/VideoMediaDeviceSession.java | 20 ++- .../transform/csrc/CsrcTransformEngine.java | 1 - .../transform/rtcp/StatisticsEngine.java | 12 +- .../impl/protocol/jabber/CallJabberImpl.java | 33 ++--- .../OperationSetBasicTelephonyJabberImpl.java | 10 +- ...ationSetContactCapabilitiesJabberImpl.java | 28 ++-- ...ionSetTelephonyConferencingJabberImpl.java | 21 ++- .../OperationSetVideoTelephonyJabberImpl.java | 12 +- .../ProtocolProviderServiceJabberImpl.java | 18 +-- .../AbstractProtocolProviderService.java | 22 +-- .../AbstractOperationSetVideoTelephony.java | 2 +- .../protocol/media/CallPeerMediaHandler.java | 10 +- .../protocol/media/MediaAwareCall.java | 39 +++-- 23 files changed, 336 insertions(+), 267 deletions(-) diff --git a/build.xml b/build.xml index ac4e37006..308666685 100644 --- a/build.xml +++ b/build.xml @@ -433,7 +433,7 @@ diff --git a/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java b/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java index 4bfb09823..13d217dda 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java @@ -211,20 +211,19 @@ public CallToggleButtonModel(Call call) public synchronized void actionPerformed(ActionEvent event) { - if(!spawnActionInNewThread) + if(spawnActionInNewThread) { - buttonPressed(); - return; - } - - if (runner == null) - { - runner = new Thread(this, LocalVideoButton.class.getName()); - runner.setDaemon(true); + if (runner == null) + { + runner = new Thread(this, LocalVideoButton.class.getName()); + runner.setDaemon(true); - setEnabled(false); - runner.start(); + setEnabled(false); + runner.start(); + } } + else + buttonPressed(); } public void run() diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java index 30f89e6d4..e67147689 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java @@ -325,14 +325,10 @@ public static void enableLocalVideo(Call call, boolean enable) public static boolean isLocalVideoEnabled(Call call) { OperationSetVideoTelephony telephony - = call.getProtocolProvider() - .getOperationSet(OperationSetVideoTelephony.class); - - if (telephony != null - && telephony.isLocalVideoAllowed(call)) - return true; + = call.getProtocolProvider().getOperationSet( + OperationSetVideoTelephony.class); - return false; + return (telephony != null) && telephony.isLocalVideoAllowed(call); } /** @@ -343,8 +339,8 @@ public static boolean isLocalVideoEnabled(Call call) * @param contact the contact to call to */ public static void createDesktopSharing( - ProtocolProviderService protocolProvider, - String contact) + ProtocolProviderService protocolProvider, + String contact) { // If the user presses cancel on the desktop sharing warning then we // have nothing more to do here. @@ -352,16 +348,16 @@ public static void createDesktopSharing( return; MediaService mediaService = GuiActivator.getMediaService(); - - List desktopDevices = mediaService.getDevices( - MediaType.VIDEO, MediaUseCase.DESKTOP); - + List desktopDevices + = mediaService.getDevices(MediaType.VIDEO, MediaUseCase.DESKTOP); int deviceNumber = desktopDevices.size(); if (deviceNumber == 1) { createDesktopSharing( - protocolProvider, contact, desktopDevices.get(0)); + protocolProvider, + contact, + desktopDevices.get(0)); } else if (deviceNumber > 1) { @@ -369,11 +365,11 @@ else if (deviceNumber > 1) = new SelectScreenDialog(desktopDevices); selectDialog.setVisible(true); - if (selectDialog.getSelectedDevice() != null) - createDesktopSharing( protocolProvider, - contact, - selectDialog.getSelectedDevice()); + createDesktopSharing( + protocolProvider, + contact, + selectDialog.getSelectedDevice()); } } @@ -1848,7 +1844,7 @@ private static class EnableLocalVideoThread { private final Call call; - private boolean enable; + private final boolean enable; /** * Creates the enable local video call thread. @@ -1878,20 +1874,16 @@ public void run() { getActiveCallContainer(call).setDesktopSharingButtonSelected( false); + JFrame frame = DesktopSharingFrame.getFrameForCall(call); if(frame != null) - { frame.dispose(); - } } try { - telephony.setLocalVideoAllowed( - call, - enable); - + telephony.setLocalVideoAllowed(call, enable); enableSucceeded = true; } catch (OperationFailedException ex) diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java index 3af107a0d..8bec33b64 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java @@ -149,9 +149,10 @@ public class CallPanel /** * The conference button. */ - private SIPCommButton conferenceButton = new SIPCommButton( - ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG), - ImageLoader.getImage(ImageLoader.ADD_TO_CALL_BUTTON)); + private SIPCommButton conferenceButton + = new SIPCommButton( + ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG), + ImageLoader.getImage(ImageLoader.ADD_TO_CALL_BUTTON)); /** * Chat button. @@ -266,9 +267,7 @@ public CallPanel(Call call, CallContainer callWindow) // the focus state of each call peer. Iterator callPeers = call.getCallPeers(); while (callPeers.hasNext()) - { callPeers.next().addCallPeerConferenceListener(this); - } // Initializes all buttons and common panels. init(); @@ -417,9 +416,7 @@ public void actionPerformed(ActionEvent evt) else if (buttonName.equals(DIAL_BUTTON)) { if (dialpadDialog == null) - { dialpadDialog = this.getDialpadDialog(); - } if(!dialpadDialog.isVisible()) { @@ -445,10 +442,7 @@ else if (buttonName.equals(DIAL_BUTTON)) } else if (buttonName.equals(CONFERENCE_BUTTON)) { - ConferenceInviteDialog inviteDialog - = new ConferenceInviteDialog(call); - - inviteDialog.setVisible(true); + new ConferenceInviteDialog(call).setVisible(true); } else if (buttonName.equals(CHAT_BUTTON)) { @@ -706,8 +700,8 @@ public void enableButtons(boolean enable) ProtocolProviderService protocolProvider = call.getProtocolProvider(); - if (protocolProvider.getOperationSet( - OperationSetVideoTelephony.class) != null) + if (protocolProvider.getOperationSet(OperationSetVideoTelephony.class) + != null) { videoButton.setEnabled(enable); } @@ -724,7 +718,8 @@ public void enableButtons(boolean enable) } if (protocolProvider.getOperationSet( - OperationSetAdvancedTelephony.class) != null) + OperationSetAdvancedTelephony.class) + != null) { transferCallButton.setEnabled(enable); } @@ -808,22 +803,18 @@ public void callPeerRemoved(CallPeerEvent evt) callPeer.removeCallPeerConferenceListener(this); - Timer timer = new Timer(5000, - new RemovePeerPanelListener(callPeer)); + Timer timer = new Timer(5000, new RemovePeerPanelListener(callPeer)); timer.setRepeats(false); timer.start(); // The call is finished when that last peer is removed. if (call.getCallPeerCount() == 0) - { this.stopCallTimer(); - } } public void callStateChanged(CallChangeEvent evt) - { - } + {} /** * Updates CallPeer related components to fit the new focus state. @@ -1002,7 +993,7 @@ public String getCallTitle() private class RemovePeerPanelListener implements ActionListener { - private CallPeer peer; + private final CallPeer peer; public RemovePeerPanelListener(CallPeer peer) { @@ -1112,15 +1103,29 @@ public void refreshContainer() // whole window to freeze. // We check also if the vertical scroll bar is visible in order to // correctly pack the window when a peer is removed. - boolean isScrollBarVisible = (callPanel instanceof ConferenceCallPanel) - && ((ConferenceCallPanel) callPanel).getVerticalScrollBar() != null - && ((ConferenceCallPanel) callPanel).getVerticalScrollBar() - .isVisible(); - - if (!isScrollBarVisible - || getHeight() - < GraphicsEnvironment.getLocalGraphicsEnvironment() - .getMaximumWindowBounds().height) + boolean isScrollBarVisible; + + if (callPanel instanceof ConferenceCallPanel) + { + Component scrollBar + = ((ConferenceCallPanel) callPanel).getVerticalScrollBar(); + + isScrollBarVisible = ((scrollBar != null) && scrollBar.isVisible()); + } + else + isScrollBarVisible = false; + + /* + * Repacking should be done only when the callWindow is not high enough + * to not have a vertical scroll bar and there is still room left to + * expand its height without going out of the screen. + */ + if (isScrollBarVisible + && (getHeight() + < GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getMaximumWindowBounds() + .height)) callWindow.pack(); else repaint(); @@ -1195,11 +1200,6 @@ private void updateCurrentCallPanel(JComponent callPanel) */ private void removeOneToOneSpecificComponents() { - // If we want to enable video in conference calls we need to comment - // these lines. - if (videoButton.isSelected()) - videoButton.doClick(); - // Disable desktop sharing. if (desktopSharingButton.isSelected()) desktopSharingButton.doClick(); diff --git a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java index 6167e3961..1889c7db3 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java @@ -20,7 +20,6 @@ import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.swing.*; -import net.java.sip.communicator.util.swing.transparent.*; /** * The UIVideoHandler is meant to handle all video related events. @@ -337,13 +336,11 @@ else if (event.getNewValue().equals(MediaDirection.SENDRECV)) if (CallManager.isDesktopSharingEnabled(call)) { callContainer.setDesktopSharingButtonSelected(true); - if (CallManager.isRegionDesktopSharingEnabled(call)) { - TransparentFrame frame = DesktopSharingFrame - .createTransparentFrame(call, false); - - frame.setVisible(true); + DesktopSharingFrame + .createTransparentFrame(call, false) + .setVisible(true); } } else if (CallManager.isLocalVideoEnabled(call)) @@ -630,7 +627,6 @@ private void handleLocalVideoStreamingChange( { if (videoTelephony == null) return; - if (callPeer == null || callPeer.getCall() == null) return; diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceCallPanel.java index 4aa450cfc..85db6f0da 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceCallPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceCallPanel.java @@ -126,20 +126,6 @@ public ConferenceCallPanel(CallPanel callPanel, Call c) scrollPane.setOpaque(false); scrollPane.getViewport().setOpaque(false); - this.addLocalCallPeer(); - - Iterator iterator = this.call.getCallPeers(); - - while (iterator.hasNext()) - this.addCallPeerPanel(iterator.next()); - - iterator = this.call.getCrossProtocolCallPeers(); - - while (iterator.hasNext()) - { - this.addCallPeerPanel(iterator.next()); - } - scrollPane.setBorder(null); /* * The scrollPane seems to receive only a few pixels of width at times @@ -161,6 +147,22 @@ public ConferenceCallPanel(CallPanel callPanel, Call c) add(scrollPane, scrollPaneGridBagConstraints); addVideoContainer(); + + /* + * XXX Call addCallPeerPanel(CallPeer) after calling addVideoContainer() + * because the video may already be flowing between the CallPeers. + * Otherwise, the videos of the remote CallPeers will not be shown. + */ + addLocalCallPeer(); + + Iterator iterator; + + iterator = this.call.getCallPeers(); + while (iterator.hasNext()) + addCallPeerPanel(iterator.next()); + iterator = this.call.getCrossProtocolCallPeers(); + while (iterator.hasNext()) + addCallPeerPanel(iterator.next()); } /** @@ -220,7 +222,7 @@ public void componentAdded(ContainerEvent e) } /* - * When the first visual/videoComponent gets added, this + * When the first visual/video Component gets added, this * videoContainer is still not accommodated by the frame * size because it has just become visible. So try to resize * the frame to accommodate this videoContainer. @@ -574,6 +576,17 @@ public void ensureSize(Component component, int width, int height) { Frame frame = CallPeerRendererUtils.getFrame(component); + /* + * CallPanel creates ConferenceCallPanel and then adds it to the UI + * hierarchy. If the associated Call has just become a conference focus + * and the UI is being updated to reflect the change, the existing + * CallPeer of the Call may cause this method to be called and then this + * ConferenceCallPanel will not have an associated Frame at the time. + * But callPanel will (likely) have one. + */ + if ((frame == null) && (callPanel != null)) + frame = CallPeerRendererUtils.getFrame(callPanel); + if (frame == null) return; else if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH) diff --git a/src/net/java/sip/communicator/impl/neomedia/AbstractRTPConnector.java b/src/net/java/sip/communicator/impl/neomedia/AbstractRTPConnector.java index 73dc8dadd..87e97a249 100755 --- a/src/net/java/sip/communicator/impl/neomedia/AbstractRTPConnector.java +++ b/src/net/java/sip/communicator/impl/neomedia/AbstractRTPConnector.java @@ -13,8 +13,12 @@ import net.java.sip.communicator.service.neomedia.*; /** + * Provides a base/default implementation of RTPConnector which has + * factory methods for its control and data input and output streams and has an + * associated StreamConnector. + * * @author Bing SU (nova.su@gmail.com) - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public abstract class AbstractRTPConnector implements RTPConnector @@ -95,13 +99,11 @@ public void close() dataOutputStream.close(); dataOutputStream = null; } - if (controlOutputStream != null) { controlOutputStream.close(); controlOutputStream = null; } - if (dataInputStream != null) { dataInputStream.close(); diff --git a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java index ea2050433..146a6cd89 100644 --- a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java @@ -348,11 +348,12 @@ public void addRTPExtension(byte extensionID, RTPExtension rtpExtension) { super.addRTPExtension(extensionID, rtpExtension); - if ( RTPExtension.CSRC_AUDIO_LEVEL_URN - .equals(rtpExtension.getURI().toString())) + if (RTPExtension.CSRC_AUDIO_LEVEL_URN.equals( + rtpExtension.getURI().toString())) { getCsrcEngine().setCsrcAudioLevelAudioLevelExtensionID( - extensionID, rtpExtension.getDirection()); + extensionID, + rtpExtension.getDirection()); } } diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java index 8a56c5bee..9a804f214 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java @@ -332,12 +332,11 @@ protected void configureRTPManagerBufferControl( private TransformEngineChain createTransformEngineChain() { ArrayList engineChain - = new ArrayList(3); + = new ArrayList(4); // CSRCs and audio levels if (csrcEngine == null) csrcEngine = new CsrcTransformEngine(this); - engineChain.add(csrcEngine); // DTMF @@ -347,7 +346,7 @@ private TransformEngineChain createTransformEngineChain() engineChain.add(dtmfEngine); // RTCP Statistics - if(statisticsEngine == null) + if (statisticsEngine == null) statisticsEngine = new StatisticsEngine(this); engineChain.add(statisticsEngine); @@ -503,7 +502,7 @@ public void close() srtpControl.cleanup(); - if(csrcEngine != null) + if (csrcEngine != null) { csrcEngine.stop(); csrcEngine = null; @@ -515,7 +514,7 @@ public void close() if (rtpManager != null) { - if(logger.isInfoEnabled()) + if (logger.isInfoEnabled()) printFlowStatistics(rtpManager); rtpManager.removeReceiveStreamListener(this); @@ -843,7 +842,6 @@ private void doSetTarget(MediaStreamTarget target) } else targetIsSet = true; - if (targetIsSet) { rtpConnectorTarget = target; @@ -859,6 +857,7 @@ private void doSetTarget(MediaStreamTarget target) + target); } } + /** * Gets the MediaDevice that this stream uses to play back and * capture media. @@ -1095,8 +1094,6 @@ private StreamRTPManager getRTPManager() if (bc != null) configureRTPManagerBufferControl(rtpManager, bc); - //Emil: if you replace this method with another init method make - //sure you check that the line below still works. rtpManager.initialize(rtpConnector); /* @@ -1256,74 +1253,74 @@ public void setConnector(StreamConnector connector) if (connector == null) throw new NullPointerException("connector"); - if (rtpConnector != null) - { - // Is the StreamConnector really changing? - if (rtpConnector.getConnector() == connector) - return; - } - AbstractRTPConnector oldValue = rtpConnector; - if(connector.getProtocol() == StreamConnector.Protocol.UDP) + // Is the StreamConnector really changing? + if ((oldValue != null) && (oldValue.getConnector() == connector)) + return; + + switch (connector.getProtocol()) { + case UDP: rtpConnector = new RTPTransformUDPConnector(connector) - { - @Override - protected TransformUDPOutputStream createDataOutputStream() - throws IOException { - TransformUDPOutputStream dataOutputStream - = super.createDataOutputStream(); + @Override + protected TransformUDPOutputStream createDataOutputStream() + throws IOException + { + TransformUDPOutputStream dataOutputStream + = super.createDataOutputStream(); - if (dataOutputStream != null) - configureDataOutputStream(dataOutputStream); - return dataOutputStream; - } + if (dataOutputStream != null) + configureDataOutputStream(dataOutputStream); + return dataOutputStream; + } - @Override - protected TransformUDPInputStream createDataInputStream() - throws IOException - { - TransformUDPInputStream dataInputStream - = super.createDataInputStream(); + @Override + protected TransformUDPInputStream createDataInputStream() + throws IOException + { + TransformUDPInputStream dataInputStream + = super.createDataInputStream(); - if (dataInputStream != null) - configureDataInputStream(dataInputStream); - return dataInputStream; - } - }; - } - else if(connector.getProtocol() == StreamConnector.Protocol.TCP) - { + if (dataInputStream != null) + configureDataInputStream(dataInputStream); + return dataInputStream; + } + }; + break; + case TCP: rtpConnector = new RTPTransformTCPConnector(connector) - { - @Override - protected TransformTCPOutputStream createDataOutputStream() - throws IOException { - TransformTCPOutputStream dataOutputStream - = super.createDataOutputStream(); + @Override + protected TransformTCPOutputStream createDataOutputStream() + throws IOException + { + TransformTCPOutputStream dataOutputStream + = super.createDataOutputStream(); - if (dataOutputStream != null) - configureDataOutputStream(dataOutputStream); - return dataOutputStream; - } + if (dataOutputStream != null) + configureDataOutputStream(dataOutputStream); + return dataOutputStream; + } - @Override - protected TransformTCPInputStream createDataInputStream() - throws IOException - { - TransformTCPInputStream dataInputStream - = super.createDataInputStream(); + @Override + protected TransformTCPInputStream createDataInputStream() + throws IOException + { + TransformTCPInputStream dataInputStream + = super.createDataInputStream(); - if (dataInputStream != null) - configureDataInputStream(dataInputStream); - return dataInputStream; - } - }; + if (dataInputStream != null) + configureDataInputStream(dataInputStream); + return dataInputStream; + } + }; + break; + default: + throw new IllegalArgumentException("connector"); } rtpConnectorChanged(oldValue, rtpConnector); @@ -1362,7 +1359,7 @@ public void setDevice(MediaDevice device) startedDirection = deviceSession.getStartedDirection(); deviceSession.removePropertyChangeListener( - deviceSessionPropertyChangeListener); + deviceSessionPropertyChangeListener); // keep player active deviceSession.setDisposePlayerOnClose( @@ -1738,10 +1735,13 @@ private void startSendStreams() { try { + DataSource sendStreamDataSource + = sendStream.getDataSource(); + // TODO Are we sure we want to connect here? - sendStream.getDataSource().connect(); + sendStreamDataSource.connect(); sendStream.start(); - sendStream.getDataSource().start(); + sendStreamDataSource.start(); if (logger.isTraceEnabled()) { @@ -1752,8 +1752,7 @@ private void startSendStreams() } catch (IOException ioe) { - logger - .warn("Failed to start stream " + sendStream, ioe); + logger.warn("Failed to start stream " + sendStream, ioe); } } } @@ -2371,6 +2370,9 @@ private void printFlowStatistics(StreamRTPManager rtpManager) */ public void setRTPTranslator(RTPTranslator rtpTranslator) { - this.rtpTranslator = rtpTranslator; + if (this.rtpTranslator != rtpTranslator) + { + this.rtpTranslator = rtpTranslator; + } } } diff --git a/src/net/java/sip/communicator/impl/neomedia/RTPTranslatorImpl.java b/src/net/java/sip/communicator/impl/neomedia/RTPTranslatorImpl.java index 33cf5ae25..9fb4a3646 100644 --- a/src/net/java/sip/communicator/impl/neomedia/RTPTranslatorImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTPTranslatorImpl.java @@ -441,17 +441,37 @@ public void removeSessionListener( // TODO Auto-generated method stub } + /** + * Notifies this ReceiveStreamListener about a specific event + * related to a ReceiveStream. + * + * @param event a ReceiveStreamEvent which contains the specifics + * of the event this ReceiveStreamListener is being notified about + * @see ReceiveStreamListener#update(ReceiveStreamEvent) + */ public void update(ReceiveStreamEvent event) { - StreamRTPManagerDesc streamRTPManagerDesc - = findStreamRTPManagerDescByReceiveSSRC( - event.getReceiveStream().getSSRC(), - null); + /* + * Because NullPointerException was seen during testing, be thorough + * with the null checks. + */ + if (event != null) + { + ReceiveStream receiveStream = event.getReceiveStream(); - if (streamRTPManagerDesc != null) - for (ReceiveStreamListener listener - : streamRTPManagerDesc.getReceiveStreamListeners()) - listener.update(event); + if (receiveStream != null) + { + StreamRTPManagerDesc streamRTPManagerDesc + = findStreamRTPManagerDescByReceiveSSRC( + receiveStream.getSSRC(), + null); + + if (streamRTPManagerDesc != null) + for (ReceiveStreamListener listener + : streamRTPManagerDesc.getReceiveStreamListeners()) + listener.update(event); + } + } } private static class OutputDataStreamDesc diff --git a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java index 5d8ef0613..efd0e2aaa 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -598,13 +598,29 @@ public Component createLocalVisualComponent() * it to the currently registered VideoListeners in a VideoEvent after * returning from the call. */ + Component localVisualComponent; + synchronized (localPlayerSyncRoot) { if (localPlayer == null) localPlayer = createLocalPlayer(); - return - (localPlayer == null) ? null : getVisualComponent(localPlayer); + localVisualComponent + = (localPlayer == null) + ? null + : getVisualComponent(localPlayer); } + /* + * If the local visual/video Component exists at this time, it has + * likely been created by a previous call to this method. However, the + * caller may still depend on a VIDEO_ADDED event being fired for it. + */ + if (localVisualComponent != null) + fireVideoEvent( + VideoEvent.VIDEO_ADDED, + localVisualComponent, + VideoEvent.LOCAL, + false); + return localVisualComponent; } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java index fc992d4a2..04b30d991 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java @@ -190,7 +190,6 @@ public void setCsrcAudioLevelAudioLevelExtensionID(byte extID, { this.csrcAudioLevelExtID = extID; this.audioLevelDirection = dir; - } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/rtcp/StatisticsEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/rtcp/StatisticsEngine.java index 191bb08e5..18e832ece 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/rtcp/StatisticsEngine.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/rtcp/StatisticsEngine.java @@ -8,6 +8,7 @@ import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.transform.*; +import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.util.*; import net.sf.fmj.media.rtp.*; @@ -112,17 +113,18 @@ public RawPacket transform(RawPacket pkt) lost = feedback.getNumLost(); - // As sender reports are send on every 5 seconds - // print every 4th packet, on every 20 seconds + // As sender reports are sent on every 5 seconds, print + // every 4th packet, on every 20 seconds. if(numberOfSenderReports % 4 != 1) return pkt; StringBuilder buff = new StringBuilder(RTP_STAT_PREFIX); + MediaFormat mediaStreamFormat = mediaStream.getFormat(); buff.append("Sending a report for ") - .append(mediaStream.getFormat() != null ? - mediaStream.getFormat().getMediaType() - : "") + .append(mediaStreamFormat != null + ? mediaStreamFormat.getMediaType() + : "") .append(" stream SSRC:") .append(feedback.getSSRC()) .append(" [packet count:") diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java index 4532842aa..88a23ba5b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java @@ -289,26 +289,30 @@ public CallPeerJabberImpl initiateSession( // if this was the first peer we added in this call then the call is // new and we also need to notify everyone of its creation. - if(getCallPeerCount() == 1 && getCallGroup() == null) + CallGroup callGroup = getCallGroup(); + + if ((getCallPeerCount() == 1) && (callGroup == null)) { parentOpSet.fireCallEvent(CallEvent.CALL_INITIATED, this); } - else if(getCallGroup() != null) + else if (callGroup != null) { // only TelephonyConferencing OperationSet should know about it - CallEvent cEvent = new CallEvent(this, CallEvent.CALL_INITIATED); - AbstractOperationSetTelephonyConferencing opSet = - (AbstractOperationSetTelephonyConferencing) - getProtocolProvider().getOperationSet( - OperationSetTelephonyConferencing.class); - if(opSet != null) - opSet.outgoingCallCreated(cEvent); + CallEvent event = new CallEvent(this, CallEvent.CALL_INITIATED); + AbstractOperationSetTelephonyConferencing opSet + = (AbstractOperationSetTelephonyConferencing) + getProtocolProvider() + .getOperationSet( + OperationSetTelephonyConferencing.class); + + if (opSet != null) + opSet.outgoingCallCreated(event); } CallPeerMediaHandlerJabberImpl mediaHandler = callPeer.getMediaHandler(); - /* enable video if it is a videocall */ + /* enable video if it is a video call */ mediaHandler.setLocalVideoTransmissionEnabled(localVideoAllowed); /* enable remote-control if it is a desktop sharing session */ mediaHandler.setLocalInputEvtAware(localInputEvtAware); @@ -320,22 +324,19 @@ else if(getCallGroup() != null) // if initializing session fails, set peer to failed boolean sessionInitiated = false; + try { - callPeer.initiateSession(sessionInitiateExtensions); sessionInitiated = true; - - return callPeer; } finally { // if initialization throws an exception - if(!sessionInitiated) - { + if (!sessionInitiated) callPeer.setState(CallPeerState.FAILED); - } } + return callPeer; } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java index f57abfcb1..beab81210 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -449,17 +449,19 @@ else if(di != null) // initiate call try { - if(isGingle) + if (isGingle) { logger.info("initiate Gingle call"); CallGTalkImpl callGTalk = new CallGTalkImpl(this); MediaUseCase useCase = call.getMediaUseCase(); boolean isVideo = call.isLocalVideoAllowed(useCase); - callGTalk.setCallGroup(call.getCallGroup()); + callGTalk.setCallGroup(call.getCallGroup()); callGTalk.setLocalVideoAllowed(isVideo, useCase); - peer = callGTalk.initiateGTalkSession(fullCalleeURI, - sessionInitiateExtensions); + peer + = callGTalk.initiateGTalkSession( + fullCalleeURI, + sessionInitiateExtensions); } else if(di != null) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java index 5803a1674..1a4da3256 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java @@ -22,7 +22,7 @@ * by the associated protocol provider to be capabilities possessed by the * Jabber Contact in question. * - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Yana Stamcheva */ public class OperationSetContactCapabilitiesJabberImpl @@ -318,14 +318,7 @@ private U getOperationSet(String jid, * should fail anyway). */ if (!online) - { - if (OFFLINE_OPERATION_SETS.contains(opsetClass)) - return opset; - else - { - return null; - } - } + return OFFLINE_OPERATION_SETS.contains(opsetClass) ? opset : null; /* * If we know the features required for the support of opsetClass, check @@ -351,19 +344,20 @@ private U getOperationSet(String jid, { if(CAPS_OPERATION_SETS_TO_FEATURES.containsKey(opsetClass)) { - String[] extFeatures = - CAPS_OPERATION_SETS_TO_FEATURES.get( - opsetClass); + String[] extFeatures + = CAPS_OPERATION_SETS_TO_FEATURES.get(opsetClass); // test GTalk - if(!parentProvider.isGTalkTesting()) + if (!parentProvider.isGTalkTesting()) { opset = null; } - else - if((extFeatures == null) || ((extFeatures.length != 0) && - !parentProvider.isExtFeatureListSupported(jid, - extFeatures))) + else if((extFeatures == null) + || ((extFeatures.length != 0) + && !parentProvider + .isExtFeatureListSupported( + jid, + extFeatures))) { opset = null; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java index ef509c794..228334521 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java @@ -123,7 +123,7 @@ protected void notifyAll(Call call) /** * Notifies all CallPeer associated with and established in a * specific call has occurred - * @param call the Call + * * @param callPeer the CallPeer */ private void notify(CallPeer callPeer) @@ -461,8 +461,10 @@ protected CallPeer inviteCalleeToCall( { if (!wasConferenceFocus && call.isConferenceFocus()) { - // reinvite other peers if any, to inform them that from now - // it is a conference call + /* + * Re-INVITE existing CallPeers to inform them that from now + * the specified call is a conference call. + */ Iterator callPeerIter = call.getCallPeers(); while (callPeerIter.hasNext()) @@ -473,10 +475,15 @@ protected CallPeer inviteCalleeToCall( } } - CoinPacketExtension confInfo = new CoinPacketExtension(true); - return getBasicTelephony().createOutgoingCall( - call, calleeAddress, - Arrays.asList(new PacketExtension[] { confInfo })); + return + getBasicTelephony().createOutgoingCall( + call, + calleeAddress, + Arrays.asList( + new PacketExtension[] + { + new CoinPacketExtension(true) + })); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java index c552f6361..fa2d91288 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java @@ -132,12 +132,16 @@ protected Call createOutgoingVideoCall(String calleeAddress) /* enable video */ call.setLocalVideoAllowed(true, getMediaUseCase()); - CallPeer callPeer = - basicTelephony.createOutgoingCall(call, calleeAddress); - // if call is a Google Talk ones, return the CallGTalkImpl + CallPeer callPeer + = basicTelephony.createOutgoingCall(call, calleeAddress); - return callPeer.getCall() == call ? call : callPeer.getCall(); + /* + * XXX OperationSetBasicTelephonyJabberImpl#createOutgoingCall( + * CallJabberImpl, String) may have replaced the CallJabberImpl instance + * created above with a CallGTalkImpl instance. + */ + return callPeer.getCall(); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index e49f83c33..98046af2b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -46,7 +46,7 @@ * * @author Damian Minkov * @author Symphorien Wanko - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Yana Stamcheva * @author Emil Ivov */ @@ -2433,13 +2433,15 @@ public SecurityAuthority getAuthority() */ public boolean isGTalkTesting() { - return (Boolean.getBoolean("gtalktesting") || - JabberActivator.getConfigurationService().getBoolean( - "net.java.sip.communicator.impl.protocol.jabber.gtalktesting" - , false) || - accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.IS_USE_GOOGLE_ICE, - true)); + return + Boolean.getBoolean("gtalktesting") + || JabberActivator.getConfigurationService().getBoolean( + "net.java.sip.communicator.impl.protocol.jabber" + + ".gtalktesting", + false) + || accountID.getAccountPropertyBoolean( + ProtocolProviderFactory.IS_USE_GOOGLE_ICE, + true); } UserCredentials getUserCredentials() diff --git a/src/net/java/sip/communicator/service/protocol/AbstractProtocolProviderService.java b/src/net/java/sip/communicator/service/protocol/AbstractProtocolProviderService.java index e8b09c348..a4423f57b 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractProtocolProviderService.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractProtocolProviderService.java @@ -16,7 +16,7 @@ * order to make it easier for implementers to provide complete solutions while * focusing on protocol-specific details. * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public abstract class AbstractProtocolProviderService implements ProtocolProviderService @@ -128,11 +128,8 @@ public void fireRegistrationStateChanged( RegistrationState oldState, if (logger.isDebugEnabled()) logger.debug( - "Dispatching " - + event - + " to " - + listeners.length - + " listeners."); + "Dispatching " + event + " to " + listeners.length + + " listeners."); for (RegistrationStateChangeListener listener : listeners) try @@ -141,7 +138,6 @@ public void fireRegistrationStateChanged( RegistrationState oldState, } catch (Throwable throwable) { - /* * The registration state has already changed and we're not * using the RegistrationStateChangeListeners to veto the change @@ -153,7 +149,10 @@ public void fireRegistrationStateChanged( RegistrationState oldState, if (throwable instanceof ThreadDeath) throw (ThreadDeath) throwable; logger.error( - "An error occurred while executing RegistrationStateChangeLister#registrationStateChanged(RegistrationStateChangeEvent) of " + "An error occurred while executing " + + "RegistrationStateChangeListener" + + "#registrationStateChanged" + + "(RegistrationStateChangeEvent) of " + listener, throwable); } @@ -188,9 +187,10 @@ public T getOperationSet(Class opsetClass) */ public String getProtocolDisplayName() { - String displayName = - getAccountID().getAccountPropertyString( - ProtocolProviderFactory.PROTOCOL); + String displayName + = getAccountID().getAccountPropertyString( + ProtocolProviderFactory.PROTOCOL); + return (displayName == null) ? getProtocolName() : displayName; } diff --git a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java index a11b33adb..39fb00a46 100644 --- a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java +++ b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java @@ -182,7 +182,7 @@ public void removeVideoListener(CallPeer peer, VideoListener listener) * @param allowed true if local video transmission is allowed and * false otherwise. * - * @throws OperationFailedException if video initialization fails. + * @throws OperationFailedException if video initialization fails. */ public void setLocalVideoAllowed(Call call, boolean allowed) throws OperationFailedException diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java index a97bce439..8dbdd9f69 100644 --- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java @@ -1261,12 +1261,16 @@ protected MediaStream initStream(StreamConnector connector, logger.trace("The media types of device and format differ."); // check whether a control already exists + SrtpControl control + = srtpControls.size() > 0 + ? srtpControls.get( + new MediaTypeSrtpControl( + mediaType, + srtpControls.firstKey().srtpControlType)) + : null; MediaService mediaService = ProtocolMediaActivator.getMediaService(); - SrtpControl control = srtpControls.size() > 0 ? - srtpControls.get(new MediaTypeSrtpControl(mediaType, - srtpControls.firstKey().srtpControlType)) : null; if(control == null) { // this creates the default control, currently ZRTP without diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java index 1b0fdaea6..20ffff902 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java @@ -380,7 +380,19 @@ public RTPTranslator getRTPTranslator(MediaType mediaType) { RTPTranslator rtpTranslator = null; - if (isConferenceFocus() && MediaType.VIDEO.equals(mediaType)) + /* + * XXX The conferenceAudioMixer is created even when this Call is not a + * conference focus in order to enable additional functionality. + * Similarly, the videoRTPTranslator is created even when this Call is + * not a conference focus in order to enable this Call to turn into a + * conference focus at a later time. More specifically, MediaStreamImpl + * is unable to accommodate an RTPTranslator after it has created its + * RTPManager. Yet again like conferenceAudioMixer, we'd better not try + * to use it on Android at this time because of performance issues that + * may arise. + */ + if (MediaType.VIDEO.equals(mediaType) + && (!OSUtils.IS_ANDROID || isConferenceFocus())) { if (videoRTPTranslator == null) { @@ -504,16 +516,20 @@ public MediaDevice getDefaultDevice(MediaType mediaType) switch (mediaType) { case AUDIO: - /* - * TODO AudioMixer leads to very poor audio quality on Android so do - * not use it unless it is really really necessary. - */ if ((conferenceAudioMixer == null) && (device != null) + /* + * TODO AudioMixer leads to very poor audio quality on + * Android so do not use it unless it is really really + * necessary. + */ && (!OSUtils.IS_ANDROID || isConferenceFocus()) - // we can use audio mixer only if we - // have capture device (device can send) - && (device.getDirection().allowsSending())) + /* + * We can use the AudioMixer only if the device is able to + * capture (because the AudioMixer will push when the + * capture device pushes). + */ + && device.getDirection().allowsSending()) conferenceAudioMixer = mediaService.createMixer(device); if (conferenceAudioMixer != null) device = conferenceAudioMixer; @@ -745,10 +761,7 @@ public MediaUseCase getMediaUseCase() */ public boolean isLocalVideoAllowed(MediaUseCase useCase) { - if (mediaUseCase.equals(useCase)) - return localVideoAllowed; - else - return false; + return mediaUseCase.equals(useCase) && localVideoAllowed; } /** @@ -1069,7 +1082,7 @@ public void callAdded(CallGroupEvent evt) CallPeer p = peers.next(); getCrossProtocolCallPeersVector().add(p); fireCallPeerEvent(p, CallPeerEvent.CALL_PEER_ADDED); - this.setConferenceFocus(true); + setConferenceFocus(true); } }