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.

cusax-fix
Lyubomir Marinov 13 years ago
parent de67e1094f
commit 3294ef4ba3

@ -320,7 +320,7 @@ public CallPanel(CallConference callConference, CallContainer callWindow)
this.callConference = callConference;
this.callWindow = callWindow;
uiVideoHandler = new UIVideoHandler2(callConference);
uiVideoHandler = new UIVideoHandler2(this.callConference);
callDurationTimer
= new Timer(
@ -345,9 +345,10 @@ public void actionPerformed(ActionEvent e)
* 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 @@ else if (buttonName.equals(DIAL_BUTTON))
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 @@ else if (buttonName.equals(INFO_BUTTON))
{
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());
}
}

@ -292,14 +292,17 @@ private void createSoundLevelIndicators()
= 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 @@ private void createSoundLevelIndicators()
+ "DISABLE_SOUND_LEVEL_INDICATORS",
false))
{
this.callPeer.addStreamSoundLevelListener(
callPeer.addStreamSoundLevelListener(
new SoundLevelListener()
{
public void soundLevelChanged(Object source, int level)
@ -347,15 +350,26 @@ public void soundLevelChanged(Object source, int level)
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 @@ private void initSecuritySettings()
CallPeerSecurityStatusEvent securityEvent
= callPeer.getCurrentSecuritySettings();
if (securityEvent != null
&& securityEvent instanceof CallPeerSecurityOnEvent)
{
if (securityEvent instanceof CallPeerSecurityOnEvent)
securityOn((CallPeerSecurityOnEvent) securityEvent);
}
}
/**
@ -733,8 +744,10 @@ public void run()
SrtpControl srtpControl = evt.getSecurityController();
if ((srtpControl.requiresSecureSignalingTransport()
&& callPeer.getProtocolProvider().isSignalingTransportSecure())
|| !srtpControl.requiresSecureSignalingTransport())
&& callPeer
.getProtocolProvider()
.isSignalingTransportSecure())
|| !srtpControl.requiresSecureSignalingTransport())
{
if (srtpControl instanceof ZrtpControl)
{

@ -245,26 +245,23 @@ protected SrtpCryptoAttribute selectSdesCryptoSuite(
{
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 @@ protected void setAndAddPreferredEncryptionProtocol(
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 @@ protected void setAndAddPreferredEncryptionProtocol(
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 @@ else if(preferredEncryptionProtocols.get(i).equals(
localDescription,
remoteDescription))
{
// Stops once an encryption advertisement has been choosen.
// Stops once an encryption advertisement has been chosen.
return;
}
}

@ -604,6 +604,88 @@ public void modifyVideoContent(boolean allowed)
peer.sendModifyVideoContent(allowed);
}
/**
* 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.
*

@ -9,6 +9,7 @@
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 synchronized void answer()
try
{
getMediaHandler().getTransportManager().
wrapupConnectivityEstablishment();
answer = getMediaHandler().generateSessionAccept();
CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
mediaHandler
.getTransportManager().wrapupConnectivityEstablishment();
answer = mediaHandler.generateSessionAccept();
}
catch(Exception exc)
{
@ -361,6 +364,26 @@ protected synchronized void initiateSession(
protocolProvider.getConnection().sendPacket(sessionInitIQ);
}
/**
* 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}.
*
@ -373,20 +396,27 @@ public void processContentAccept(JingleIQ content)
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 void processContentAdd(final JingleIQ content)
{
new Thread()
{
@Override
public void run()
{
try
@ -537,25 +568,27 @@ public void processContentModify(JingleIQ content)
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 void processSessionAccept(JingleIQ sessionInitIQ)
try
{
mediaHandler.getTransportManager().
wrapupConnectivityEstablishment();
mediaHandler
.getTransportManager().wrapupConnectivityEstablishment();
mediaHandler.processAnswer(answer);
}
catch(Exception exc)

@ -9,6 +9,7 @@
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 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.*;
/**
@ -142,6 +143,61 @@ public ContentPacketExtension getRemoteContent(String contentType)
return null;
}
/**
* {@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).
*
@ -976,6 +1032,86 @@ private void removeContent(
}
}
/**
* 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 @@ private void removeContent(
* @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 @@ private void wrapupConnectivityEstablishment()
/**
* Returns the quality control for video calls if any.
*
* @return the implemented quality control.
*/
public QualityControl getQualityControl()
@ -1695,6 +1832,7 @@ public QualityControl getQualityControl()
/**
* 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 void setSupportQualityControls(boolean value)
/**
* Closes the <tt>CallPeerMediaHandler</tt>.
*/
@Override
public synchronized void close()
{
super.close();
@ -1721,16 +1860,17 @@ public synchronized void close()
/**
* 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);
}

@ -668,7 +668,7 @@ public PacketExtension createTransport(IceMediaStream stream)
for(Component component : stream.getComponents())
{
for(Candidate candidate : component.getLocalCandidates())
for(Candidate<?> candidate : component.getLocalCandidates())
trans.addCandidate(createCandidate(candidate));
}
@ -685,7 +685,7 @@ public PacketExtension createTransport(IceMediaStream stream)
* @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();

@ -117,7 +117,6 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
}
else if (registrationState == RegistrationState.UNREGISTERED)
{
// plug jingle unregistration
unsubscribeForJinglePackets();
if (logger.isInfoEnabled())
@ -751,14 +750,14 @@ private void subscribeForJinglePackets()
}
/**
* 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 @@ private void unsubscribeForJinglePackets()
*/
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 boolean accept(Packet packet)
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 boolean accept(Packet packet)
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 @@ else if(packet instanceof SessionIQ)
{
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 @@ else if(packet instanceof SessionIQ)
*/
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 @@ private void processJingleIQ(final JingleIQ jingleIQ)
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 @@ private void processJingleIQ(final JingleIQ jingleIQ)
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 @@ && getFullCalleeURI(attendant.getAddress())
&& protocolProvider.getOurJID().equals(
transfer.getTo()))
{
// OK transfer correspond to us
// OK, we are legally involved in the transfer.
call = attendantCall;
}
}
@ -1000,18 +1002,16 @@ && getFullCalleeURI(attendant.getAddress())
}
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 @@ else if (action == JingleAction.SESSION_INFO)
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 @@ private void processSessionIQ(SessionIQ sessionIQ)
// smack processor
new Thread()
{
@Override
public void run()
{
try

@ -341,26 +341,23 @@ private IQ getConferenceInfo(CallPeerJabberImpl callPeer, int version)
*
* @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 @@ private void subscribeForCoinPackets()
}
/**
* 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 @@ private void unsubscribeForCoinPackets()
*/
public boolean accept(Packet packet)
{
if(!(packet instanceof CoinIQ))
{
return false;
}
return true;
return (packet instanceof CoinIQ);
}
/**
@ -487,17 +479,20 @@ public void processPacket(Packet packet)
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());
}

@ -8,23 +8,38 @@
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 OperationSetVideoBridgeImpl(ProtocolProviderServiceJabberImpl
*/
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 CallPeer inviteCalleeToCall(String uri, Call call)
throws OperationFailedException,
OperationNotSupportedException
{
return protocolProvider.getOperationSet(
OperationSetTelephonyConferencing.class).inviteCalleeToCall(
uri,
call);
return
protocolProvider
.getOperationSet(OperationSetTelephonyConferencing.class)
.inviteCalleeToCall(uri, call);
}
/**
@ -100,7 +135,151 @@ public boolean isActive()
{
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);
}
}
}

@ -21,6 +21,7 @@
* Google P2P TransportManager.
*
* @author Sebastien Vincent
* @author Lyubomir Marinov
*/
public class P2PTransportManager
extends IceUdpTransportManager
@ -61,13 +62,16 @@ public P2PTransportManager(CallPeerJabberImpl callPeer)
* @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 @@ protected Agent createIceAgent()
* 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 @@ protected Agent createIceAgent()
* <tt>TransportManagerJabberImpl</tt>
* @see TransportManagerJabberImpl#getXmlNamespace()
*/
@Override
public String getXmlNamespace()
{
return "http://www.google.com/transport/p2p";
@ -99,6 +104,7 @@ public String getXmlNamespace()
*
* @return <tt>PacketExtension</tt>
*/
@Override
protected PacketExtension getTransportPacketExtension()
{
return new GTalkTransportPacketExtension();
@ -113,37 +119,46 @@ protected PacketExtension getTransportPacketExtension()
*
* @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 PacketExtension createTransport(IceMediaStream stream)
* @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 @@ private GTalkCandidatePacketExtension createCandidate(Candidate candidate)
* {@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 void startCandidateHarvest(
}
for(ContentPacketExtension ourContent : offer)
{
ourContent.addChildExtension(
getTransportPacketExtension());
}
ourContent.addChildExtension(getTransportPacketExtension());
new Thread()
{
@Override
public void run()
{
Collection<ContentPacketExtension> transportInfoContents
@ -246,7 +254,8 @@ public void run()
= ourContent.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
ourContent.addChildExtension(getTransportPacketExtension());
ourContent.addChildExtension(
getTransportPacketExtension());
IceMediaStream stream = null;
try
@ -275,7 +284,7 @@ public void run()
transportInfoContents.add(transportInfoContent);
transportInfoSender.sendTransportInfo(
transportInfoContents);
transportInfoContents);
}
}
}
@ -303,28 +312,23 @@ public synchronized boolean startConnectivityEstablishment(
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 synchronized boolean startConnectivityEstablishment(
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 synchronized boolean startConnectivityEstablishment(
for(IceMediaStream stream : iceAgent.getStreams())
{
for(Component component : stream.getComponents())
{
component.updateRemoteCandidate();
}
}
return false;
@ -399,7 +401,6 @@ public synchronized boolean startConnectivityEstablishment(
GTalkTransportPacketExtension transport
= content.getFirstChildOfType(
GTalkTransportPacketExtension.class);
List<GTalkCandidatePacketExtension> candidates
= transport.getChildExtensionsOfType(
GTalkCandidatePacketExtension.class);
@ -440,7 +441,6 @@ public synchronized boolean startConnectivityEstablishment(
*/
if (candidate.getGeneration() != generation)
continue;
if(candidate.getProtocol().equalsIgnoreCase("ssltcp"))
continue;
@ -462,7 +462,7 @@ public synchronized boolean startConnectivityEstablishment(
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);

@ -337,6 +337,11 @@ public MediaStreamTarget getStreamTarget(MediaType mediaType)
* 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 MediaStreamTarget getStreamTarget(MediaType mediaType)
* 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)
{

@ -27,7 +27,6 @@
import org.jivesoftware.smackx.packet.*;
import org.xmlpull.mxp1.*;
import org.xmlpull.v1.*;
// disambiguation
/**
* Keeps track of entity capabilities.
@ -35,7 +34,7 @@
* 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 void processPacket(Packet packet)
/* 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 void processPacket(Packet packet)
* 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
{

@ -38,6 +38,13 @@ public class CobriConferenceIQ
public static final String NAMESPACE
= "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.
*/
@ -233,6 +240,13 @@ public static class Channel
*/
public static final String RTP_PORT_ATTR_NAME = "rtpport";
/**
* The name of the XML element which is a child of the &lt;channel&gt;
* 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.
@ -266,6 +280,13 @@ public static class Channel
*/
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>.
@ -289,6 +310,33 @@ public boolean addPayloadType(PayloadTypePacketExtension payloadType)
: payloadTypes.add(payloadType);
}
/**
* 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.
@ -331,6 +379,18 @@ public int getRTPPort()
return rtpPort;
}
/**
* 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>.
@ -346,6 +406,54 @@ public boolean removePayloadType(PayloadTypePacketExtension payloadType)
return payloadTypes.remove(payloadType);
}
/**
* 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 void setRTPPort(int rtpPort)
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 void toXML(StringBuilder xml)
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(" />");
}
}
}

@ -51,6 +51,7 @@ public IQ parseIQ(XmlPullParser parser)
CobriConferenceIQ.Channel channel = null;
CobriConferenceIQ.Content content = null;
PacketExtensionProvider payloadTypePacketExtensionProvider = null;
StringBuilder ssrc = null;
while (!done)
{
@ -64,14 +65,20 @@ public IQ parseIQ(XmlPullParser parser)
{
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,8 +136,13 @@ else if (CobriConferenceIQ.Content.ELEMENT_NAME
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();
@ -181,6 +193,13 @@ else if (PayloadTypePacketExtension.ELEMENT_NAME.equals(
}
break;
}
case XmlPullParser.TEXT:
{
if (ssrc != null)
ssrc.append(parser.getText());
break;
}
}
}

@ -253,9 +253,8 @@ public void addConferenceMember(ConferenceMember conferenceMember)
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);

@ -463,14 +463,15 @@ public boolean isEncryptionProtocolEnabled(String encryptionProtocolName)
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 List<String> getSortedEnabledEncryptionProtocolList()
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;
}
/**

@ -100,7 +100,12 @@ public Iterator<T> getActiveCalls()
{
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();
}
}

@ -10,8 +10,7 @@
/**
* 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);
}

@ -526,63 +526,38 @@ protected long getRemoteSourceID(
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 @@ protected abstract CalleeAddressT parseAddressString(
* 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 void propertyChange(PropertyChangeEvent event)
propertyName))
{
Call call
= ((CallPeerMediaHandler<MediaAwareCallPeerT>)
event.getSource())
= ((CallPeerMediaHandler<MediaAwareCallPeerT>) ev.getSource())
.getPeer()
.getCall();

@ -606,15 +606,15 @@ public void startSrtpMultistream(SrtpControl master)
}
/**
* 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 @@ void registerAudioLevelListeners(AudioMediaStream audioStream)
synchronized (localAudioLevelListenerLock)
{
if (localAudioLevelListener != null)
{
audioStream.setLocalUserAudioLevelListener(
localAudioLevelListener);
}
}
synchronized (streamAudioLevelListenerLock)
{
if (streamAudioLevelListener != null)
{
audioStream.setStreamAudioLevelListener(
streamAudioLevelListener);
}
}
synchronized (csrcAudioLevelListenerLock)
{

@ -192,7 +192,7 @@ private byte nextExtensionID()
{
throw new IllegalStateException(
"Impossible to map more than the 255 already mapped "
+" RTP extensions");
+" RTP extensions");
}
byte extID = nextExtensionMapping++;

@ -773,10 +773,10 @@ public void conferenceMemberAdded(CallPeerConferenceEvent conferenceEvent)
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 void audioLevelChanged(int newLevel)
&& ((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 void audioLevelChanged(int newLevel)
= streamAudioLevelListeners.size();
for(int i = 0; i < streamAudioLevelListenerCount; i++)
{
streamAudioLevelListeners.get(i).soundLevelChanged(
this,
newLevel);
}
}
}

@ -149,26 +149,34 @@ public void propertyChange(PropertyChangeEvent evt)
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 @@ private void setAudioStream(AudioMediaStream audioStream)
{
this.audioStream
.removePropertyChangeListener(
streamPropertyChangeListener);
streamPropertyChangeListener);
this.audioStream.close();
}
@ -831,7 +838,7 @@ private void setAudioStream(AudioMediaStream audioStream)
{
this.audioStream
.addPropertyChangeListener(
streamPropertyChangeListener);
streamPropertyChangeListener);
audioLocalSSRC = this.audioStream.getLocalSourceID();
audioRemoteSSRC = this.audioStream.getRemoteSourceID();
}

@ -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.*;

Loading…
Cancel
Save