Ongoing work on cross protocol conference calls. Adds missing portaudio hotplug patch.

cusax-fix
Sebastien Vincent 14 years ago
parent 936b4790ec
commit db293d098b

File diff suppressed because it is too large Load Diff

@ -209,8 +209,23 @@ public void run()
{
try
{
opSet.hangupCallPeer(
cCall.getCallPeers().next());
Iterator<? extends CallPeer> peers =
cCall.getCrossProtocolCallPeers();
while(peers.hasNext())
{
CallPeer peer = peers.next();
peer.getProtocolProvider().getOperationSet(
OperationSetBasicTelephony.class).
hangupCallPeer(peer);
}
peers = cCall.getCallPeers();
while(peers.hasNext())
opSet.hangupCallPeer(
peers.next());
}
catch(OperationFailedException e)
{
@ -262,7 +277,8 @@ public void outgoingCallCreated(CallEvent event)
{
synchronized(outgoingCalls)
{
outgoingCalls.add(event.getSourceCall());
if(event.getSourceCall().getCallGroup() == null)
outgoingCalls.add(event.getSourceCall());
}
}

@ -755,6 +755,23 @@ public static void inviteToConferenceCall( String[] callees,
new InviteToConferenceCallThread(callees, call).start();
}
/**
* Invites the given list of <tt>callees</tt> to the given conference
* <tt>call</tt>.
*
* @param provider provider to which the callee belongs
* @param callee the list of contacts to invite
* @param call existing call
*/
public static void inviteToCrossProtocolConferenceCall(
ProtocolProviderService provider,
String callee,
Call call)
{
new InviteToCrossProtocolConferenceCallThread(provider, callee, call).
start();
}
/**
* Puts on or off hold the given <tt>callPeer</tt>.
* @param callPeer the peer to put on/off hold
@ -1493,6 +1510,79 @@ public void run()
}
}
/**
* Invites a list of callees to a conference call.
*/
private static class InviteToCrossProtocolConferenceCallThread
extends Thread
{
private final ProtocolProviderService provider;
private final String callee;
private final Call call;
public InviteToCrossProtocolConferenceCallThread(
ProtocolProviderService provider,
String callee,
Call call)
{
this.provider = provider;
this.callee = callee;
this.call = call;
}
@Override
public void run()
{
CallGroup group = null;
if(call.getCallGroup() == null)
{
group = new CallGroup();
group.addCall(call);
}
else
{
group = call.getCallGroup();
}
OperationSetBasicTelephony<?> opSetTelephony =
provider.getOperationSet(OperationSetBasicTelephony.class);
if(opSetTelephony != null)
{
Exception exception = null;
try
{
Call c = opSetTelephony.createCall(callee, group);
group.addCall(c);
}
catch(Exception e)
{
exception = e;
logger.info("Failed to call " + callee, e);
}
if (exception != null)
{
logger
.error("Failed to invite callee: " + callee, exception);
new ErrorDialog(
null,
GuiActivator
.getResources()
.getI18NString("service.gui.ERROR"),
exception.getMessage(),
ErrorDialog.ERROR)
.showDialog();
}
}
}
}
/**
* Hang-ups all call peers in the given call.
*/
@ -1509,23 +1599,51 @@ public HangupCallThread(Call call)
@Override
public void run()
{
ProtocolProviderService pps = call.getProtocolProvider();
Iterator<? extends CallPeer> peers = call.getCallPeers();
Iterator<? extends CallPeer> peers = null;
while (peers.hasNext())
if(call.getCallGroup() != null)
{
CallPeer peer = peers.next();
OperationSetBasicTelephony<?> telephony
= pps.getOperationSet(OperationSetBasicTelephony.class);
List<Call> calls = call.getCallGroup().getCalls();
try
for(Call c : calls)
{
telephony.hangupCallPeer(peer);
peers = c.getCallPeers();
CallPeer peer = peers.next();
OperationSetBasicTelephony<?> telephony
= peer.getCall().getProtocolProvider().
getOperationSet(OperationSetBasicTelephony.class);
try
{
telephony.hangupCallPeer(peer);
}
catch (OperationFailedException e)
{
logger.error("Could not hang up : " + peer
+ " caused by the following exception: " + e);
}
}
catch (OperationFailedException e)
}
else
{
ProtocolProviderService pps = call.getProtocolProvider();
peers = call.getCallPeers();
while (peers.hasNext())
{
logger.error("Could not hang up : " + peer
+ " caused by the following exception: " + e);
CallPeer peer = peers.next();
OperationSetBasicTelephony<?> telephony
= pps.getOperationSet(OperationSetBasicTelephony.class);
try
{
telephony.hangupCallPeer(peer);
}
catch (OperationFailedException e)
{
logger.error("Could not hang up : " + peer
+ " caused by the following exception: " + e);
}
}
}
}

@ -137,6 +137,13 @@ public ConferenceCallPanel(CallPanel callPanel, Call c)
while (iterator.hasNext())
this.addCallPeerPanel(iterator.next());
iterator = this.call.getCrossProtocolCallPeers();
while (iterator.hasNext())
{
this.addCallPeerPanel(iterator.next());
}
scrollPane.setBorder(null);
/*
* The scrollPane seems to receive only a few pixels of width at times

@ -616,10 +616,9 @@ public void soundLevelChanged(
{
ConferenceMember key = e.getKey();
Integer value = e.getValue();
Contact contact = focusPeerPanel.getCallPeerContact();
String address = focusPeerPanel.getCallPeerContactAddress();
if(key.getAddress().equals(
contact.getAddress()))
if(key.getAddress().equals(address))
{
focusPeerPanel.updateSoundBar(value);
break;

@ -479,12 +479,12 @@ public UIVideoHandler getVideoHandler()
}
/**
* Returns <tt>CallPeer</tt> contact.
* Returns <tt>CallPeer</tt> contact address.
*
* @return <tt>CallPeer</tt> contact
* @return <tt>CallPeer</tt> contact address
*/
public Contact getCallPeerContact()
public String getCallPeerContactAddress()
{
return callPeer.getContact();
return callPeer.getURI();
}
}

@ -1394,7 +1394,7 @@ public void setDevice(MediaDevice device)
* Copy the playback from the old MediaDeviceSession into the new
* MediaDeviceSession in order to prevent the recreation of the
* playback of the ReceiveStream(s) when just changing the
* MediaDevice of this MediaSteam.
* MediaDevice of this MediaSteam.
*/
if (oldValue != null)
deviceSession.copyPlayback(oldValue);
@ -1696,7 +1696,7 @@ private void startReceiveStreams()
* It turns out that the receiveStreams list of rtpManager can be
* empty. As a workaround, use the receiveStreams of this instance.
*/
if (receiveStreams.isEmpty() && (this.receiveStreams != null))
if (receiveStreams.isEmpty() && (this.receiveStreams != null))
receiveStreams = this.receiveStreams;
for (ReceiveStream receiveStream : receiveStreams)
@ -1869,7 +1869,7 @@ private void stopReceiveStreams()
* It turns out that the receiveStreams list of rtpManager can be
* empty. As a workaround, use the receiveStreams of this instance.
*/
if (receiveStreams.isEmpty() && (this.receiveStreams != null))
if (receiveStreams.isEmpty() && (this.receiveStreams != null))
receiveStreams = this.receiveStreams;
for (ReceiveStream receiveStream : receiveStreams)

@ -74,7 +74,8 @@ private synchronized void close(
streamDeviceSessions.remove(streamDeviceSession);
if (streamDeviceSessions.isEmpty())
{
deviceSession.close();
if(deviceSession != null)
deviceSession.close();
deviceSession = null;
}
else

@ -176,4 +176,22 @@ public void removeLocalUserSoundLevelListener(
SoundLevelListener l)
{
}
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt)
{
}
/**
* Notified when a call are removed from a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callRemoved(CallGroupEvent evt)
{
}
}

@ -109,6 +109,16 @@ public String getAddress()
return peerAddress;
}
/**
* Returns full URI of the address.
*
* @return full URI of the address
*/
public String getURI()
{
return "gibberish:" + peerAddress;
}
/**
* Returns a reference to the call that this peer belongs to.
*

@ -120,6 +120,43 @@ public Call createCall(Contact callee)
return createCall(callee.getAddress());
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>String</tt> URI.
*
* @param callee the address of the callee who we should invite to a new
* <tt>Call</tt>
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(String callee, CallGroup group)
throws OperationFailedException
{
return createCall(callee);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>Contact</tt>.
*
* @param callee the address of the callee who we should invite to a new
* call
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(Contact callee, CallGroup group)
throws OperationFailedException
{
return createCall(callee);
}
/**
* Returns an iterator over all currently active calls.
*

@ -134,7 +134,7 @@ public CallPeerGTalkImpl processGTalkInitiate(SessionIQ sessionIQ)
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(this.getCallPeerCount() == 1)
if(this.getCallPeerCount() == 1 && this.getCallGroup() == null)
parentOpSet.fireCallEvent( CallEvent.CALL_RECEIVED, this);
return callPeer;

@ -289,8 +289,21 @@ public CallPeerJabberImpl initiateSession(
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(getCallPeerCount() == 1)
if(getCallPeerCount() == 1 && getCallGroup() == null)
{
parentOpSet.fireCallEvent(CallEvent.CALL_INITIATED, this);
}
else if(getCallGroup() != null)
{
// only TelephonyConferencing OperationSet should know about it
CallEvent cEvent = new CallEvent(this, CallEvent.CALL_INITIATED);
AbstractOperationSetTelephonyConferencing<?,?,?,?,?> opSet =
(AbstractOperationSetTelephonyConferencing<?,?,?,?,?>)
getProtocolProvider().getOperationSet(
OperationSetTelephonyConferencing.class);
if(opSet != null)
opSet.outgoingCallCreated(cEvent);
}
CallPeerMediaHandlerJabberImpl mediaHandler
= callPeer.getMediaHandler();
@ -395,4 +408,24 @@ public CallPeerJabberImpl getPeerBySessInitPacketID(String id)
}
return null;
}
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt)
{
Iterator<CallPeerJabberImpl> peers = getCallPeers();
// reflect conference focus
while(peers.hasNext())
{
setConferenceFocus(true);
CallPeerJabberImpl callPeer = peers.next();
callPeer.sendCoinSessionInfo(true);
}
super.callAdded(evt);
}
}

@ -84,6 +84,16 @@ public String getAddress()
return peerJID;
}
/**
* Returns full URI of the address.
*
* @return full URI of the address
*/
public String getURI()
{
return "xmpp:" + peerJID;
}
/**
* Specifies the address, phone number, or other protocol specific
* identifier that represents this call peer. This method is to be
@ -269,7 +279,7 @@ protected synchronized void initiateSession(
{
return;
}
getMediaHandler().harvestCandidates(offer.getPayloadTypes(),
new CandidatesSender()
{

@ -119,6 +119,16 @@ public String getAddress()
return peerJID;
}
/**
* Returns full URI of the address.
*
* @return full URI of the address
*/
public String getURI()
{
return "xmpp:" + peerJID;
}
/**
* Specifies the address, phone number, or other protocol specific
* identifier that represents this call peer. This method is to be

@ -126,18 +126,21 @@ else if (registrationState == RegistrationState.UNREGISTERED)
*
* @param callee the address of the callee who we should invite to a new
* <tt>Call</tt>
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
* @see OperationSetBasicTelephony#createCall(String)
*/
public Call createCall(String callee)
public Call createCall(String callee, CallGroup group)
throws OperationFailedException
{
CallJabberImpl call = new CallJabberImpl(this);
CallPeer callPeer = null;
call.setCallGroup(group);
callPeer = createOutgoingCall(call, callee);
if (callPeer == null)
{
@ -155,6 +158,43 @@ public Call createCall(String callee)
return call;
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt>
* to it given by her <tt>Contact</tt>.
*
* @param callee the address of the callee who we should invite to a new
* call
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
* @see OperationSetBasicTelephony#createCall(Contact)
*/
public Call createCall(Contact callee, CallGroup group)
throws OperationFailedException
{
return createCall(callee.getAddress(), group);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>String</tt> URI.
*
* @param callee the address of the callee who we should invite to a new
* <tt>Call</tt>
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
* @see OperationSetBasicTelephony#createCall(String)
*/
public Call createCall(String callee)
throws OperationFailedException
{
return createCall(callee, null);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt>
* to it given by her <tt>Contact</tt>.
@ -170,7 +210,7 @@ public Call createCall(String callee)
public Call createCall(Contact callee)
throws OperationFailedException
{
return createCall(callee.getAddress());
return createCall(callee.getAddress(), null);
}
/**

@ -101,7 +101,7 @@ protected void basicTelephonyChanged(
*/
protected void notifyAll(Call call)
{
if(!call.isConferenceFocus())
if(call.getCallGroup() == null && !call.isConferenceFocus())
{
return;
}
@ -128,6 +128,9 @@ protected void notifyAll(Call call)
*/
private void notify(CallPeer callPeer)
{
if(!(callPeer instanceof CallPeerJabberImpl))
return;
// check that callPeer supports COIN before sending him a
// conference-info
String to = getBasicTelephony().getFullCalleeURI(callPeer.getAddress());
@ -166,11 +169,11 @@ private void notify(CallPeer callPeer)
* @return list of media packet extension
*/
private List<MediaPacketExtension> getMedia(
CallPeerJabberImpl callPeer,
MediaAwareCallPeer<?,?,?> callPeer,
boolean remote)
{
MediaPacketExtension ext = null;
CallPeerMediaHandlerJabberImpl mediaHandler =
CallPeerMediaHandler<?> mediaHandler =
callPeer.getMediaHandler();
List<MediaPacketExtension> ret =
new ArrayList<MediaPacketExtension>();
@ -212,7 +215,7 @@ private List<MediaPacketExtension> getMedia(
* @return user packet extension
*/
private UserPacketExtension getUser(
CallPeerJabberImpl callPeer)
MediaAwareCallPeer<?,?,?> callPeer)
{
UserPacketExtension ext = new UserPacketExtension(
callPeer.getAddress());
@ -222,7 +225,7 @@ private UserPacketExtension getUser(
ext.setDisplayText(callPeer.getDisplayName());
EndpointStatusType status = getEndpointStatus(callPeer);
endpoint = new EndpointPacketExtension(callPeer.getAddress());
endpoint = new EndpointPacketExtension(callPeer.getURI());
endpoint.setStatus(status);
medias = getMedia(callPeer, true);
@ -251,7 +254,8 @@ private UserPacketExtension getUser(
* an <tt>endpoint</tt> XML element and which describes the state of the
* specified <tt>callPeer</tt>
*/
private EndpointStatusType getEndpointStatus(CallPeerJabberImpl callPeer)
private EndpointStatusType getEndpointStatus(
MediaAwareCallPeer<?,?,?> callPeer)
{
CallPeerState callPeerState = callPeer.getState();
@ -314,7 +318,8 @@ private IQ getConferenceInfo(CallPeerJabberImpl callPeer, int version)
// conference-state
StatePacketExtension state = new StatePacketExtension();
state.setUserCount(call.getCallPeerCount() + 1);
state.setUserCount(call.getCallPeerCount() + 1 +
call.getCrossProtocolCallPeerCount());
iq.addExtension(state);
// users
@ -322,11 +327,11 @@ private IQ getConferenceInfo(CallPeerJabberImpl callPeer, int version)
// user
UserPacketExtension user = new UserPacketExtension(
parentProvider.getOurJID());
"xmpp:" + parentProvider.getOurJID());
// endpoint
EndpointPacketExtension endpoint = new EndpointPacketExtension(
parentProvider.getOurJID());
"xmpp:" + parentProvider.getOurJID());
endpoint.setStatus(EndpointStatusType.connected);
// media
@ -348,6 +353,16 @@ private IQ getConferenceInfo(CallPeerJabberImpl callPeer, int version)
users.addChildExtension(ext);
}
Iterator<CallPeer> crossProtocolCallPeerIter =
call.getCrossProtocolCallPeers();
while (crossProtocolCallPeerIter.hasNext())
{
UserPacketExtension ext = getUser(
(MediaAwareCallPeer<?,?,?>)crossProtocolCallPeerIter.next());
users.addChildExtension(ext);
}
iq.addExtension(users);
return iq;
}
@ -531,27 +546,6 @@ public void processPacket(Packet packet)
handleCoin(coinIQ, callPeer);
}
/**
* Removes the parameters (specified after a slash) from a specific
* address <tt>String</tt> if any are present in it.
*
* @param address the <tt>String</tt> value representing an address from
* which any parameters are to be removed
* @return a <tt>String</tt> representing the specified <tt>address</tt>
* without any parameters
*/
private static String stripParametersFromAddress(String address)
{
if (address != null)
{
int parametersBeginIndex = address.indexOf('/');
if (parametersBeginIndex > -1)
address = address.substring(0, parametersBeginIndex);
}
return address;
}
/**
* Handle Coin IQ.
*
@ -594,9 +588,7 @@ private void handleCoin(CoinIQ coinIQ, CallPeerJabberImpl callPeer)
if(u.getAttribute("entity") != null)
{
address
= stripParametersFromAddress(
(String)u.getAttribute("entity"));
address = (String)u.getAttribute("entity");
}
if ((address == null) || (address.length() < 1))

@ -26,6 +26,11 @@ public class MockCall
*/
private static final Logger logger = Logger.getLogger(MockCall.class);
/**
* Constructs a new <tt>MockCall</tt>.
*
* @param sourceProvider Provider
*/
public MockCall(MockProvider sourceProvider)
{
super(sourceProvider);
@ -150,4 +155,22 @@ public void removeLocalUserSoundLevelListener(
SoundLevelListener l)
{
}
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt)
{
}
/**
* Notified when a call are removed from a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callRemoved(CallGroupEvent evt)
{
}
}

@ -52,6 +52,16 @@ public String getAddress()
return peerAddress;
}
/**
* Returns full URI of the address.
*
* @return full URI of the address
*/
public String getURI()
{
return "mock:" + peerAddress;
}
/**
* Returns a reference to the call that this peer belongs to.
*

@ -103,6 +103,43 @@ public Call createCall(Contact callee) throws OperationFailedException
return createNewCall(callee.getAddress());
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>String</tt> URI.
*
* @param callee the address of the callee who we should invite to a new
* <tt>Call</tt>
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(String callee, CallGroup group)
throws OperationFailedException,
ParseException
{
return createCall(callee);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>Contact</tt>.
*
* @param callee the address of the callee who we should invite to a new
* call
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(Contact callee, CallGroup group)
throws OperationFailedException
{
return createCall(callee);
}
private Call createNewCall(String address)
{
MockCall newCall = new MockCall(protocolProvider);

@ -150,6 +150,16 @@ public String getAddress()
return sipURI.getUser() + "@" + sipURI.getHost();
}
/**
* Returns full URI of the address.
*
* @return full URI of the address
*/
public String getURI()
{
return getPeerAddress().getURI().toString();
}
/**
* Returns the address of the remote party (making sure that it corresponds
* to the latest address we've received) and caches it.

@ -301,11 +301,14 @@ else if(mediaType.equals(MediaType.AUDIO))
logger.warn("Error getting media types", t);
}
getParentOperationSet().fireCallEvent( (incomingCall
if(this.getCallGroup() == null)
{
getParentOperationSet().fireCallEvent( (incomingCall
? CallEvent.CALL_RECEIVED
: CallEvent.CALL_INITIATED),
this,
mediaDirections);
}
}
return callPeer;
@ -407,4 +410,33 @@ public void setInitialQualityPreferences(QualityPreset qualityPreferences)
{
this.initialQualityPreferences = qualityPreferences;
}
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt)
{
Iterator<CallPeerSipImpl> peers = getCallPeers();
// reinvite peers to reflect conference focus
while(peers.hasNext())
{
setConferenceFocus(true);
CallPeerSipImpl callPeer = peers.next();
try
{
callPeer.sendReInvite();
}
catch(OperationFailedException e)
{
logger.info("Failed to reinvite peer: "
+ callPeer.getAddress());
}
}
super.callAdded(evt);
}
}

@ -111,9 +111,7 @@ public Call createCall(String callee)
throws OperationFailedException,
ParseException
{
Address toAddress = protocolProvider.parseAddressString(callee);
return createOutgoingCall(toAddress, null);
return createCall(callee, null);
}
/**
@ -129,6 +127,47 @@ public Call createCall(String callee)
* @see OperationSetBasicTelephony#createCall(Contact)
*/
public Call createCall(Contact callee) throws OperationFailedException
{
return createCall(callee, null);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>String</tt> URI.
*
* @param callee the address of the callee who we should invite to a new
* <tt>Call</tt>
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
* @throws ParseException if <tt>callee</tt> is not a valid SIP address
* <tt>String</tt>
*/
public Call createCall(String callee, CallGroup group)
throws OperationFailedException,
ParseException
{
Address toAddress = protocolProvider.parseAddressString(callee);
return createOutgoingCall(toAddress, null, group);
}
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt>
* to it given by her <tt>Contact</tt>.
*
* @param callee the address of the callee who we should invite to a new
* call
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(Contact callee, CallGroup group)
throws OperationFailedException
{
Address toAddress;
@ -143,7 +182,7 @@ public Call createCall(Contact callee) throws OperationFailedException
throw new IllegalArgumentException(ex.getMessage());
}
return createOutgoingCall(toAddress, null);
return createOutgoingCall(toAddress, null, group);
}
/**
@ -185,10 +224,12 @@ protected synchronized CallSipImpl createOutgoingCall()
* to create the call.
*/
private synchronized CallSipImpl createOutgoingCall(Address calleeAddress,
javax.sip.message.Message cause) throws OperationFailedException
javax.sip.message.Message cause, CallGroup group)
throws OperationFailedException
{
CallSipImpl call = createOutgoingCall();
call.setCallGroup(group);
call.invite(calleeAddress, cause);
return call;
@ -563,7 +604,7 @@ public boolean processResponse(ResponseEvent responseEvent)
* We have to bypass SIP specifications in the SIP NOTIFY
* message is desktop sharing specific and thus do not close the
* call.
*
*
* XXX this is not an optimal solution, the ideal will be
* to prevent disordering.
*/
@ -1328,7 +1369,8 @@ private void processRefer(ServerTransaction serverTransaction,
CallSipImpl referToCall;
try
{
referToCall = createOutgoingCall(referToAddress, referRequest);
referToCall = createOutgoingCall(referToAddress, referRequest,
null);
}
catch (OperationFailedException ex)
{

@ -252,9 +252,13 @@ protected void basicTelephonyChanged(
*/
public void callPeerAdded(CallPeerEvent event)
{
super.callPeerAdded(event);
if(!(event.getSourceCallPeer() instanceof CallPeerSipImpl))
return;
CallPeerSipImpl callPeer = (CallPeerSipImpl) event.getSourceCallPeer();
callPeer.addMethodProcessorListener(this);
super.callPeerAdded(event);
}
/**
@ -331,7 +335,8 @@ private String getConferenceInfoXML(CallPeerSipImpl callPeer, int version)
append(xml, "<", ELEMENT_CONFERENCE_STATE, ">");
// <user-count>
append(xml, "<", ELEMENT_USER_COUNT, ">");
xml.append(call.getCallPeerCount() + 1);
xml.append(call.getCallPeerCount() + 1 +
call.getCrossProtocolCallPeerCount());
// </user-count>
append(xml, "</", ELEMENT_USER_COUNT, ">");
// </conference-state>
@ -375,6 +380,15 @@ private String getConferenceInfoXML(CallPeerSipImpl callPeer, int version)
while (callPeerIter.hasNext())
getUserXML(callPeerIter.next(), xml);
// cross-protocol CallPeer
Iterator<CallPeer> crossProtocolCallPeerIter =
call.getCrossProtocolCallPeers();
while (crossProtocolCallPeerIter.hasNext())
getUserXML(
(MediaAwareCallPeer<?,?,?>)crossProtocolCallPeerIter.next(),
xml);
// </users>
append(xml, "</", ELEMENT_USERS, ">");
// </conference-info>
@ -477,7 +491,7 @@ private String getEndpointStatus(Node endpoint)
* an <tt>endpoint</tt> XML element and which describes the state of the
* specified <tt>callPeer</tt>
*/
private String getEndpointStatusXML(CallPeerSipImpl callPeer)
private String getEndpointStatusXML(MediaAwareCallPeer<?,?,?> callPeer)
{
CallPeerState callPeerState = callPeer.getState();
@ -523,11 +537,11 @@ private String getEndpointStatusXML(CallPeerSipImpl callPeer)
* <tt>callPeer</tt>
*/
private void getMediaXML(
CallPeerSipImpl callPeer,
MediaAwareCallPeer<?,?,?> callPeer,
boolean remote,
StringBuffer xml)
{
CallPeerMediaHandlerSipImpl mediaHandler = callPeer.getMediaHandler();
CallPeerMediaHandler<?> mediaHandler = callPeer.getMediaHandler();
for (MediaType mediaType : MediaType.values())
{
@ -585,7 +599,8 @@ private void getMediaXML(
* tree describing the conference participation of the specified
* <tt>callPeer</tt> to
*/
private void getUserXML(CallPeerSipImpl callPeer, StringBuffer xml)
private void getUserXML(MediaAwareCallPeer<?,?,?> callPeer,
StringBuffer xml)
{
// <user>
append(xml, "<", ELEMENT_USER);
@ -594,7 +609,7 @@ private void getUserXML(CallPeerSipImpl callPeer, StringBuffer xml)
xml,
" entity=\"",
domElementWriter
.encode(stripParametersFromAddress(callPeer.getAddress())),
.encode(stripParametersFromAddress(callPeer.getURI())),
"\"");
// state
xml.append(" state=\"full\">");

@ -29,6 +29,17 @@ public abstract class AbstractCall<T extends CallPeer,
*/
private Vector<T> callPeers = new Vector<T>();
/**
* List of <tt>CallPeer</tt> from other protocol (cross-protocol conference
* call).
*/
private Vector<CallPeer> crossProtocolCallPeers = new Vector<CallPeer>();
/**
* The <tt>CallGroup</tt> of this <tt>Call</tt>.
*/
protected CallGroup callGroup = null;
/**
* Creates a new Call instance.
*
@ -73,6 +84,43 @@ protected Vector<T> getCallPeersVector()
return callPeers;
}
/**
* Returns an iterator over all cross-protocol call peers.
*
* @return an Iterator over all cross-protocol peers currently involved in
* the call.
*/
public Iterator<CallPeer> getCrossProtocolCallPeers()
{
return new LinkedList<CallPeer>(getCrossProtocolCallPeersVector()).
iterator();
}
/**
* Returns the number of cross-protocol peers currently associated with this
* call.
*
* @return an <tt>int</tt> indicating the number of cross-protocol peers
* currently associated with this call.
*/
public int getCrossProtocolCallPeerCount()
{
return crossProtocolCallPeers.size();
}
/**
* Returns the {@link Vector} containing cross-protocol {@link CallPeer}s
* currently part of this call. This method should eventually be removed and
* code that is using it in the descendants should be brought here.
*
* @return the {@link Vector} containing cross-protocol {@link CallPeer}s
* currently participating in this call.
*/
protected Vector<CallPeer> getCrossProtocolCallPeersVector()
{
return crossProtocolCallPeers;
}
/**
* Returns a reference to the <tt>ProtocolProviderService</tt> instance
* that created this call.
@ -85,4 +133,25 @@ public U getProtocolProvider()
{
return (U) super.getProtocolProvider();
}
/**
* Returns the <tt>CallGroup</tt> from which this <tt>Call</tt> belongs.
*
* @return <tt>CallGroup</tt> or null if the <tt>Call</tt> does not belongs
* to a <tt>CallGroup</tt>
*/
public CallGroup getCallGroup()
{
return callGroup;
}
/**
* Sets the <tt>CallGroup</tt> of this <tt>Call</tt>.
*
* @param callGroup <tt>CallGroup</tt> to set
*/
public void setCallGroup(CallGroup callGroup)
{
this.callGroup = callGroup;
}
}

@ -21,6 +21,7 @@
* @author Emanuel Onica
*/
public abstract class Call
implements CallGroupListener
{
/**
* Our class logger.
@ -301,7 +302,7 @@ protected void setCallState(CallState newState)
*
* @param newState a reference to the <tt>CallState</tt> instance that the
* call is to enter.
* @param cause the event that is the cause of the current change of state.
* @param cause the event that is the cause of the current change of state.
*/
protected void setCallState(CallState newState, CallPeerChangeEvent cause)
{
@ -336,6 +337,21 @@ public boolean isSipZrtpAttribute()
return sipZrtpAttribute;
}
/**
* Sets the <tt>CallGroup</tt> of this <tt>Call</tt>.
*
* @param callGroup <tt>CallGroup</tt> to set
*/
public abstract void setCallGroup(CallGroup callGroup);
/**
* Returns the <tt>CallGroup</tt> from which this <tt>Call</tt> belongs.
*
* @return <tt>CallGroup</tt> or null if the <tt>Call</tt> does not belongs
* to a <tt>CallGroup</tt>
*/
public abstract CallGroup getCallGroup();
/**
* Returns an iterator over all call peers.
*
@ -351,6 +367,23 @@ public boolean isSipZrtpAttribute()
*/
public abstract int getCallPeerCount();
/**
* Returns an iterator over all cross-protocol call peers.
*
* @return an Iterator over all cross-protocol peers currently involved in
* the call.
*/
public abstract Iterator<CallPeer> getCrossProtocolCallPeers();
/**
* Returns the number of cross-protocol peers currently associated with this
* call.
*
* @return an <tt>int</tt> indicating the number of cross-protocol peers
* currently associated with this call.
*/
public abstract int getCrossProtocolCallPeerCount();
/**
* Gets the indicator which determines whether the local peer represented by
* this <tt>Call</tt> is acting as a conference focus and thus should send
@ -376,5 +409,6 @@ public boolean isSipZrtpAttribute()
* related information.
* @param l the <tt>SoundLevelListener</tt> to remove
*/
public abstract void removeLocalUserSoundLevelListener(SoundLevelListener l);
public abstract void removeLocalUserSoundLevelListener(
SoundLevelListener l);
}

@ -0,0 +1,158 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol;
import java.util.*;
import net.java.sip.communicator.service.protocol.event.*;
/**
* This class represents group of several calls. It is used to make
* cross-protocol conference calls.
*
* @author Sebastien Vincent
*/
public class CallGroup
implements CallChangeListener
{
/**
* List of <tt>Call</tt>s.
*/
private List<Call> calls = new ArrayList<Call>();
/**
* Synchronization object.
*/
private final Object syncRoot = new Object();
/**
* Returns list of existing <tt>Call</tt>s in this <tt>CallGroup</tt>.
*
* @return list of existing <tt>Call</tt>s in this <tt>CallGroup</tt>.
*/
public List<Call> getCalls()
{
return calls;
}
/**
* Fires <tt>CallGroupEvent</tt>.
*
* @param call source <tt>Call</tt>
* @param eventID event ID
*/
public void fireCallGroupEvent(Call call, int eventID)
{
CallGroupEvent evt = new CallGroupEvent(call, eventID);
for(Call c : calls)
{
if(c == call)
continue;
switch(eventID)
{
case CallGroupEvent.CALLGROUP_CALL_ADDED:
c.callAdded(evt);
call.callAdded(new CallGroupEvent(c, eventID));
break;
case CallGroupEvent.CALLGROUP_CALL_REMOVED:
c.callRemoved(evt);
call.callRemoved(new CallGroupEvent(c, eventID));
break;
default:
break;
}
}
}
/**
* Adds a call.
*
* @param call call to add
*/
public void addCall(Call call)
{
synchronized(syncRoot)
{
if(!calls.contains(call))
{
call.addCallChangeListener(this);
call.setCallGroup(this);
calls.add(call);
}
}
}
/**
* Removes a call.
*
* @param call call to remove
*/
public void removeCall(Call call)
{
synchronized(syncRoot)
{
if(calls.contains(call))
{
call.removeCallChangeListener(this);
calls.remove(call);
call.setCallGroup(null);
}
}
}
/**
* Indicates that a new call peer has joined the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerAdded(CallPeerEvent evt)
{
/* not used */
}
/**
* Indicates that a call peer has left the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerRemoved(CallPeerEvent evt)
{
/* not used */
}
/**
* Indicates that a change has occurred in the state of the source call.
*
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
* calls and its old and new state.
*/
public void callStateChanged(CallChangeEvent evt)
{
Call call = evt.getSourceCall();
if(evt.getEventType() == CallChangeEvent.CALL_STATE_CHANGE)
{
CallState state = (CallState)evt.getNewValue();
if(evt.getNewValue() == evt.getOldValue())
return;
if(state == CallState.CALL_ENDED)
{
fireCallGroupEvent(call, CallGroupEvent.CALLGROUP_CALL_REMOVED);
}
else if(state == CallState.CALL_IN_PROGRESS)
{
fireCallGroupEvent(call, CallGroupEvent.CALLGROUP_CALL_ADDED);
}
}
}
}

@ -72,6 +72,14 @@ public interface CallPeer
*/
public String getAddress();
/**
* Returns full URI of the address. For example sip:user@domain.org or
* xmpp:user@domain.org.
*
* @return full URI of the address
*/
public String getURI();
/**
* Returns an object representing the current state of that peer.
* CallPeerState may vary among CONNECTING, RINGING, CALLING, BUSY,

@ -88,6 +88,39 @@ public Call createCall(String uri)
public Call createCall(Contact callee)
throws OperationFailedException;
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt> to
* it given by her <tt>String</tt> URI.
*
* @param uri the address of the callee who we should invite to a new
* <tt>Call</tt>
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
* @throws ParseException if <tt>callee</tt> is not a valid SIP address
* <tt>String</tt>
*/
public Call createCall(String uri, CallGroup group)
throws OperationFailedException,
ParseException;
/**
* Creates a new <tt>Call</tt> and invites a specific <tt>CallPeer</tt>
* to it given by her <tt>Contact</tt>.
*
* @param callee the address of the callee who we should invite to a new
* call
* @param group <tt>CallGroup</tt> from which the <tt>Call</tt> will belong
* @return a newly created <tt>Call</tt>. The specified <tt>callee</tt> is
* available in the <tt>Call</tt> as a <tt>CallPeer</tt>
* @throws OperationFailedException with the corresponding code if we fail
* to create the call
*/
public Call createCall(Contact callee, CallGroup group)
throws OperationFailedException;
/**
* Indicates a user request to answer an incoming call from the specified
* CallPeer.

@ -0,0 +1,74 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol.event;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* An event class representing that an <tt>Call</tt> or <tt>CallPeer</tt> is
* added/removed to/from a <tt>CallGroup</tt>.
*
* @author Sebastien Vincent
*/
public class CallGroupEvent
extends EventObject
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* CALLGROUP_CALL_ADDED event name.
*/
public static final int CALLGROUP_CALL_ADDED = 0;
/**
* CALLGROUP_CALL_REMOVED event name.
*/
public static final int CALLGROUP_CALL_REMOVED = 1;
/**
* The id indicating the type of this event.
*/
private final int eventID;
/**
* Constructor.
*
* @param call call source
* @param eventID event ID
*/
public CallGroupEvent(Call call, int eventID)
{
super(call);
this.eventID = eventID;
}
/**
* Returns one of the CALLGROUP_XXX member ints indicating
* the type of this event.
* @return one of the CALLGROUP_XXX member ints indicating
* the type of this event.
*/
public int getEventID()
{
return this.eventID;
}
/**
* Returns the source call.
*
* @return The source call
*/
public Call getSourceCall()
{
return (Call)getSource();
}
}

@ -0,0 +1,33 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol.event;
import java.util.*;
/**
* Instances of this class are used for listening for notifications coming out
* of a <tt>CallGroup</tt>.
*
* @author Sebastien Vincent
*/
public interface CallGroupListener
extends EventListener
{
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt);
/**
* Notified when a call are removed from a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callRemoved(CallGroupEvent evt);
}

@ -318,11 +318,10 @@ else if (RegistrationState.UNREGISTERED.equals(newState))
* @param event a <tt>CallPeerEvent</tt> which specifies the
* <tt>CallPeer</tt> which has been added to a <tt>Call</tt>
*/
@SuppressWarnings("unchecked")
public void callPeerAdded(CallPeerEvent event)
{
MediaAwareCallPeerT callPeer =
(MediaAwareCallPeerT)event.getSourceCallPeer();
MediaAwareCallPeer<?,?,?> callPeer =
(MediaAwareCallPeer<?,?,?>)event.getSourceCallPeer();
callPeer.addCallPeerListener(callPeerListener);
callPeer.getMediaHandler().addPropertyChangeListener(this);
@ -357,7 +356,10 @@ public void callPeerRemoved(CallPeerEvent event)
*/
private void callPeersChanged(CallPeerEvent event)
{
notifyAll(event.getSourceCall());
Call call = event.getSourceCall();
notifyAll(call);
notifyCallsInGroup(call);
}
/**
@ -389,6 +391,7 @@ public void propertyChange(PropertyChangeEvent event)
if (call != null)
{
notifyAll(call);
notifyCallsInGroup(call);
}
}
}
@ -483,6 +486,33 @@ public void callStateChanged(CallChangeEvent event)
{
}
/**
* Notify the <tt>Call</tt>s in the <tt>CallGroup</tt> if any.
*
* @param call the <tt>Call</tt>
*/
private void notifyCallsInGroup(Call call)
{
if(call.getCallGroup() != null)
{
CallGroup group = call.getCallGroup();
for(Call c : group.getCalls())
{
if(c == call)
continue;
AbstractOperationSetTelephonyConferencing<?,?,?,?,?> opSet =
(AbstractOperationSetTelephonyConferencing<?,?,?,?,?>)
c.getProtocolProvider().getOperationSet(
OperationSetTelephonyConferencing.class);
if(opSet != null)
{
opSet.notifyAll(c);
}
}
}
}
/**
* Notifies all CallPeer associated with and established in a
* specific call for conference information.

@ -41,6 +41,7 @@ public abstract class MediaAwareCall<
V extends ProtocolProviderService>
extends AbstractCall<T, V>
implements CallPeerListener,
CallChangeListener,
PropertyChangeListener
{
/**
@ -429,6 +430,27 @@ public MediaDevice getDefaultDevice(MediaType mediaType)
{
MediaDevice device;
if(conferenceAudioMixer == null && mediaType == MediaType.AUDIO &&
callGroup != null && callGroup.getCalls().size() > 0)
{
conferenceAudioMixer =
((MediaAwareCall<?,?,?>)callGroup.getCalls().get(0)).
conferenceAudioMixer;
device = conferenceAudioMixer;
}
/*
else if(conferenceVideoMixer == null && mediaType == MediaType.VIDEO &&
callGroup != null && callGroup.getCalls().size() > 0 &&
isConferenceFocus())
{
conferenceVideoMixer =
((MediaAwareCall<?,?,?>)callGroup.getCalls().get(0)).
conferenceVideoMixer;
device = conferenceVideoMixer;
}
*/
switch (mediaType)
{
case AUDIO:
@ -469,7 +491,7 @@ public MediaDevice getDefaultDevice(MediaType mediaType)
case VIDEO:
if (isConferenceFocus())
{
if ((conferenceVideoMixer == null) && (device != null))
if ((conferenceVideoMixer == null) && (device != null))
conferenceVideoMixer = mediaService.createMixer(device);
if (conferenceVideoMixer != null)
device = conferenceVideoMixer;
@ -925,6 +947,7 @@ public void propertyChange(PropertyChangeEvent evt)
if(propertyName.equals("CHANGE_CAPTURE_DEV"))
{
conferenceAudioMixer = null;
audioDevice = null;
for(MediaAwareCallPeer<?,?,?> p : getCallPeersVector())
{
MediaStream stream =
@ -938,4 +961,76 @@ public void propertyChange(PropertyChangeEvent evt)
}
}
}
/**
* Indicates that a new call peer has joined the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerAdded(CallPeerEvent evt)
{
/* peer from another protocol (for cross-protocol conference call) */
getCrossProtocolCallPeersVector().add(evt.getSourceCallPeer());
}
/**
* Indicates that a call peer has left the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerRemoved(CallPeerEvent evt)
{
/* peer from another protocol (for cross-protocol conference call) */
getCrossProtocolCallPeersVector().remove(evt.getSourceCallPeer());
}
/**
* Indicates that a change has occurred in the state of the source call.
*
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
* calls and its old and new state.
*/
public void callStateChanged(CallChangeEvent evt)
{
}
/**
* Notified when a call are added to a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callAdded(CallGroupEvent evt)
{
Call c = evt.getSourceCall();
c.addCallChangeListener(this);
Iterator<? extends CallPeer> peers = c.getCallPeers();
while(peers.hasNext())
{
CallPeer p = peers.next();
getCrossProtocolCallPeersVector().add(p);
fireCallPeerEvent(p, CallPeerEvent.CALL_PEER_ADDED);
}
}
/**
* Notified when a call are removed from a <tt>CallGroup</tt>.
*
* @param evt event
*/
public void callRemoved(CallGroupEvent evt)
{
Call c = evt.getSourceCall();
c.removeCallChangeListener(this);
Iterator<? extends CallPeer> peers = c.getCallPeers();
while(peers.hasNext())
{
CallPeer p = peers.next();
getCrossProtocolCallPeersVector().remove(p);
fireCallPeerEvent(p, CallPeerEvent.CALL_PEER_REMOVED);
}
}
}

Loading…
Cancel
Save