diff options
author | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2012-11-27 22:17:19 +0000 |
---|---|---|
committer | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2012-11-27 22:17:19 +0000 |
commit | 3294ef4ba326ae74df152acfdd7a6dcddb87986e (patch) | |
tree | b7befa419fb49c658405276963895a9b79d2d085 /src | |
parent | de67e1094fe85551c8395e6d7c471cdd122de855 (diff) | |
download | jitsi-3294ef4ba326ae74df152acfdd7a6dcddb87986e.zip jitsi-3294ef4ba326ae74df152acfdd7a6dcddb87986e.tar.gz jitsi-3294ef4ba326ae74df152acfdd7a6dcddb87986e.tar.bz2 |
Allows the telephony conferences utilizing the Jitsi VideoBridge server-side technology to associate an RTP stream with the participant who is contributing it. Fixes multiple NullPointerExceptions in MediaStreamStatsImpl and OneToOneCallPeerPanel. Fixes an ArrayIndexOutOfBoundsException in AccountID.
Diffstat (limited to 'src')
25 files changed, 1008 insertions, 407 deletions
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 f1d3466..e79b01b 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 @@ -320,7 +320,7 @@ public class CallPanel this.callConference = callConference; this.callWindow = callWindow; - uiVideoHandler = new UIVideoHandler2(callConference); + uiVideoHandler = new UIVideoHandler2(this.callConference); callDurationTimer = new Timer( @@ -345,9 +345,10 @@ public class CallPanel * Adds the listeners which will observe the model and will trigger the * updates of this view from it. */ - callConference.addCallChangeListener(callConferenceListener); - callConference.addCallPeerConferenceListener(callConferenceListener); - callConference.addPropertyChangeListener(callConferenceListener); + this.callConference.addCallChangeListener(callConferenceListener); + this.callConference.addCallPeerConferenceListener( + callConferenceListener); + this.callConference.addPropertyChangeListener(callConferenceListener); uiVideoHandler.addObserver(uiVideoHandlerObserver); updateViewFromModel(); @@ -400,12 +401,16 @@ public class CallPanel else if (buttonName.equals(CONFERENCE_BUTTON)) { ConferenceInviteDialog inviteDialog; + if (callConference.isJitsiVideoBridge()) - inviteDialog = new ConferenceInviteDialog( - callConference, - callConference.getCalls() - .get(0).getProtocolProvider(), - true); + { + inviteDialog + = new ConferenceInviteDialog( + callConference, + callConference.getCalls().get(0) + .getProtocolProvider(), + true); + } else inviteDialog = new ConferenceInviteDialog(callConference); @@ -437,18 +442,11 @@ public class CallPanel { if (callInfoFrame == null) { - this.callInfoFrame = new CallInfoFrame(callConference); - this.addCallTitleListener(callInfoFrame); - } - - if (callInfoFrame.hasCallInfo()) - { - callInfoFrame.setVisible(!callInfoFrame.isVisible()); - } - else - { - callInfoFrame.setVisible(false); + callInfoFrame = new CallInfoFrame(callConference); + addCallTitleListener(callInfoFrame); } + callInfoFrame.setVisible( + callInfoFrame.hasCallInfo() && !callInfoFrame.isVisible()); } } diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java index d04411b..4897908 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java @@ -292,14 +292,17 @@ public class OneToOneCallPeerPanel = new TransparentPanel(new BorderLayout(5, 0)); TransparentPanel remoteLevelPanel = new TransparentPanel(new BorderLayout(5, 0)); - - localLevel = new InputVolumeControlButton( - callPeer.getCall(), - ImageLoader.MICROPHONE, - ImageLoader.MUTE_BUTTON, - false, false, false); - remoteLevel = new OutputVolumeControlButton( - ImageLoader.HEADPHONE, false, false).getComponent(); + Call call = callPeer.getCall(); + + localLevel + = new InputVolumeControlButton( + call, + ImageLoader.MICROPHONE, + ImageLoader.MUTE_BUTTON, + false, false, false); + remoteLevel + = new OutputVolumeControlButton(ImageLoader.HEADPHONE, false, false) + .getComponent(); final SoundLevelIndicator localLevelIndicator = new SoundLevelIndicator( @@ -339,7 +342,7 @@ public class OneToOneCallPeerPanel + "DISABLE_SOUND_LEVEL_INDICATORS", false)) { - this.callPeer.addStreamSoundLevelListener( + callPeer.addStreamSoundLevelListener( new SoundLevelListener() { public void soundLevelChanged(Object source, int level) @@ -347,15 +350,26 @@ public class OneToOneCallPeerPanel remoteLevelIndicator.updateSoundLevel(level); } }); - - this.callPeer.getCall().addLocalUserSoundLevelListener( - new SoundLevelListener() - { - public void soundLevelChanged(Object source, int level) + /* + * By the time the UI gets to be initialized, the callPeer may have + * been removed from its Call. As far as the UI is concerned, the + * callPeer will never have a Call again and there will be no audio + * levels to display anyway so there is no point in throwing a + * NullPointerException here. + */ + if (call != null) + { + call.addLocalUserSoundLevelListener( + new SoundLevelListener() { - localLevelIndicator.updateSoundLevel(level); - } - }); + public void soundLevelChanged( + Object source, + int level) + { + localLevelIndicator.updateSoundLevel(level); + } + }); + } } } @@ -502,11 +516,8 @@ public class OneToOneCallPeerPanel CallPeerSecurityStatusEvent securityEvent = callPeer.getCurrentSecuritySettings(); - if (securityEvent != null - && securityEvent instanceof CallPeerSecurityOnEvent) - { + if (securityEvent instanceof CallPeerSecurityOnEvent) securityOn((CallPeerSecurityOnEvent) securityEvent); - } } /** @@ -733,8 +744,10 @@ public class OneToOneCallPeerPanel SrtpControl srtpControl = evt.getSecurityController(); if ((srtpControl.requiresSecureSignalingTransport() - && callPeer.getProtocolProvider().isSignalingTransportSecure()) - || !srtpControl.requiresSecureSignalingTransport()) + && callPeer + .getProtocolProvider() + .isSignalingTransportSecure()) + || !srtpControl.requiresSecureSignalingTransport()) { if (srtpControl instanceof ZrtpControl) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java index 8dfee62..0e535b0 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java @@ -245,26 +245,23 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl { List<CryptoPacketExtension> cryptoPacketExtensions = encryptionPacketExtension.getCryptoList(); - Vector<SrtpCryptoAttribute> peerAttributes - = new Vector<SrtpCryptoAttribute>(cryptoPacketExtensions.size()); + List<SrtpCryptoAttribute> peerAttributes + = new ArrayList<SrtpCryptoAttribute>(cryptoPacketExtensions.size()); for (CryptoPacketExtension cpe : cryptoPacketExtensions) peerAttributes.add(cpe.toSrtpCryptoAttribute()); - if (peerAttributes == null) - return null; - - if (isInitiator) - return sDesControl.initiatorSelectAttribute(peerAttributes); - else - return sDesControl.responderSelectAttribute(peerAttributes); + return + isInitiator + ? sDesControl.initiatorSelectAttribute(peerAttributes) + : sDesControl.responderSelectAttribute(peerAttributes); } /** * Returns if the remote peer supports ZRTP. * * @param encryptionPacketExtension The ENCRYPTION element received from - * the remote peer. This may contain the ZRTP acket element for the remote + * the remote peer. This may contain the ZRTP packet element for the remote * peer. * * @return True if the remote peer supports ZRTP. False, otherwise. @@ -513,15 +510,16 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl RtpDescriptionPacketExtension remoteDescription) { // Sets ZRTP or SDES, depending on the preferences for this account. - List<String> preferredEncryptionProtocols = getPeer() - .getProtocolProvider() - .getAccountID() - .getSortedEnabledEncryptionProtocolList(); + List<String> preferredEncryptionProtocols + = getPeer() + .getProtocolProvider() + .getAccountID() + .getSortedEnabledEncryptionProtocolList(); - for(int i = 0; i < preferredEncryptionProtocols.size(); ++i) + for(String preferredEncryptionProtocol : preferredEncryptionProtocols) { // ZRTP - if(preferredEncryptionProtocols.get(i).equals( + if(preferredEncryptionProtocol.equals( ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".ZRTP")) { boolean isZRTPAddedToDescription @@ -529,18 +527,19 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl mediaType, localDescription, remoteDescription); + if(isZRTPAddedToDescription) { addZRTPAdvertisedEncryptions( false, remoteDescription, mediaType); - // Stops once an encryption advertisement has been choosen. + // Stops once an encryption advertisement has been chosen. return; } } // SDES - else if(preferredEncryptionProtocols.get(i).equals( + else if(preferredEncryptionProtocol.equals( ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".SDES")) { addSDESAdvertisedEncryptions( @@ -552,7 +551,7 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl localDescription, remoteDescription)) { - // Stops once an encryption advertisement has been choosen. + // Stops once an encryption advertisement has been chosen. return; } } 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 213bf29..a7995f5 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java @@ -605,6 +605,88 @@ public class CallJabberImpl } /** + * Notifies this instance that a specific <tt>CobriConferenceIQ</tt> has + * been received. + * + * @param conferenceIQ the <tt>CobriConferenceIQ</tt> which has been + * received + * @return <tt>true</tt> if the specified <tt>conferenceIQ</tt> was + * processed by this instance and no further processing is to be performed + * by other possible processors of <tt>CobriConferenceIQ</tt>s; otherwise, + * <tt>false</tt>. Because a <tt>CobriConferenceIQ</tt> request sent from + * the Jitsi VideoBridge server to the application as its client concerns a + * specific <tt>CallJabberImpl</tt> implementation, no further processing by + * other <tt>CallJabberImpl</tt> instances is necessary once the + * <tt>CobriConferenceIQ</tt> is processed by the associated + * <tt>CallJabberImpl</tt> instance. + */ + boolean processCobriConferenceIQ(CobriConferenceIQ conferenceIQ) + { + if (cobri == null) + { + /* + * This instance has not set up any conference using the Jitsi + * VideoBridge server-side technology yet so it cannot be bothered + * with related requests. + */ + return false; + } + else if (conferenceIQ.getID().equals(cobri.getID())) + { + /* + * Remove the local Channels (from the specified conferenceIQ) i.e. + * the Channels on which the local peer/user is sending to the Jitsi + * VideoBridge server because they concern this Call only and not + * its CallPeers. + */ + for (MediaType mediaType : MediaType.values()) + { + String contentName = mediaType.toString(); + CobriConferenceIQ.Content content + = conferenceIQ.getContent(contentName); + + if (content != null) + { + CobriConferenceIQ.Content thisContent + = cobri.getContent(contentName); + + if ((thisContent != null) + && (thisContent.getChannelCount() > 0)) + { + CobriConferenceIQ.Channel thisChannel + = thisContent.getChannel(0); + CobriConferenceIQ.Channel channel + = content.getChannel(thisChannel.getID()); + + if (channel != null) + content.removeChannel(channel); + } + } + } + + for (CallPeerJabberImpl callPeer : getCallPeerList()) + callPeer.processCobriConferenceIQ(conferenceIQ); + + /* + * We have removed the local Channels from the specified + * conferenceIQ. Consequently, it is no longer the same and fit for + * processing by other CallJabberImpl instances. + */ + return true; + } + else + { + /* + * This instance has set up a conference using the Jitsi VideoBridge + * server-side technology but it is not the one referred to by the + * specified conferenceIQ i.e. the specified conferenceIQ does not + * concern this instance. + */ + return false; + } + } + + /** * Creates a new call peer and sends a RINGING response. * * @param jingleIQ the {@link JingleIQ} that created the session. diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java index 4ec4dd0..b2632fc 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java @@ -9,6 +9,7 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.lang.reflect.*; import java.util.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum; import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*; @@ -114,9 +115,11 @@ public class CallPeerJabberImpl try { - getMediaHandler().getTransportManager(). - wrapupConnectivityEstablishment(); - answer = getMediaHandler().generateSessionAccept(); + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + mediaHandler + .getTransportManager().wrapupConnectivityEstablishment(); + answer = mediaHandler.generateSessionAccept(); } catch(Exception exc) { @@ -362,6 +365,26 @@ public class CallPeerJabberImpl } /** + * Notifies this instance that a specific <tt>CobriConferenceIQ</tt> has + * been received. This <tt>CallPeerJabberImpl</tt> uses the part of the + * information provided in the specified <tt>conferenceIQ</tt> which + * concerns it only. + * + * @param conferenceIQ the <tt>CobriConferenceIQ</tt> which has been + * received + */ + void processCobriConferenceIQ(CobriConferenceIQ conferenceIQ) + { + /* + * CallPeerJabberImpl does not itself/directly know the specifics + * related to the channels allocated on the Jitsi VideoBridge server. + * The channels contain transport and media-related information so + * forward the notification to CallPeerMediaHandlerJabberImpl. + */ + getMediaHandler().processCobriConferenceIQ(conferenceIQ); + } + + /** * Processes the content-accept {@link JingleIQ}. * * @param content The {@link JingleIQ} that contains content that remote @@ -373,20 +396,27 @@ public class CallPeerJabberImpl try { - getMediaHandler().getTransportManager(). - wrapupConnectivityEstablishment(); - getMediaHandler().processAnswer(contents); + CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler(); + + mediaHandler + .getTransportManager().wrapupConnectivityEstablishment(); + mediaHandler.processAnswer(contents); } - catch(Exception exc) + catch (Exception e) { - logger.warn("Failed to process a content-accept", exc); - //send an error response; - JingleIQ errResp = JinglePacketFactory.createSessionTerminate( - sessionInitIQ.getTo(), sessionInitIQ.getFrom(), - sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS, - "Error: " + exc.getMessage()); + logger.warn("Failed to process a content-accept", e); - setState(CallPeerState.FAILED, "Error: " + exc.getMessage()); + // Send an error response. + String reason = "Error: " + e.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + Reason.INCOMPATIBLE_PARAMETERS, + reason); + + setState(CallPeerState.FAILED, reason); getProtocolProvider().getConnection().sendPacket(errResp); return; } @@ -446,6 +476,7 @@ public class CallPeerJabberImpl { new Thread() { + @Override public void run() { try @@ -537,25 +568,27 @@ public class CallPeerJabberImpl try { - boolean modify = false; - if(ext.getFirstChildOfType(RtpDescriptionPacketExtension.class) - != null) - { - modify = true; - } + boolean modify + = (ext.getFirstChildOfType(RtpDescriptionPacketExtension.class) + != null); + getMediaHandler().reinitContent(ext.getName(), ext, modify); } - catch(Exception exc) + catch(Exception e) { - logger.info("Failed to process an incoming content-modify", exc); + logger.info("Failed to process an incoming content-modify", e); - //send an error response; - JingleIQ errResp = JinglePacketFactory.createSessionTerminate( - sessionInitIQ.getTo(), sessionInitIQ.getFrom(), - sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS, - "Error: " + exc.getMessage()); + // Send an error response. + String reason = "Error: " + e.getMessage(); + JingleIQ errResp + = JinglePacketFactory.createSessionTerminate( + sessionInitIQ.getTo(), + sessionInitIQ.getFrom(), + sessionInitIQ.getSID(), + Reason.INCOMPATIBLE_PARAMETERS, + reason); - setState(CallPeerState.FAILED, "Error: " + exc.getMessage()); + setState(CallPeerState.FAILED, reason); getProtocolProvider().getConnection().sendPacket(errResp); return; } @@ -626,8 +659,8 @@ public class CallPeerJabberImpl try { - mediaHandler.getTransportManager(). - wrapupConnectivityEstablishment(); + mediaHandler + .getTransportManager().wrapupConnectivityEstablishment(); mediaHandler.processAnswer(answer); } catch(Exception exc) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index 6af49cb..eaa5563 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -9,6 +9,7 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.lang.reflect.*; import java.util.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*; import net.java.sip.communicator.service.protocol.*; @@ -18,7 +19,7 @@ import net.java.sip.communicator.util.*; import org.jitsi.service.neomedia.*; import org.jitsi.service.neomedia.device.*; import org.jitsi.service.neomedia.format.*; -import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.*; import org.jivesoftware.smackx.packet.*; /** @@ -143,6 +144,61 @@ public class CallPeerMediaHandlerJabberImpl } /** + * {@inheritDoc} + * + * In the case of a telephony conference organized by the local peer/user + * via the Jitsi VideoBridge server-side technology, returns an SSRC + * reported by the server as received on the channel allocated by the local + * peer/user for the purposes of communicating with the <tt>CallPeer</tt> + * associated with this instance. + */ + @Override + public long getRemoteSSRC(MediaType mediaType) + { + /* + * If the Jitsi VideoBridge server-side technology is utilized, a single + * MediaStream (per MediaType) is shared among the participating + * CallPeers and, consequently, the remote SSRCs cannot be associated + * with the CallPeers from which they are actually being sent. That's + * why the server will report them to the conference focus. + */ + TransportManagerJabberImpl transportManager = this.transportManager; + + if (transportManager instanceof RawUdpTransportManager) + { + RawUdpTransportManager rawUdpTransportManager + = (RawUdpTransportManager) transportManager; + CobriConferenceIQ.Channel channel + = rawUdpTransportManager.getCobriChannel( + mediaType, + false /* remote */); + + if (channel != null) + { + long[] ssrcs = channel.getSSRCs(); + + /* + * A peer (regardless of whether it is local or remote) may send + * multiple RTP streams at any time. In such a case, it is not + * clear which one of their SSRCs is to be returned. Anyway, the + * super says that the returned is the last known and we will + * presume that the last known in the list reported by the Jitsi + * VideoBridge server is the last. + */ + for (int i = ssrcs.length - 1; i >= 0; i--) + { + long remoteSSRC = ssrcs[i]; + + if (remoteSSRC != -1) + return remoteSSRC; + } + } + } + + return super.getRemoteSSRC(mediaType); + } + + /** * Get the local content of a specific content type (like audio or video). * * @param contentType content type name @@ -977,6 +1033,86 @@ public class CallPeerMediaHandlerJabberImpl } /** + * Notifies this instance that a specific <tt>CobriConferenceIQ</tt> has + * been received. This <tt>CallPeerMediaHandler</tt> uses the part of the + * information provided in the specified <tt>conferenceIQ</tt> which + * concerns it only. + * + * @param conferenceIQ the <tt>CobriConferenceIQ</tt> which has been + * received + */ + void processCobriConferenceIQ(CobriConferenceIQ conferenceIQ) + { + /* + * This CallPeerMediaHandler stores the media information but it does + * not store the cobri Channels (which contain both media and transport + * information). The TransportManager associated with this instance + * stores the cobri Channels but does not store media information (such + * as the remote SSRCs). An design/implementation choice has to be made + * though and the present one is to have this CallPeerMediaHandler + * transparently (with respect to the TransportManager) store the media + * information inside the TransportManager. + */ + TransportManagerJabberImpl transportManager = this.transportManager; + + if (transportManager instanceof RawUdpTransportManager) + { + RawUdpTransportManager rawUdpTransportManager + = (RawUdpTransportManager) transportManager; + long oldAudioRemoteSSRC = getRemoteSSRC(MediaType.AUDIO); + long oldVideoRemoteSSRC = getRemoteSSRC(MediaType.VIDEO); + + for (MediaType mediaType : MediaType.values()) + { + CobriConferenceIQ.Channel dst + = rawUdpTransportManager.getCobriChannel( + mediaType, + false /* remote */); + + if (dst != null) + { + CobriConferenceIQ.Content content + = conferenceIQ.getContent(mediaType.toString()); + + if (content != null) + { + CobriConferenceIQ.Channel src + = content.getChannel(dst.getID()); + + if (src != null) + { + long[] ssrcs = src.getSSRCs(); + + if (!Arrays.equals(dst.getSSRCs(), ssrcs)) + dst.setSSRCs(src.getSSRCs()); + } + } + } + } + + /* + * Do fire new PropertyChangeEvents for the properties + * AUDIO_REMOTE_SSRC and VIDEO_REMOTE_SSRC if necessary. + */ + long newAudioRemoteSSRC = getRemoteSSRC(MediaType.AUDIO); + long newVideoRemoteSSRC = getRemoteSSRC(MediaType.VIDEO); + + if (oldAudioRemoteSSRC != newAudioRemoteSSRC) + { + firePropertyChange( + AUDIO_REMOTE_SSRC, + oldAudioRemoteSSRC, newAudioRemoteSSRC); + } + if (oldVideoRemoteSSRC != newVideoRemoteSSRC) + { + firePropertyChange( + VIDEO_REMOTE_SSRC, + oldVideoRemoteSSRC, newVideoRemoteSSRC); + } + } + } + + /** * Process a <tt>ContentPacketExtension</tt> and initialize its * corresponding <tt>MediaStream</tt>. * @@ -986,7 +1122,7 @@ public class CallPeerMediaHandlerJabberImpl * @throws OperationFailedException if we fail to handle <tt>content</tt> * for reasons like failing to initialize media devices or streams. * @throws IllegalArgumentException if there's a problem with the syntax or - * the semantics of <tt>content</tt>. Method is synchronized in order to + * the semantics of <tt>content</tt>. The method is synchronized in order to * avoid closing mediaHandler when we are currently in process of * initializing, configuring and starting streams and anybody interested * in this operation can synchronize to the mediaHandler instance to wait @@ -1676,6 +1812,7 @@ public class CallPeerMediaHandlerJabberImpl /** * Returns the quality control for video calls if any. + * * @return the implemented quality control. */ public QualityControl getQualityControl() @@ -1695,6 +1832,7 @@ public class CallPeerMediaHandlerJabberImpl /** * Sometimes as initing a call with custom preset can set and we force * that quality controls is supported. + * * @param value whether quality controls is supported.. */ public void setSupportQualityControls(boolean value) @@ -1705,6 +1843,7 @@ public class CallPeerMediaHandlerJabberImpl /** * Closes the <tt>CallPeerMediaHandler</tt>. */ + @Override public synchronized void close() { super.close(); @@ -1721,16 +1860,17 @@ public class CallPeerMediaHandlerJabberImpl /** * Overrides to give access to the transport manager to send events * about ICE state changes. + * * @param property the name of the property of this * <tt>PropertyChangeNotifier</tt> which had its value changed * @param oldValue the value of the property with the specified name before * the change * @param newValue the value of the property with the specified name after */ + @Override protected void firePropertyChange( String property, - Object oldValue, - Object newValue) + Object oldValue, Object newValue) { super.firePropertyChange(property, oldValue, newValue); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java index b2c9c46..8501831 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java @@ -668,7 +668,7 @@ public class IceUdpTransportManager for(Component component : stream.getComponents()) { - for(Candidate candidate : component.getLocalCandidates()) + for(Candidate<?> candidate : component.getLocalCandidates()) trans.addCandidate(createCandidate(candidate)); } @@ -685,7 +685,7 @@ public class IceUdpTransportManager * @return a new {@link CandidatePacketExtension} corresponding to the state * of the <tt>candidate</tt> candidate. */ - private CandidatePacketExtension createCandidate(Candidate candidate) + private CandidatePacketExtension createCandidate(Candidate<?> candidate) { CandidatePacketExtension packet = new CandidatePacketExtension(); 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 053e51e..0e5ae0d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -117,7 +117,6 @@ public class OperationSetBasicTelephonyJabberImpl } else if (registrationState == RegistrationState.UNREGISTERED) { - // plug jingle unregistration unsubscribeForJinglePackets(); if (logger.isInfoEnabled()) @@ -751,14 +750,14 @@ public class OperationSetBasicTelephonyJabberImpl } /** - * Unsubscribes us to notifications about incoming jingle packets. + * Unsubscribes us from notifications about incoming jingle packets. */ private void unsubscribeForJinglePackets() { - if(protocolProvider.getConnection() != null) - { - protocolProvider.getConnection().removePacketListener(this); - } + XMPPConnection connection = protocolProvider.getConnection(); + + if(connection != null) + connection.removePacketListener(this); } /** @@ -773,19 +772,19 @@ public class OperationSetBasicTelephonyJabberImpl */ public boolean accept(Packet packet) { - String sid = null; - - //we only handle JingleIQ-s - if( ! (packet instanceof JingleIQ) && !(packet instanceof SessionIQ)) + // We handle JingleIQ and SessionIQ. + if(!(packet instanceof JingleIQ) && !(packet instanceof SessionIQ)) { - AbstractCallPeer<?, ?> callPeer = - activeCallsRepository.findCallPeerBySessInitPacketID( - packet.getPacketID()); + String packetID = packet.getPacketID(); + AbstractCallPeer<?, ?> callPeer + = activeCallsRepository.findCallPeerBySessInitPacketID( + packetID); if(callPeer == null) { - callPeer = activeGTalkCallsRepository. - findCallPeerBySessInitPacketID(packet.getPacketID()); + callPeer + = activeGTalkCallsRepository.findCallPeerBySessInitPacketID( + packetID); } if(callPeer != null) @@ -798,20 +797,30 @@ public class OperationSetBasicTelephonyJabberImpl if (error != null) { - logger.error("Received an error: code=" + error.getCode() - + " message=" + error.getMessage()); - String message = "Service unavailable"; - Roster roster = getProtocolProvider().getConnection(). - getRoster(); + String errorMessage = error.getMessage(); - if(!roster.contains(packet.getFrom())) + logger.error( + "Received an error: code=" + error.getCode() + + " message=" + errorMessage); + + String message; + + if (errorMessage == null) { - message += ": try adding the contact to your contact " + - "list first."; - } + Roster roster + = getProtocolProvider().getConnection().getRoster(); + String packetFrom = packet.getFrom(); - if (error.getMessage() != null) - message = error.getMessage(); + message = "Service unavailable"; + if(!roster.contains(packetFrom)) + { + message + += ": try adding the contact " + packetFrom + + " to your contact list first."; + } + } + else + message = errorMessage; callPeer.setState(CallPeerState.FAILED, message); } @@ -831,7 +840,7 @@ public class OperationSetBasicTelephonyJabberImpl RtpDescriptionPacketExtension.class); } - sid = jingleIQ.getSID(); + String sid = jingleIQ.getSID(); //if this is not a session-initiate we'll only take it if we've //already seen its session ID. @@ -845,12 +854,14 @@ public class OperationSetBasicTelephonyJabberImpl { return true; } + else + { + String sid = sessionIQ.getID(); - sid = sessionIQ.getID(); - - //if this is not a session's initiate we'll only take it if we've - //already seen its session ID. - return (activeGTalkCallsRepository.findSID(sid) != null); + // If this is not a session's initiate, we'll take it only if + // we've seen its session ID already. + return (activeGTalkCallsRepository.findSID(sid) != null); + } } return false; } @@ -863,66 +874,56 @@ public class OperationSetBasicTelephonyJabberImpl */ public void processPacket(Packet packet) { - if(packet instanceof JingleIQ) - { - JingleIQ jingleIQ = (JingleIQ)packet; + IQ iq = (IQ) packet; - //to prevent hijacking sessions from other jingle based features - //like file transfer for example, we should only send the - //ack if this is a session-initiate with rtp content or if we are - //the owners of this packet's sid + /* + * To prevent hijacking sessions from other Jingle-based features such + * as file transfer, we should send the ack only if this is a + * session-initiate with RTP content or if we are the owners of the + * packet's SID. + */ - //first ack all "set" requests. - if(jingleIQ.getType() == IQ.Type.SET) - { - IQ ack = IQ.createResultIQ(jingleIQ); - protocolProvider.getConnection().sendPacket(ack); - } + //first ack all "set" requests. + if(iq.getType() == IQ.Type.SET) + { + IQ ack = IQ.createResultIQ(iq); - try - { - processJingleIQ(jingleIQ); - } - catch(Throwable t) - { - logger.info("Error while handling incoming Jingle packet: ", t); + protocolProvider.getConnection().sendPacket(ack); + } - /* - * The Javadoc on ThreadDeath says: If ThreadDeath is caught by - * a method, it is important that it be rethrown so that the - * thread actually dies. - */ - if (t instanceof ThreadDeath) - throw (ThreadDeath) t; - } + try + { + if (iq instanceof JingleIQ) + processJingleIQ((JingleIQ) iq); + else if (iq instanceof SessionIQ) + processSessionIQ((SessionIQ) iq); } - else if(packet instanceof SessionIQ) + catch(Throwable t) { - SessionIQ sessionIQ = (SessionIQ)packet; - - //first ack all "set" requests. - if(sessionIQ.getType() == IQ.Type.SET) + if (logger.isInfoEnabled()) { - IQ ack = IQ.createResultIQ(sessionIQ); - protocolProvider.getConnection().sendPacket(ack); - } + String packetClass; - try - { - processSessionIQ(sessionIQ); - } - catch(Throwable t) - { - logger.info("Error while handling incoming GTalk packet: ", t); + if (iq instanceof JingleIQ) + packetClass = "Jingle"; + else if (iq instanceof SessionIQ) + packetClass = "Gtalk"; + else + packetClass = packet.getClass().getSimpleName(); - /* - * The Javadoc on ThreadDeath says: If ThreadDeath is caught by - * a method, it is important that it be rethrown so that the - * thread actually dies. - */ - if (t instanceof ThreadDeath) - throw (ThreadDeath) t; + logger.info( + "Error while handling incoming " + packetClass + + " packet: ", + t); } + + /* + * The Javadoc on ThreadDeath says: If ThreadDeath is caught by + * a method, it is important that it be rethrown so that the + * thread actually dies. + */ + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; } } @@ -948,10 +949,12 @@ public class OperationSetBasicTelephonyJabberImpl if(error != null) { - message += "\ncode=" + error.getCode() - + " message=" + error.getMessage(); - logger.error(" code=" + error.getCode() - + " message=" + error.getMessage()); + String errorStr + = "code=" + error.getCode() + + " message=" + error.getMessage(); + + message += "\n" + errorStr; + logger.error(" " + errorStr); } if (callPeer != null) @@ -964,13 +967,12 @@ public class OperationSetBasicTelephonyJabberImpl if(action == JingleAction.SESSION_INITIATE) { - CallJabberImpl call = null; - TransferPacketExtension transfer = (TransferPacketExtension) jingleIQ.getExtension( - TransferPacketExtension.ELEMENT_NAME, - TransferPacketExtension.NAMESPACE); + TransferPacketExtension.ELEMENT_NAME, + TransferPacketExtension.NAMESPACE); + CallJabberImpl call = null; if (transfer != null) { @@ -992,7 +994,7 @@ public class OperationSetBasicTelephonyJabberImpl && protocolProvider.getOurJID().equals( transfer.getTo())) { - // OK transfer correspond to us + // OK, we are legally involved in the transfer. call = attendantCall; } } @@ -1000,18 +1002,16 @@ public class OperationSetBasicTelephonyJabberImpl } if(call == null) - { call = new CallJabberImpl(this); - } - final CallJabberImpl callThread = call; + final CallJabberImpl finalCall = call; new Thread() { @Override public void run() { - callThread.processSessionInitiate(jingleIQ); + finalCall.processSessionInitiate(jingleIQ); } }.start(); @@ -1076,11 +1076,14 @@ public class OperationSetBasicTelephonyJabberImpl if (packetExtension instanceof CoinPacketExtension) { - CoinPacketExtension coinExt = - (CoinPacketExtension)packetExtension; + CoinPacketExtension coinExt + = (CoinPacketExtension)packetExtension; + callPeer.setConferenceFocus( - Boolean.parseBoolean(coinExt.getAttributeAsString( - CoinPacketExtension.ISFOCUS_ATTR_NAME))); + Boolean.parseBoolean( + coinExt.getAttributeAsString( + CoinPacketExtension + .ISFOCUS_ATTR_NAME))); } } } @@ -1164,6 +1167,7 @@ public class OperationSetBasicTelephonyJabberImpl // smack processor new Thread() { + @Override public void run() { try 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 511cc43..3931c14 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java @@ -341,26 +341,23 @@ public class OperationSetTelephonyConferencingJabberImpl * * @param evt the event received */ + @Override public void registrationStateChanged(RegistrationStateChangeEvent evt) { super.registrationStateChanged(evt); RegistrationState registrationState = evt.getNewState(); - if (registrationState == RegistrationState.REGISTERED) + if (RegistrationState.REGISTERED.equals(registrationState)) { if(logger.isDebugEnabled()) - { logger.debug("Subscribes to Coin packets"); - } subscribeForCoinPackets(); } - else if (registrationState == RegistrationState.UNREGISTERED) + else if (RegistrationState.UNREGISTERED.equals(registrationState)) { if(logger.isDebugEnabled()) - { logger.debug("Unsubscribes to Coin packets"); - } unsubscribeForCoinPackets(); } } @@ -432,14 +429,14 @@ public class OperationSetTelephonyConferencingJabberImpl } /** - * Unsubscribes us to notifications about incoming Coin packets. + * Unsubscribes us from notifications about incoming Coin packets. */ private void unsubscribeForCoinPackets() { - if(parentProvider.getConnection() != null) - { - parentProvider.getConnection().removePacketListener(this); - } + XMPPConnection connection = parentProvider.getConnection(); + + if (connection != null) + connection.removePacketListener(this); } /** @@ -452,12 +449,7 @@ public class OperationSetTelephonyConferencingJabberImpl */ public boolean accept(Packet packet) { - if(!(packet instanceof CoinIQ)) - { - return false; - } - - return true; + return (packet instanceof CoinIQ); } /** @@ -487,17 +479,20 @@ public class OperationSetTelephonyConferencingJabberImpl sid); if (callPeer != null) - handleCoin(coinIQ, callPeer); + handleCoin(callPeer, coinIQ); } } /** - * Handle Coin IQ. + * Handles a specific <tt>CoinIQ</tt> sent from a specific + * <tt>CallPeer</tt>. * - * @param coinIQ Coin IQ - * @param callPeer a <tt>CallPeer</tt> + * @param callPeer the <tt>CallPeer</tt> from which the specified + * <tt>CoinIQ</tt> was sent + * @param coinIQ the <tt>CoinIQ</tt> which was sent from the specified + * <tt>callPeer</tt> */ - private void handleCoin(CoinIQ coinIQ, CallPeerJabberImpl callPeer) + private void handleCoin(CallPeerJabberImpl callPeer, CoinIQ coinIQ) { setConferenceInfoXML(callPeer, -1, coinIQ.getChildElementXML()); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java index 2408cd7..f4ff56b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java @@ -8,23 +8,38 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.util.*; -import org.jivesoftware.smack.*; -import org.jivesoftware.smackx.packet.*; - import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.media.*; +import net.java.sip.communicator.util.*; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; /** * Implements <tt>OperationSetVideoBridge</tt> for Jabber. * * @author Yana Stamcheva + * @author Lyubomir Marinov */ public class OperationSetVideoBridgeImpl - implements OperationSetVideoBridge + implements OperationSetVideoBridge, + PacketFilter, + PacketListener, + RegistrationStateChangeListener { /** - * The parent protocol provider. + * The <tt>Logger</tt> used by the <tt>OperationSetVideoBridgeImpl</tt> + * class and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(OperationSetVideoBridgeImpl.class); + + /** + * The <tt>ProtocolProviderService</tt> implementation which initialized + * this instance, owns it and is often referred to as its parent. */ private final ProtocolProviderServiceJabberImpl protocolProvider; @@ -35,10 +50,28 @@ public class OperationSetVideoBridgeImpl * * @param protocolProvider the parent Jabber protocol provider */ - public OperationSetVideoBridgeImpl(ProtocolProviderServiceJabberImpl - protocolProvider) + public OperationSetVideoBridgeImpl( + ProtocolProviderServiceJabberImpl protocolProvider) { this.protocolProvider = protocolProvider; + this.protocolProvider.addRegistrationStateChangeListener(this); + } + + /** + * Implements {@link PacketFilter}. Determines whether this instance is + * interested in a specific {@link Packet}. + * <tt>OperationSetVideoBridgeImpl</tt> returns <tt>true</tt> if the + * specified <tt>packet</tt> is a {@link CobriConferenceIQ}; otherwise, + * <tt>false</tt>. + * + * @param packet the <tt>Packet</tt> to be determined whether this instance + * is interested in it + * @return <tt>true</tt> if the specified <tt>packet</tt> is a + * <tt>CobriConferenceIQ</tt>; otherwise, <tt>false</tt> + */ + public boolean accept(Packet packet) + { + return (packet instanceof CobriConferenceIQ); } /** @@ -54,12 +87,14 @@ public class OperationSetVideoBridgeImpl */ public Call createConfCall(String[] callees) throws OperationFailedException, - OperationNotSupportedException + OperationNotSupportedException { - return protocolProvider + return + protocolProvider .getOperationSet(OperationSetTelephonyConferencing.class) - .createConfCall(callees, - new MediaAwareCallConference(true)); + .createConfCall( + callees, + new MediaAwareCallConference(true)); } /** @@ -82,10 +117,10 @@ public class OperationSetVideoBridgeImpl throws OperationFailedException, OperationNotSupportedException { - return protocolProvider.getOperationSet( - OperationSetTelephonyConferencing.class).inviteCalleeToCall( - uri, - call); + return + protocolProvider + .getOperationSet(OperationSetTelephonyConferencing.class) + .inviteCalleeToCall(uri, call); } /** @@ -100,7 +135,151 @@ public class OperationSetVideoBridgeImpl { String jitsiVideoBridge = protocolProvider.getJitsiVideoBridge(); - return (jitsiVideoBridge != null - && jitsiVideoBridge.length() > 0); + return ((jitsiVideoBridge != null) && (jitsiVideoBridge.length() > 0)); + } + + /** + * Notifies this instance that a specific <tt>CobriConferenceIQ</tt> has + * been received. + * + * @param conferenceIQ the <tt>CobriConferenceIQ</tt> which has been + * received + */ + private void processCobriConferenceIQ(CobriConferenceIQ conferenceIQ) + { + /* + * The application is not a Jitsi VideoBridge server, it is a client. + * Consequently, the specified CobriConferenceIQ is sent to it in + * relation to the part of the application's functionality which makes + * requests to a Jitsi VideoBridge server i.e. CallJabberImpl. + * + * Additionally, the method processCobriConferenceIQ is presently tasked + * with processing CobriConferenceIQ requests only. They are SET IQs + * sent by the Jitsi VideoBridge server to notify the application about + * updates in the states of (cobri) conferences organized by the + * application. + */ + if (IQ.Type.SET.equals(conferenceIQ.getType()) + && conferenceIQ.getID() != null) + { + OperationSetBasicTelephony<?> basicTelephony + = protocolProvider.getOperationSet( + OperationSetBasicTelephony.class); + + if (basicTelephony != null) + { + Iterator<? extends Call> i = basicTelephony.getActiveCalls(); + + while (i.hasNext()) + { + Call call = i.next(); + + if (call instanceof CallJabberImpl) + { + CallJabberImpl callJabberImpl = (CallJabberImpl) call; + MediaAwareCallConference conference + = callJabberImpl.getConference(); + + if ((conference != null) + && conference.isJitsiVideoBridge()) + { + /* + * TODO We may want to disallow rogue CallJabberImpl + * instances which may throw an exception to prevent + * the conferenceIQ from reaching the CallJabberImpl + * instance which it was meant for. + */ + callJabberImpl.processCobriConferenceIQ( + conferenceIQ); + break; + } + } + } + } + } + } + + /** + * Implements {@link PacketListener}. Notifies this instance that a specific + * {@link Packet} (which this instance has already expressed interest into + * by returning <tt>true</tt> from {@link #accept(Packet)}) has been + * received. + * + * @param packet the <tt>Packet</tt> which has been received and which this + * instance is given a chance to process + */ + public void processPacket(Packet packet) + { + /* + * As we do elsewhere, acknowledge the receipt of the Packet first and + * then go about our business with it. + */ + IQ iq = (IQ) packet; + + if (iq.getType() == IQ.Type.SET) + protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq)); + + /* + * Now that the acknowledging is out of the way, do go about our + * business with the Packet. + */ + CobriConferenceIQ conferenceIQ = (CobriConferenceIQ) iq; + boolean interrupted = false; + + try + { + processCobriConferenceIQ(conferenceIQ); + } + catch (Throwable t) + { + logger.error( + "An error occurred during the processing of a " + + packet.getClass().getName() + " packet", + t); + + if (t instanceof InterruptedException) + { + /* + * We cleared the interrupted state of the current Thread by + * catching the InterruptedException. However, we do not really + * care whether the current Thread has been interrupted - we + * caught the InterruptedException because we want to swallow + * any Throwable. Consequently, we should better restore the + * interrupted state. + */ + interrupted = true; + } + else if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + } + if (interrupted) + Thread.currentThread().interrupt(); + } + + /** + * {@inheritDoc} + * + * Implements {@link RegistrationStateChangeListener}. Notifies this + * instance that there has been a change in the <tt>RegistrationState</tt> + * of {@link #protocolProvider}. Subscribes this instance to + * {@link CobriConferenceIQ}s as soon as <tt>protocolProvider</tt> is + * registered and unsubscribes it as soon as <tt>protocolProvider</tt> is + * unregistered. + */ + public void registrationStateChanged(RegistrationStateChangeEvent ev) + { + RegistrationState registrationState = ev.getNewState(); + + if (RegistrationState.REGISTERED.equals(registrationState)) + { + protocolProvider.getConnection().addPacketListener(this, this); + } + else if (RegistrationState.UNREGISTERED.equals(registrationState)) + { + XMPPConnection connection = protocolProvider.getConnection(); + + if (connection != null) + connection.removePacketListener(this); + } } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java index d56660e..f8f0289 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java @@ -21,6 +21,7 @@ import org.jivesoftware.smack.packet.*; * Google P2P TransportManager. * * @author Sebastien Vincent + * @author Lyubomir Marinov */ public class P2PTransportManager extends IceUdpTransportManager @@ -61,13 +62,16 @@ public class P2PTransportManager * @return the ICE agent to use for all the ICE negotiation that this * transport manager would be going through */ + @Override protected Agent createIceAgent() { CallPeerJabberImpl peer = getCallPeer(); ProtocolProviderServiceJabberImpl provider = peer.getProtocolProvider(); - Agent iceAgent = TransportManagerGTalkImpl.createAgent(provider, - !peer.isInitiator()); + Agent iceAgent + = TransportManagerGTalkImpl.createAgent( + provider, + !peer.isInitiator()); /* We use a custom strategy that will wait a little bit before choosing * to go through a relay. In fact Empathy will begin to send first the @@ -76,7 +80,7 @@ public class P2PTransportManager * checks will start earlier for relay. */ iceAgent.setNominationStrategy( - NominationStrategy.NOMINATE_FIRST_HOST_OR_REFLEXIVE_VALID); + NominationStrategy.NOMINATE_FIRST_HOST_OR_REFLEXIVE_VALID); return iceAgent; } @@ -89,6 +93,7 @@ public class P2PTransportManager * <tt>TransportManagerJabberImpl</tt> * @see TransportManagerJabberImpl#getXmlNamespace() */ + @Override public String getXmlNamespace() { return "http://www.google.com/transport/p2p"; @@ -99,6 +104,7 @@ public class P2PTransportManager * * @return <tt>PacketExtension</tt> */ + @Override protected PacketExtension getTransportPacketExtension() { return new GTalkTransportPacketExtension(); @@ -113,37 +119,46 @@ public class P2PTransportManager * * @return the {@link GTalkTransportPacketExtension} */ + @Override public PacketExtension createTransport(IceMediaStream stream) { GTalkTransportPacketExtension trans = new GTalkTransportPacketExtension(); if(stream != null) + { for(Component component : stream.getComponents()) { - List<LocalCandidate> candToRemove = - new ArrayList<LocalCandidate>(); - List<LocalCandidate> candidates = - component.getLocalCandidates(); + /* Remove the bases of the UPNP candidates. */ + List<LocalCandidate> candidates + = component.getLocalCandidates(); + List<LocalCandidate> candidatesToRemove = null; - for(LocalCandidate candidate : component.getLocalCandidates()) + for(LocalCandidate candidate : candidates) { if(candidate instanceof UPNPCandidate) { LocalCandidate base = candidate.getBase(); - candToRemove.add(base); + + if (candidatesToRemove == null) + { + candidatesToRemove + = new ArrayList<LocalCandidate>( + candidates.size()); + } + candidatesToRemove.add(base); } } - - for(Candidate candidate : candToRemove) + if (candidatesToRemove != null) { - candidates.remove(candidate); + for(Candidate<?> candidateToRemove : candidatesToRemove) + candidates.remove(candidateToRemove); } - for(Candidate candidate : candidates) + for(Candidate<?> candidate : candidates) trans.addCandidate(createCandidate(candidate)); } - + } return trans; } @@ -157,19 +172,13 @@ public class P2PTransportManager * @return a new {@link CandidatePacketExtension} corresponding to the state * of the <tt>candidate</tt> candidate. */ - private GTalkCandidatePacketExtension createCandidate(Candidate candidate) + private GTalkCandidatePacketExtension createCandidate( + Candidate<?> candidate) { - String name = - candidate.getParentComponent().getParentStream().getName(); - - if(candidate.getParentComponent().getComponentID() == Component.RTP) - { - name = "rtp"; - } - else - { - name = "rtcp"; - } + String name + = (candidate.getParentComponent().getComponentID() == Component.RTP) + ? "rtp" + : "rtcp"; return GTalkPacketFactory.createCandidate(candidate, name); } @@ -194,9 +203,10 @@ public class P2PTransportManager * {@link #wrapupCandidateHarvest()}. * @throws OperationFailedException in case we fail allocating ports */ + @Override public void startCandidateHarvest( - List<ContentPacketExtension> ourOffer, - final TransportInfoSender transportInfoSender) + List<ContentPacketExtension> ourOffer, + final TransportInfoSender transportInfoSender) throws OperationFailedException { final List<ContentPacketExtension> offer = ourOffer; @@ -224,13 +234,11 @@ public class P2PTransportManager } for(ContentPacketExtension ourContent : offer) - { - ourContent.addChildExtension( - getTransportPacketExtension()); - } + ourContent.addChildExtension(getTransportPacketExtension()); new Thread() { + @Override public void run() { Collection<ContentPacketExtension> transportInfoContents @@ -246,7 +254,8 @@ public class P2PTransportManager = ourContent.getFirstChildOfType( RtpDescriptionPacketExtension.class); - ourContent.addChildExtension(getTransportPacketExtension()); + ourContent.addChildExtension( + getTransportPacketExtension()); IceMediaStream stream = null; try @@ -275,7 +284,7 @@ public class P2PTransportManager transportInfoContents.add(transportInfoContent); transportInfoSender.sendTransportInfo( - transportInfoContents); + transportInfoContents); } } } @@ -303,28 +312,23 @@ public class P2PTransportManager if (IceProcessingState.RUNNING.equals(iceAgent.getState())) { if(logger.isInfoEnabled()) - { logger.info("Update ICE remote candidates"); - } for (ContentPacketExtension content : remote) { GTalkTransportPacketExtension transport = content.getFirstChildOfType( GTalkTransportPacketExtension.class); - List<GTalkCandidatePacketExtension> candidates = transport.getChildExtensionsOfType( - GTalkCandidatePacketExtension.class); + GTalkCandidatePacketExtension.class); - if(candidates == null || candidates.size() == 0) - { + if((candidates == null) || (candidates.size() == 0)) return false; - } RtpDescriptionPacketExtension description = content.getFirstChildOfType( - RtpDescriptionPacketExtension.class); + RtpDescriptionPacketExtension.class); if (description == null) { @@ -371,7 +375,7 @@ public class P2PTransportManager candidate.getType().toString()), "0", (long)(candidate.getPreference() * 1000), - // Gingle does not send rel-addr/rel-port infromation. + // Gingle does not send rel-addr/rel-port information. null, ufrag); @@ -383,9 +387,7 @@ public class P2PTransportManager for(IceMediaStream stream : iceAgent.getStreams()) { for(Component component : stream.getComponents()) - { component.updateRemoteCandidate(); - } } return false; @@ -399,7 +401,6 @@ public class P2PTransportManager GTalkTransportPacketExtension transport = content.getFirstChildOfType( GTalkTransportPacketExtension.class); - List<GTalkCandidatePacketExtension> candidates = transport.getChildExtensionsOfType( GTalkCandidatePacketExtension.class); @@ -440,7 +441,6 @@ public class P2PTransportManager */ if (candidate.getGeneration() != generation) continue; - if(candidate.getProtocol().equalsIgnoreCase("ssltcp")) continue; @@ -462,7 +462,7 @@ public class P2PTransportManager candidate.getType().toString()), "0", (long)(candidate.getPreference() * 1000), - // Gingle does not send rel-addr/rel-port infromation. + // Gingle does not send rel-addr/rel-port information. null, ufrag); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java index dea5306..6af0f58 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java @@ -337,6 +337,11 @@ public class RawUdpTransportManager * Gets the {@link CobriConferenceIQ.Channel} which belongs to a content * associated with a specific <tt>MediaType</tt> and is to be either locally * or remotely used. + * <p> + * <b>Note</b>: Modifications to the <tt>CobriConferenceIQ.Channel</tt> + * instance returned by the method propagate to (the state of) this + * instance. + * </p> * * @param mediaType the <tt>MediaType</tt> associated with the content which * contains the <tt>CobriConferenceIQ.Channel</tt> to get @@ -348,7 +353,7 @@ public class RawUdpTransportManager * in accord with the specified <tt>local</tt> indicator if such a channel * exists; otherwise, <tt>null</tt> */ - private CobriConferenceIQ.Channel getCobriChannel( + CobriConferenceIQ.Channel getCobriChannel( MediaType mediaType, boolean local) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java index b8e9bf0..301d4fd 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java @@ -27,7 +27,6 @@ import org.jivesoftware.smackx.*; import org.jivesoftware.smackx.packet.*; import org.xmlpull.mxp1.*; import org.xmlpull.v1.*; -// disambiguation /** * Keeps track of entity capabilities. @@ -35,7 +34,7 @@ import org.xmlpull.v1.*; * This work is based on Jonas Adahl's smack fork. * * @author Emil Ivov - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class EntityCapsManager { @@ -885,18 +884,16 @@ public class EntityCapsManager /* Google Talk web does not set hash but we need it to be cached */ if(hash == null) - { hash = ""; - } if (hash != null) { // Check it the packet indicates that the user is online. We // will use this information to decide if we're going to send // the discover info request. - boolean online = false; - if (packet instanceof Presence) - online = ((Presence) packet).isAvailable(); + boolean online + = (packet instanceof Presence) + && ((Presence) packet).isAvailable(); if(online) { @@ -917,7 +914,7 @@ public class EntityCapsManager * Implements an immutable value which stands for a specific node, a * specific hash (algorithm) and a specific ver. * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public static class Caps { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java index d44c6ca..db23b3a 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriConferenceIQ.java @@ -39,6 +39,13 @@ public class CobriConferenceIQ = "http://jitsi.org/protocol/videobridge#conference"; /** + * An array of <tt>long</tt>s which represents the lack of any (RTP) SSRCs + * seen/received on a <tt>Channel</tt>. Explicitly defined to reduce + * unnecessary allocations. + */ + public static final long[] NO_SSRCS = new long[0]; + + /** * The list of {@link Content}s included into this <tt>conference</tt> IQ. */ private final List<Content> contents = new LinkedList<Content>(); @@ -234,6 +241,13 @@ public class CobriConferenceIQ public static final String RTP_PORT_ATTR_NAME = "rtpport"; /** + * The name of the XML element which is a child of the <channel> + * element and which identifies/specifies an (RTP) SSRC which has been + * seen/received on the respective <tt>Channel</tt>. + */ + public static final String SSRC_ELEMENT_NAME = "ssrc"; + + /** * The number of seconds of inactivity after which the <tt>channel</tt> * represented by this instance expires. */ @@ -267,6 +281,13 @@ public class CobriConferenceIQ private int rtpPort; /** + * The list of (RTP) SSRCs which have been seen/received on this + * <tt>Channel</tt> by now. These may exclude SSRCs which are no longer + * active. Set by the Jitsi VideoBridge server, not its clients. + */ + private long[] ssrcs = NO_SSRCS; + + /** * Adds a <tt>payload-type</tt> element defined by XEP-0167: Jingle RTP * Sessions to this <tt>channel</tt>. * @@ -290,6 +311,33 @@ public class CobriConferenceIQ } /** + * Adds a specific (RTP) SSRC to the list of SSRCs seen/received on this + * <tt>Channel</tt>. Invoked by the Jitsi VideoBridge server, not its + * clients. + * + * @param ssrc the (RTP) SSRC to be added to the list of SSRCs + * seen/received on this <tt>Channel</tt> + * @return <tt>true</tt> if the list of SSRCs seen/received on this + * <tt>Channel</tt> has been modified as part of the method call; + * otherwise, <tt>false</tt> + */ + public synchronized boolean addSSRC(long ssrc) + { + // contains + for (long element : ssrcs) + if (element == ssrc) + return false; + + // add + long[] newSSRCs = new long[ssrcs.length + 1]; + + System.arraycopy(ssrcs, 0, newSSRCs, 0, ssrcs.length); + newSSRCs[ssrcs.length] = ssrc; + ssrcs = newSSRCs; + return true; + } + + /** * Gets the number of seconds of inactivity after which the * <tt>channel</tt> represented by this instance expires. * @@ -332,6 +380,18 @@ public class CobriConferenceIQ } /** + * Gets (a copy of) the list of (RTP) SSRCs seen/received on this + * <tt>Channel</tt>. + * + * @return an array of <tt>long</tt>s which represents (a copy of) the + * list of (RTP) SSRCs seen/received on this <tt>Channel</tt> + */ + public synchronized long[] getSSRCs() + { + return (ssrcs.length == 0) ? NO_SSRCS : ssrcs.clone(); + } + + /** * Removes a <tt>payload-type</tt> element defined by XEP-0167: Jingle * RTP Sessions from this <tt>channel</tt>. * @@ -347,6 +407,54 @@ public class CobriConferenceIQ } /** + * Removes a specific (RTP) SSRC from the list of SSRCs seen/received on + * this <tt>Channel</tt>. Invoked by the Jitsi VideoBridge server, not + * its clients. + * + * @param ssrc the (RTP) SSRC to be removed from the list of SSRCs + * seen/received on this <tt>Channel</tt> + * @return <tt>true</tt> if the list of SSRCs seen/received on this + * <tt>Channel</tt> has been modified as part of the method call; + * otherwise, <tt>false</tt> + */ + public synchronized boolean removeSSRC(long ssrc) + { + if (ssrcs.length == 1) + { + if (ssrcs[0] == ssrc) + { + ssrcs = NO_SSRCS; + return true; + } + else + return false; + } + else + { + for (int i = 0; i < ssrcs.length; i++) + { + if (ssrcs[i] == ssrc) + { + long[] newSSRCs = new long[ssrcs.length - 1]; + + if (i != 0) + System.arraycopy(ssrcs, 0, newSSRCs, 0, i); + if (i != newSSRCs.length) + { + System.arraycopy( + ssrcs, i + 1, + newSSRCs, i, + newSSRCs.length - i); + } + ssrcs = newSSRCs; + return true; + } + } + return false; + } + } + + /** * Sets the number of seconds of inactivity after which the * <tt>channel</tt> represented by this instance expires. * @@ -389,6 +497,24 @@ public class CobriConferenceIQ this.rtpPort = rtpPort; } + /** + * Sets the list of (RTP) SSRCs seen/received on this <tt>Channel</tt>. + * + * @param ssrcs the list of (RTP) SSRCs to be set as seen/received on + * this <tt>Channel</tt> + */ + public void setSSRCs(long[] ssrcs) + { + /* + * TODO Make sure that the SSRCs set on this instance do not contain + * duplicates. + */ + this.ssrcs + = ((ssrcs == null) || (ssrcs.length == 0)) + ? NO_SSRCS + : ssrcs.clone(); + } + public void toXML(StringBuilder xml) { xml.append('<').append(ELEMENT_NAME); @@ -396,45 +522,70 @@ public class CobriConferenceIQ String id = getID(); if (id != null) + { xml.append(' ').append(ID_ATTR_NAME).append("='").append(id) .append('\''); + } String host = getHost(); if (host != null) + { xml.append(' ').append(HOST_ATTR_NAME).append("='").append(host) .append('\''); + } int rtpPort = getRTPPort(); if (rtpPort > 0) + { xml.append(' ').append(RTP_PORT_ATTR_NAME).append("='") .append(rtpPort).append('\''); + } int rtcpPort = getRTCPPort(); if (rtcpPort > 0) + { xml.append(' ').append(RTCP_PORT_ATTR_NAME).append("='") .append(rtcpPort).append('\''); + } int expire = getExpire(); if (expire >= 0) + { xml.append(' ').append(EXPIRE_ATTR_NAME).append("='") .append(expire).append('\''); + } List<PayloadTypePacketExtension> payloadTypes = getPayloadTypes(); + boolean hasPayloadTypes = (payloadTypes.size() != 0); + long[] ssrcs = getSSRCs(); + boolean hasSSRCs = (ssrcs.length != 0); - if (payloadTypes.size() == 0) + if (hasPayloadTypes || hasSSRCs) { - xml.append(" />"); + xml.append('>'); + if (hasPayloadTypes) + { + for (PayloadTypePacketExtension payloadType : payloadTypes) + xml.append(payloadType.toXML()); + } + if (hasSSRCs) + { + for (long ssrc : ssrcs) + { + xml.append('<').append(SSRC_ELEMENT_NAME).append('>') + .append(ssrc).append("</") + .append(SSRC_ELEMENT_NAME).append('>'); + } + } + xml.append("</").append(ELEMENT_NAME).append('>'); } else { - xml.append('>'); - for (PayloadTypePacketExtension payloadType : payloadTypes) - xml.append(payloadType.toXML()); - xml.append("</").append(ELEMENT_NAME).append('>'); + xml.append(" />"); } } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java index aab8ad3..54d4ccf 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriIQProvider.java @@ -51,6 +51,7 @@ public class CobriIQProvider CobriConferenceIQ.Channel channel = null; CobriConferenceIQ.Content content = null; PacketExtensionProvider payloadTypePacketExtensionProvider = null; + StringBuilder ssrc = null; while (!done) { @@ -64,15 +65,21 @@ public class CobriIQProvider { done = true; } - else if (CobriConferenceIQ.Channel.ELEMENT_NAME - .equals(name)) + else if (CobriConferenceIQ.Channel.ELEMENT_NAME.equals( + name)) { content.addChannel(channel); channel = null; } - else if (CobriConferenceIQ.Content.ELEMENT_NAME + else if (CobriConferenceIQ.Channel.SSRC_ELEMENT_NAME .equals(name)) { + channel.addSSRC(Long.parseLong(ssrc.toString().trim())); + ssrc = null; + } + else if (CobriConferenceIQ.Content.ELEMENT_NAME.equals( + name)) + { conference.addContent(content); content = null; } @@ -129,9 +136,14 @@ public class CobriIQProvider if ((expire != null) && (expire.length() != 0)) channel.setExpire(Integer.parseInt(expire)); } - else if (CobriConferenceIQ.Content.ELEMENT_NAME + else if (CobriConferenceIQ.Channel.SSRC_ELEMENT_NAME .equals(name)) { + ssrc = new StringBuilder(); + } + else if (CobriConferenceIQ.Content.ELEMENT_NAME.equals( + name)) + { content = new CobriConferenceIQ.Content(); String contentName @@ -181,6 +193,13 @@ public class CobriIQProvider } break; } + + case XmlPullParser.TEXT: + { + if (ssrc != null) + ssrc.append(parser.getText()); + break; + } } } diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java index e490dd1..44f4a68 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java @@ -253,9 +253,8 @@ public abstract class AbstractCallPeer<T extends Call, protected ConferenceMember findConferenceMember(long ssrc) { List<ConferenceMember> members = getConferenceMembers(); - int memberCount = members.size(); - for (int i = 0; i < memberCount; i++) + for (int i = 0, memberCount = members.size(); i < memberCount; i++) { ConferenceMember member = members.get(i); diff --git a/src/net/java/sip/communicator/service/protocol/AccountID.java b/src/net/java/sip/communicator/service/protocol/AccountID.java index ba1cf86..c373120 100644 --- a/src/net/java/sip/communicator/service/protocol/AccountID.java +++ b/src/net/java/sip/communicator/service/protocol/AccountID.java @@ -463,14 +463,15 @@ public abstract class AccountID public List<String> getSortedEnabledEncryptionProtocolList() { Map<String, Integer> encryptionProtocols - = this.getIntegerPropertiesByPrefix( + = getIntegerPropertiesByPrefix( ProtocolProviderFactory.ENCRYPTION_PROTOCOL, true); Map<String, Boolean> encryptionProtocolStatus - = this.getBooleanPropertiesByPrefix( + = getBooleanPropertiesByPrefix( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS, true, false); + // If the account is not yet configured, then ZRTP is activated by // default. if(encryptionProtocols.size() == 0) @@ -484,49 +485,45 @@ public abstract class AccountID true); } - List<String> sortedEncryptionProtocolList + List<String> sortedEncryptionProtocols = new ArrayList<String>(encryptionProtocols.size()); - Iterator<String> names = encryptionProtocols.keySet().iterator(); - String name; - int index; + // First: add all protocol in the right order. - while(names.hasNext()) + for (Map.Entry<String, Integer> e : encryptionProtocols.entrySet()) { - name = names.next(); - index = encryptionProtocols.get(name); + int index = e.getValue(); // If the key is set. - if(index != -1) + if (index != -1) { - if(index > sortedEncryptionProtocolList.size()) - { - index = sortedEncryptionProtocolList.size(); - } - sortedEncryptionProtocolList.add(index, name); + if (index > sortedEncryptionProtocols.size()) + index = sortedEncryptionProtocols.size(); + + String name = e.getKey(); + + sortedEncryptionProtocols.add(index, name); } } // Second: remove all disabled protocol. - String encryptionProtocolPropertyName; - int prefixeLength + int namePrefixLength = ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + 1; - names = encryptionProtocols.keySet().iterator(); - while(names.hasNext()) + + for (Iterator<String> i = sortedEncryptionProtocols.iterator(); + i.hasNext();) { - encryptionProtocolPropertyName = names.next(); - index = encryptionProtocols.get(encryptionProtocolPropertyName); - name = encryptionProtocolPropertyName.substring(prefixeLength); - // If the key is set. - if(index != -1 && !encryptionProtocolStatus.get( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + String name = i.next().substring(namePrefixLength); + + if (!encryptionProtocolStatus.get( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + "." + name)) { - sortedEncryptionProtocolList.remove(index); + i.remove(); } } - return sortedEncryptionProtocolList; + return sortedEncryptionProtocols; } /** diff --git a/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java b/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java index e1ceeb4..ebe4008 100644 --- a/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java +++ b/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java @@ -100,7 +100,12 @@ public abstract class ActiveCallsRepository<T extends Call, { synchronized(activeCalls) { - return new LinkedList<T>(activeCalls.values()).iterator(); + /* + * Given that we know the elements that will go into the new List, + * it is more optimal in terms of memory and execution time to use + * ArrayList rather than LinkedList. + */ + return new ArrayList<T>(activeCalls.values()).iterator(); } } diff --git a/src/net/java/sip/communicator/service/protocol/event/RegistrationStateChangeListener.java b/src/net/java/sip/communicator/service/protocol/event/RegistrationStateChangeListener.java index aba6456..3b4d56d 100644 --- a/src/net/java/sip/communicator/service/protocol/event/RegistrationStateChangeListener.java +++ b/src/net/java/sip/communicator/service/protocol/event/RegistrationStateChangeListener.java @@ -10,8 +10,7 @@ import java.util.*; /** * An event listener that should be implemented by parties interested in changes - * that occur in the registration state of a - * <code>ProtocolProviderService</code>. + * that occur in the registration state of a <tt>ProtocolProviderService</tt>. * * @author Emil Ivov */ @@ -19,12 +18,11 @@ public interface RegistrationStateChangeListener extends EventListener { /** - * The method is called by a <code>ProtocolProviderService</code> - * implementation whenever a change in the registration state of the - * corresponding provider had occurred. + * The method is called by a <tt>ProtocolProviderService</tt> implementation + * whenever a change in its registration state has occurred. * - * @param evt - * the event describing the status change. + * @param evt a <tt>RegistrationStateChangeEvent</tt> which describes the + * registration state change. */ public void registrationStateChanged(RegistrationStateChangeEvent evt); } diff --git a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java index 3b31144..9d6812c 100644 --- a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java +++ b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java @@ -526,63 +526,38 @@ public abstract class AbstractOperationSetTelephonyConferencing< MediaAwareCallPeer<?,?,?> callPeer, MediaType mediaType) { - MediaStream stream = callPeer.getMediaHandler().getStream(mediaType); - long remoteSourceID; + long remoteSourceID + = callPeer.getMediaHandler().getRemoteSSRC(mediaType); - if (stream != null) + if (remoteSourceID != -1) { - remoteSourceID = stream.getRemoteSourceID(); - if (remoteSourceID != -1) - { - /* - * TODO Technically, we are detecting conflicts within a Call - * while we should be detecting them within the whole - * CallConference. - */ - MediaAwareCall<?,?,?> call = callPeer.getCall(); + /* + * TODO Technically, we are detecting conflicts within a Call + * while we should be detecting them within the whole + * CallConference. + */ + MediaAwareCall<?,?,?> call = callPeer.getCall(); - if (call != null) + if (call != null) + { + for (MediaAwareCallPeer<?,?,?> aCallPeer + : call.getCallPeerList()) { - for (MediaAwareCallPeer<?,?,?> aCallPeer - : call.getCallPeerList()) + if (aCallPeer != callPeer) { - if (aCallPeer != callPeer) + long aRemoteSourceID + = aCallPeer.getMediaHandler().getRemoteSSRC( + mediaType); + + if (aRemoteSourceID == remoteSourceID) { - MediaStream aStream - = aCallPeer.getMediaHandler().getStream( - mediaType); - - /* - * When the Jitsi VideoBridge server-side technology - * is utilized by the local peer/user to host a - * telephony conference, one and the same - * MediaStream instance will be shared by the - * CallPeers. This will definitely lead to a - * conflict. - */ - if (aStream == stream) - { - remoteSourceID = -1; - break; - } - else - { - long aRemoteSourceID - = stream.getRemoteSourceID(); - - if (aRemoteSourceID == remoteSourceID) - { - remoteSourceID = -1; - break; - } - } + remoteSourceID = -1; + break; } } } } } - else - remoteSourceID = -1; return remoteSourceID; } @@ -706,15 +681,15 @@ public abstract class AbstractOperationSetTelephonyConferencing< * Notifies this <tt>PropertyChangeListener</tt> that the value of a * specific property of the notifier it is registered with has changed. * - * @param event a <tt>PropertyChangeEvent</tt> which describes the source of + * @param ev a <tt>PropertyChangeEvent</tt> which describes the source of * the event, the name of the property which has changed its value and the * old and new values of the property * @see PropertyChangeListener#propertyChange(PropertyChangeEvent) */ @SuppressWarnings("unchecked") - public void propertyChange(PropertyChangeEvent event) + public void propertyChange(PropertyChangeEvent ev) { - String propertyName = event.getPropertyName(); + String propertyName = ev.getPropertyName(); if (CallPeerMediaHandler.AUDIO_LOCAL_SSRC.equals( propertyName) @@ -726,8 +701,7 @@ public abstract class AbstractOperationSetTelephonyConferencing< propertyName)) { Call call - = ((CallPeerMediaHandler<MediaAwareCallPeerT>) - event.getSource()) + = ((CallPeerMediaHandler<MediaAwareCallPeerT>) ev.getSource()) .getPeer() .getCall(); 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 f5c4c98..7702c42 100644 --- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java @@ -606,15 +606,15 @@ public abstract class CallPeerMediaHandler } /** - * Gets the last-known remote SSRC of the audio <tt>MediaStream</tt> of this - * instance. + * Gets the last-known SSRC of an RTP stream with a specific + * <tt>MediaType</tt> received by a <tt>MediaStream</tt> of this instance. * - * @return the last-known remote SSRC of the audio <tt>MediaStream</tt> of - * this instance + * @return the last-known SSRC of an RTP stream with a specific + * <tt>MediaType</tt> received by a <tt>MediaStream</tt> of this instance */ - public long getAudioRemoteSSRC() + public long getRemoteSSRC(MediaType mediaType) { - return mediaHandler.getRemoteSSRC(this, MediaType.AUDIO); + return mediaHandler.getRemoteSSRC(this, mediaType); } /** @@ -1032,14 +1032,18 @@ public abstract class CallPeerMediaHandler synchronized (localAudioLevelListenerLock) { if (localAudioLevelListener != null) + { audioStream.setLocalUserAudioLevelListener( localAudioLevelListener); + } } synchronized (streamAudioLevelListenerLock) { if (streamAudioLevelListener != null) + { audioStream.setStreamAudioLevelListener( streamAudioLevelListener); + } } synchronized (csrcAudioLevelListenerLock) { diff --git a/src/net/java/sip/communicator/service/protocol/media/DynamicRTPExtensionsRegistry.java b/src/net/java/sip/communicator/service/protocol/media/DynamicRTPExtensionsRegistry.java index 9a9e654..5c03c0c 100644 --- a/src/net/java/sip/communicator/service/protocol/media/DynamicRTPExtensionsRegistry.java +++ b/src/net/java/sip/communicator/service/protocol/media/DynamicRTPExtensionsRegistry.java @@ -192,7 +192,7 @@ public class DynamicRTPExtensionsRegistry { throw new IllegalStateException( "Impossible to map more than the 255 already mapped " - +" RTP extensions"); + +" RTP extensions"); } byte extID = nextExtensionMapping++; diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java index 92b2b25..55054ea 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java @@ -773,10 +773,10 @@ public abstract class MediaAwareCallPeer if (getConferenceMemberCount() > 2) { // this peer is now a conference focus with more than three - // participants. This means that the this peer is mixing and sending - // us audio for at least two separate participants. We therefore - // need to remove the stream level listeners and switch to CSRC - // level listening + // participants. This means that this peer is mixing and sending us + // audio for at least two separate participants. We therefore need + // to remove the stream level listeners and switch to CSRC level + // listening CallPeerMediaHandler<?> mediaHandler = getMediaHandler(); mediaHandler.setStreamAudioLevelListener(null); @@ -839,7 +839,8 @@ public abstract class MediaAwareCallPeer && ((conferenceMemberCount = getConferenceMemberCount()) > 0) && (conferenceMemberCount < 3)) { - long audioRemoteSSRC = getMediaHandler().getAudioRemoteSSRC(); + long audioRemoteSSRC + = getMediaHandler().getRemoteSSRC(MediaType.AUDIO); if (audioRemoteSSRC != CallPeerMediaHandler.SSRC_UNKNOWN) { @@ -873,9 +874,11 @@ public abstract class MediaAwareCallPeer = streamAudioLevelListeners.size(); for(int i = 0; i < streamAudioLevelListenerCount; i++) + { streamAudioLevelListeners.get(i).soundLevelChanged( this, newLevel); + } } } diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java index 5211d80..c5e2632 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java @@ -149,26 +149,34 @@ public class MediaHandler Object source = evt.getSource(); if (source == audioStream) + { setLocalSSRC( MediaType.AUDIO, audioStream.getLocalSourceID()); + } else if (source == videoStream) + { setLocalSSRC( MediaType.VIDEO, videoStream.getLocalSourceID()); + } } else if (MediaStream.PNAME_REMOTE_SSRC.equals(propertyName)) { Object source = evt.getSource(); if (source == audioStream) + { setRemoteSSRC( MediaType.AUDIO, audioStream.getRemoteSourceID()); + } else if (source == videoStream) + { setRemoteSSRC( MediaType.VIDEO, videoStream.getRemoteSourceID()); + } } } }; @@ -817,8 +825,7 @@ public class MediaHandler { this.audioStream .removePropertyChangeListener( - streamPropertyChangeListener); - + streamPropertyChangeListener); this.audioStream.close(); } @@ -831,7 +838,7 @@ public class MediaHandler { this.audioStream .addPropertyChangeListener( - streamPropertyChangeListener); + streamPropertyChangeListener); audioLocalSSRC = this.audioStream.getLocalSourceID(); audioRemoteSSRC = this.audioStream.getRemoteSourceID(); } diff --git a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java index 6106360..ede1b3a 100644 --- a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java +++ b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java @@ -7,7 +7,6 @@ package net.java.sip.communicator.service.protocol.media; import java.net.*; -import java.util.*; import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; |