diff --git a/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java b/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java index e326f7318..b62a525c0 100644 --- a/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java @@ -18,7 +18,7 @@ * @author Yana Stamcheva */ public class OperationSetBasicTelephonyGibberishImpl - extends AbstractOperationSetBasicTelephony + extends AbstractOperationSetBasicTelephony implements CallChangeListener { private static final Logger logger @@ -271,4 +271,15 @@ else if (on) gibberishPeer.setState(CallPeerState.ON_HOLD_LOCALLY); } } + + /** + * Returns the protocol provider that this operation set belongs to. + * + * @return a reference to the ProtocolProviderService that created + * this operation set. + */ + public ProtocolProviderServiceGibberishImpl getProtocolProvider() + { + return protocolProvider; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java index 00943e8cc..abb1a5662 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java @@ -9,6 +9,7 @@ import org.jivesoftware.smack.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.service.neomedia.event.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java new file mode 100644 index 000000000..7dbec209e --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -0,0 +1,116 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber; + +import java.net.*; + +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.media.*; + +/** + * @author Emil Ivov + */ +public class CallPeerMediaHandlerJabberImpl + extends CallPeerMediaHandler +{ + + /** + * Creates a new handler that will be managing media streams for + * peer. + * + * @param peer that CallPeerSipImpl instance that we will be + * managing media for. + */ + public CallPeerMediaHandlerJabberImpl(CallPeerJabberImpl peer) + { + //TODO - callpeer jabber impl should implement otr listeenr + super(peer, null); + } + + /** + * Should return a reference to the currently valid configuration service. + * We need this method in order to keep the protocol-media bundle as light + * as possible: we don't want it to have an activator and deal with a + * bundle context. + * + * @return a reference to the currently valid {@link ConfigurationService} + */ + @Override + protected ConfigurationService getConfigurationService() + { + return JabberActivator.getConfigurationService(); + } + + /** + * Returns a reference to the currently valid media service. + * + * @return a reference to the currently valid {@link MediaService} + */ + @Override + protected MediaService getMediaService() + { + return JabberActivator.getMediaService(); + } + + /** + * Returns a reference to the currently valid network address service. + * + * @return a reference to the currently valid {@link + * NetworkAddressManagerService} + */ + @Override + protected NetworkAddressManagerService getNetworkAddressManagerService() + { + return JabberActivator.getNetworkAddressManagerService(); + } + + /** + * Lets the underlying implementation take note of this error and only + * then throws it to the using bundles. + * + * @param message the message to be logged and then wrapped in a new + * OperationFailedException + * @param errorCode the error code to be assigned to the new + * OperationFailedException + * @param cause the Throwable that has caused the necessity to log + * an error and have a new OperationFailedException thrown + * + * @throws OperationFailedException the exception that we wanted this method + * to throw. + */ + @Override + protected void throwOperationFailedException(String message, int errorCode, + Throwable cause) throws OperationFailedException + { + // TODO Auto-generated method stub - implement + } + + /** + * Returns the InetAddress that is most likely to be to be used + * as a next hop when contacting the specified destination. This is + * an utility method that is used whenever we have to choose one of our + * local addresses to put in the Via, Contact or (in the case of no + * registrar accounts) From headers. + * + * @param peer the CallPeer that we would contact. + * + * @return the InetAddress that is most likely to be to be used + * as a next hop when contacting the specified destination. + * + * @throws IllegalArgumentException if destination is not a valid + * host/ip/fqdn + */ + @Override + protected InetAddress getIntendedDestination(CallPeerJabberImpl peer) + { + /* TODO implement */ + return null; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java index 877e4d4ec..5f83ea12b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java @@ -11,6 +11,7 @@ import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; @@ -47,6 +48,12 @@ public class JabberActivator */ private static MediaService mediaService = null; + /** + * A reference to the currently valid {@link NetworkAddressManagerService}. + */ + private static NetworkAddressManagerService + networkAddressManagerService = null; + /** * The Jabber protocol provider factory. */ @@ -246,4 +253,25 @@ public static MediaService getMediaService() } return mediaService; } + + /** + * Returns a reference to a NetworkAddressManagerService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a currently valid implementation of the + * NetworkAddressManagerService . + */ + public static NetworkAddressManagerService getNetworkAddressManagerService() + { + if(networkAddressManagerService == null) + { + ServiceReference confReference + = bundleContext.getServiceReference( + NetworkAddressManagerService.class.getName()); + networkAddressManagerService = (NetworkAddressManagerService) + bundleContext.getService(confReference); + } + return networkAddressManagerService; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java index 3d771b41a..691c8550d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -450,11 +450,11 @@ else if(action == JingleAction.SESSION_ACCEPT) } /** - * Returns a reference to the {@link ActiveCallsRepositoryJabberImpl} that we are - * currently using. + * Returns a reference to the {@link ActiveCallsRepositoryJabberImpl} that + * we are currently using. * - * @return a reference to the {@link ActiveCallsRepositoryJabberImpl} that we are - * currently using. + * @return a reference to the {@link ActiveCallsRepositoryJabberImpl} that + * we are currently using. */ protected ActiveCallsRepositoryJabberImpl getActiveCallsRepository() { diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java index 7ed0e2978..b4cff0381 100644 --- a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java +++ b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java @@ -19,7 +19,7 @@ * @author Damian Minkov */ public class MockOperationSetBasicTelephony - extends AbstractOperationSetBasicTelephony + extends AbstractOperationSetBasicTelephony implements CallChangeListener { private static final Logger logger @@ -240,4 +240,15 @@ public void callStateChanged(CallChangeEvent evt) fireCallEvent(CallEvent.CALL_ENDED, sourceCall); } } + + /** + * Returns the protocol provider that this operation set belongs to. + * + * @return a reference to the ProtocolProviderService that created + * this operation set. + */ + public MockProvider getProtocolProvider() + { + return protocolProvider; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java index bfcf4894e..1e16fed2e 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -18,6 +18,7 @@ import net.java.sip.communicator.service.neomedia.event.SimpleAudioLevelListener; 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.*; /** @@ -26,7 +27,9 @@ * @author Emil Ivov */ public class CallSipImpl - extends AbstractCall + extends AbstractMediaAwareCall implements CallPeerListener { /** @@ -34,71 +37,12 @@ public class CallSipImpl */ private static final Logger logger = Logger.getLogger(CallSipImpl.class); - /** - * The MediaDevice which performs audio mixing for this - * Call and its CallPeers when the local peer represented - * by this Call is acting as a conference focus i.e. - * {@link #conferenceFocus} is true. - */ - private MediaDevice conferenceAudioMixer; - - /** - * The indicator which determines whether the local peer represented by this - * Call is acting as a conference focus and is thus specifying the - * "isfocus" parameter in the Contact headers of its outgoing SIP - * signaling. - */ - private boolean conferenceFocus = false; - - /** - * Our video streaming policy. - */ - private boolean localVideoAllowed = false; - /** * A reference to the SipMessageFactory instance that we should * use when creating requests. */ private final SipMessageFactory messageFactory; - /** - * A reference to the OperationSetBasicTelephonySipImpl that - * created us; - */ - private final OperationSetBasicTelephonySipImpl parentOpSet; - - /** - * Holds listeners registered for level changes in local audio. - */ - private final List localUserAudioLevelListeners - = new ArrayList(); - - /** - * The indicator which determines whether this Call is set - * to transmit "silence" instead of the actual media. - */ - private boolean mute = false; - - /** - * Device used in call will be choosen according to MediaUseCase. - */ - MediaUseCase mediaUseCase = MediaUseCase.ANY; - - /** - * The listener that would actually subscribe for level events from the - * media handler if there's at least one listener in - * localUserAudioLevelListeners. - */ - private final SimpleAudioLevelListener localAudioLevelDelegator - = new SimpleAudioLevelListener() - { - public void audioLevelChanged(int level) - { - fireLocalUserAudioLevelChangeEvent(level); - } - }; - - /** * Crates a CallSipImpl instance belonging to sourceProvider and * initiated by CallCreator. @@ -108,154 +52,15 @@ public void audioLevelChanged(int level) */ protected CallSipImpl(OperationSetBasicTelephonySipImpl parentOpSet) { - super(parentOpSet.getProtocolProvider()); + super(parentOpSet); this.messageFactory = getProtocolProvider().getMessageFactory(); - this.parentOpSet = parentOpSet; //let's add ourselves to the calls repo. we are doing it ourselves just //to make sure that no one ever forgets. parentOpSet.getActiveCallsRepository().addCall(this); } - /** - * Adds callPeer to the list of peers in this call. - * If the call peer is already included in the call, the method has - * no effect. - * - * @param callPeer the new CallPeer - */ - private void addCallPeer(CallPeerSipImpl callPeer) - { - if (getCallPeersVector().contains(callPeer)) - return; - - callPeer.addCallPeerListener(this); - - synchronized(localUserAudioLevelListeners) - { - // if there's someone listening for audio level events then they'd - // also like to know about the new peer. - if(getCallPeersVector().size() == 0) - { - callPeer.getMediaHandler().setLocalUserAudioLevelListener( - localAudioLevelDelegator); - } - } - - getCallPeersVector().add(callPeer); - fireCallPeerEvent(callPeer, CallPeerEvent.CALL_PEER_ADDED); - } - - /** - * Removes callPeer from the list of peers in this - * call. The method has no effect if there was no such peer in the - * call. - * - * @param callPeer the CallPeer leaving the call; - */ - private void removeCallPeer(CallPeerSipImpl callPeer) - { - if (!getCallPeersVector().contains(callPeer)) - return; - - getCallPeersVector().remove(callPeer); - callPeer.removeCallPeerListener(this); - - synchronized(localUserAudioLevelListeners) - { - // remove sound level listeners from the peer - callPeer.getMediaHandler().setLocalUserAudioLevelListener(null); - } - - try - { - fireCallPeerEvent(callPeer, - CallPeerEvent.CALL_PEER_REMOVED); - } - finally - { - - /* - * The peer should loose its state once it has finished - * firing its events in order to allow the listeners to undo. - */ - callPeer.setCall(null); - } - - if (getCallPeersVector().size() == 0) - setCallState(CallState.CALL_ENDED); - } - - /** - * Dummy implementation of a method (inherited from CallPeerListener) - * that we don't need. - * - * @param evt unused. - */ - public void peerImageChanged(CallPeerChangeEvent evt) - { - //does not concern us - } - - /** - * Dummy implementation of a method (inherited from CallPeerListener) - * that we don't need. - * - * @param evt unused. - */ - public void peerAddressChanged(CallPeerChangeEvent evt) - { - //does not concern us - } - - /** - * Dummy implementation of a method (inherited from CallPeerListener) - * that we don't need. - * - * @param evt unused. - */ - public void peerTransportAddressChanged( - CallPeerChangeEvent evt) - { - //does not concern us - } - /** - * Dummy implementation of a method (inherited from CallPeerListener) - * that we don't need. - * - * @param evt unused. - */ - public void peerDisplayNameChanged(CallPeerChangeEvent evt) - { - //does not concern us - } - - /** - * Verifies whether the call peer has entered a state. - * - * @param evt The CallPeerChangeEvent instance containing - * the source event as well as its previous and its new status. - */ - public void peerStateChanged(CallPeerChangeEvent evt) - { - CallPeerState newState = (CallPeerState) evt.getNewValue(); - - if (CallPeerState.DISCONNECTED.equals(newState) - || CallPeerState.FAILED.equals(newState)) - { - removeCallPeer((CallPeerSipImpl) evt.getSourceCallPeer()); - } - else if (CallPeerState.CONNECTED.equals(newState) - || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(newState)) - { - setCallState(CallState.CALL_IN_PROGRESS); - } - else if (CallPeerState.REFERRED.equals(newState)) - { - setCallState(CallState.CALL_REFERRED); - } - } /** * Returns true if dialog matches the jain sip dialog @@ -324,18 +129,6 @@ public ProtocolProviderServiceSipImpl getProtocolProvider() return super.getProtocolProvider(); } - /** - * Returns a reference to the OperationSetBasicTelephonySipImpl - * instance that created this call. - * - * @return a reference to the OperationSetBasicTelephonySipImpl - * instance that created this call. - */ - public OperationSetBasicTelephonySipImpl getParentOperationSet() - { - return parentOpSet; - } - /** * Creates a CallPeerSipImpl from calleeAddress and sends * them an invite request. The invite request will be initialized according @@ -434,7 +227,7 @@ private CallPeerSipImpl createCallPeerFor( // new and we also need to notify everyone of its creation. if(this.getCallPeerCount() == 1) { - parentOpSet.fireCallEvent( (incomingCall + getParentOperationSet().fireCallEvent( (incomingCall ? CallEvent.CALL_RECEIVED : CallEvent.CALL_INITIATED), this); @@ -638,204 +431,4 @@ public void removeVideoPropertyChangeListener( peer.removeVideoPropertyChangeListener(listener); } } - - /** - * Gets the indicator which determines whether the local peer represented by - * this Call is acting as a conference focus and thus should send - * the "isfocus" parameter in the Contact headers of its outgoing - * SIP signaling. - * - * @return true if the local peer represented by this Call - * is acting as a conference focus; otherwise, false - */ - public boolean isConferenceFocus() - { - return conferenceFocus; - } - - /** - * Sets the indicator which determines whether the local peer represented by - * this Call is acting as a conference focus and thus should send - * the "isfocus" parameter in the Contact headers of its outgoing - * SIP signaling - * - * @param conferenceFocus true if the local peer represented by - * this Call is to act as a conference focus; otherwise, - * false - */ - void setConferenceFocus(boolean conferenceFocus) - { - if (this.conferenceFocus != conferenceFocus) - { - this.conferenceFocus = conferenceFocus; - - /* - * If this Call switches from being a conference focus to not being - * one, dispose of the audio mixer used when it was a conference - * focus. - */ - if (!this.conferenceFocus) - conferenceAudioMixer = null; - - // fire that the focus property has changed - fireCallChangeEvent( - CallChangeEvent.CALL_FOCUS_CHANGE, - !this.conferenceFocus, this.conferenceFocus); - } - } - - /** - * Gets a MediaDevice which is capable of capture and/or playback - * of media of the specified MediaType, is the default choice of - * the user for a MediaDevice with the specified MediaType - * and is appropriate for the current state of this Call. - *

- * For example, when the local peer represented by this Call - * instance is acting as a conference focus, the audio device must be a - * mixer. - *

- * - * @param mediaType the MediaType in which the retrieved - * MediaDevice is to capture and/or play back media - * @return a MediaDevice which is capable of capture and/or - * playback of media of the specified mediaType, is the default - * choice of the user for a MediaDevice with the specified - * mediaType and is appropriate for the current state of this - * Call - */ - MediaDevice getDefaultDevice(MediaType mediaType) - { - MediaService mediaService = SipActivator.getMediaService(); - MediaDevice device = mediaService.getDefaultDevice(mediaType, - mediaUseCase); - - if (MediaType.AUDIO.equals(mediaType) && isConferenceFocus()) - { - if (conferenceAudioMixer == null) - { - if (device != null) - conferenceAudioMixer = mediaService.createMixer(device); - } - return conferenceAudioMixer; - } - return device; - } - - /** - * Adds a specific SoundLevelListener to the list of - * listeners interested in and notified about changes in local sound level - * related information. When the first listener is being registered the - * method also registers its single listener with the call peer media - * handlers so that it would receive level change events and delegate them - * to the listeners that have registered with us. - * - * @param l the SoundLevelListener to add - */ - public void addLocalUserSoundLevelListener(SoundLevelListener l) - { - synchronized(localUserAudioLevelListeners) - { - - if (localUserAudioLevelListeners.size() == 0) - { - //if this is the first listener that's being registered with - //us, we also need to register ourselves as an audio level - //listener with the media handler. we do this so that audio - //level would only be calculated if anyone is interested in - //receiving them. - Iterator cps = getCallPeers(); - while (cps.hasNext()) - { - CallPeerSipImpl callPeerSipImpl = cps.next(); - callPeerSipImpl.getMediaHandler() - .setLocalUserAudioLevelListener( - localAudioLevelDelegator); - } - } - - localUserAudioLevelListeners.add(l); - } - } - - /** - * Removes a specific SoundLevelListener from the list of - * listeners interested in and notified about changes in local sound level - * related information. If l is the last listener that we had here - * we are also going to unregister our own level event delegator in order - * to stop level calculations. - * - * @param l the SoundLevelListener to remove - */ - public void removeLocalUserSoundLevelListener(SoundLevelListener l) - { - synchronized(localUserAudioLevelListeners) - { - localUserAudioLevelListeners.add(l); - - if (localUserAudioLevelListeners.size() == 0) - { - //if this was the last listener that was registered with us then - //no long need to have a delegator registered with the call - //peer media handlers. We therefore remove it so that audio - //level calculations would be ceased. - Iterator cps = getCallPeers(); - while (cps.hasNext()) - { - CallPeerSipImpl callPeerSipImpl = cps.next(); - callPeerSipImpl.getMediaHandler() - .setLocalUserAudioLevelListener(null); - } - } - } - } - - /** - * Notified by its very majesty the media service about changes in the - * audio level of the local user, this listener generates the corresponding - * events and delivers them to the listeners that have registered here. - * - * @param newLevel the new audio level of the local user. - */ - private void fireLocalUserAudioLevelChangeEvent(int newLevel) - { - SoundLevelChangeEvent evt - = new SoundLevelChangeEvent(this, newLevel); - - synchronized( localUserAudioLevelListeners ) - { - for(SoundLevelListener listener : localUserAudioLevelListeners) - listener.soundLevelChanged(evt); - } - } - - /** - * Determines whether this call is mute. - * - * @return true if an audio streams being sent to the call - * peers are currently muted; false, otherwise - */ - public boolean isMute() - { - return this.mute; - } - - /** - * Sets the mute property for this call. - * - * @param newMuteValue the new value of the mute property for this call - */ - public void setMute(boolean newMuteValue) - { - if (this.mute != newMuteValue) - { - this.mute = newMuteValue; - - Iterator peers = getCallPeers(); - while (peers.hasNext()) - { - CallPeerSipImpl peer = peers.next(); - peer.setMute(newMuteValue); - } - } - } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java index 4bb09afbd..e3ca9ea0e 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -32,7 +32,7 @@ * @author Emanuel Onica */ public class OperationSetBasicTelephonySipImpl - extends AbstractOperationSetBasicTelephony + extends AbstractOperationSetBasicTelephony implements MethodProcessor, OperationSetAdvancedTelephony, OperationSetSecureTelephony diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCall.java b/src/net/java/sip/communicator/service/protocol/AbstractCall.java index ac1bf7c65..b814338ba 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractCall.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractCall.java @@ -12,6 +12,12 @@ * Provides implementations for some of the methods in the Call * abstract class to facilitate implementations. * + * @param the peer extension class like for example CallPeerSipImpl + * or CallPeerJabberImpl + * @param the provider extension class like for example + * ProtocolProviderServiceSipImpl or + * ProtocolProviderServiceJabberImpl + * * @author Emil Ivov */ public abstract class AbstractCall the implementation specific provider class like for example + * ProtocolProviderServiceSipImpl. + * * @author Lubomir Marinov * @author Emil Ivov */ public abstract class AbstractOperationSetBasicTelephony - implements OperationSetBasicTelephony + + implements OperationSetBasicTelephony { /** * Our class logger diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java index 9a7540a30..31dcb0512 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java @@ -16,10 +16,14 @@ * supplementary operation set and if included in the service should be available * behind the basic telephony set. * + * @param the provider extension class like for example + * ProtocolProviderServiceSipImpl or + * ProtocolProviderServiceJabberImpl + * * @author Emil Ivov * @author Lubomir Marinov */ -public interface OperationSetBasicTelephony +public interface OperationSetBasicTelephony extends OperationSet { /** @@ -147,4 +151,12 @@ public void hangupCallPeer(CallPeer peer) * peers; otherwise, false */ public void setMute(Call call, boolean mute); + + /** + * Returns the protocol provider that this operation set belongs to. + * + * @return a reference to the ProtocolProviderService that created + * this operation set. + */ + public T getProtocolProvider(); } diff --git a/src/net/java/sip/communicator/service/protocol/media/AbstractMediaAwareCall.java b/src/net/java/sip/communicator/service/protocol/media/AbstractMediaAwareCall.java new file mode 100644 index 000000000..94cf80782 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/media/AbstractMediaAwareCall.java @@ -0,0 +1,457 @@ +/* + * SIP Communicator, 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.media; + +import java.util.*; + +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.neomedia.device.*; +import net.java.sip.communicator.service.neomedia.event.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; + +/** + * + * @param the peer extension class like for example CallPeerSipImpl + * or CallPeerJabberImpl + * @param the provider extension class like for example + * OperationSetBasicTelephonySipImpl or + * OperationSetBasicTelephonySipImpl + * @param the provider extension class like for example + * ProtocolProviderServiceSipImpl or + * ProtocolProviderServiceJabberImpl + * + * @author Emil Ivov + */ +public abstract class AbstractMediaAwareCall, + V extends ProtocolProviderService> + extends AbstractCall + implements CallPeerListener +{ + /** + * The MediaDevice which performs audio mixing for this + * Call and its CallPeers when the local peer represented + * by this Call is acting as a conference focus i.e. + * {@link #conferenceFocus} is true. + */ + private MediaDevice conferenceAudioMixer; + + /** + * The indicator which determines whether the local peer represented by this + * Call is acting as a conference focus and is thus specifying the + * "isfocus" parameter in the Contact headers of its outgoing SIP + * signaling. + */ + private boolean conferenceFocus = false; + + /** + * Our video streaming policy. + */ + protected boolean localVideoAllowed = false; + + /** + * A reference to the OperationSetBasicTelephonySipImpl that + * created us; + */ + private final U parentOpSet; + + /** + * Holds listeners registered for level changes in local audio. + */ + private final List localUserAudioLevelListeners + = new ArrayList(); + + /** + * The indicator which determines whether this Call is set + * to transmit "silence" instead of the actual media. + */ + private boolean mute = false; + + /** + * Device used in call will be chosen according to MediaUseCase. + */ + protected MediaUseCase mediaUseCase = MediaUseCase.ANY; + + /** + * The listener that would actually subscribe for level events from the + * media handler if there's at least one listener in + * localUserAudioLevelListeners. + */ + private final SimpleAudioLevelListener localAudioLevelDelegator + = new SimpleAudioLevelListener() + { + public void audioLevelChanged(int level) + { + fireLocalUserAudioLevelChangeEvent(level); + } + }; + + /** + * Crates a CallSipImpl instance belonging to sourceProvider and + * initiated by CallCreator. + * + * @param parentOpSet a reference to the operation set that's creating us + * and that we would be able to use for even dispatching. + */ + protected AbstractMediaAwareCall(U parentOpSet) + { + super(parentOpSet.getProtocolProvider()); + } + + /** + * Adds callPeer to the list of peers in this call. + * If the call peer is already included in the call, the method has + * no effect. + * + * @param callPeer the new CallPeer + */ + protected void addCallPeer(T callPeer) + { + if (getCallPeersVector().contains(callPeer)) + return; + + callPeer.addCallPeerListener(this); + + synchronized(localUserAudioLevelListeners) + { + // if there's someone listening for audio level events then they'd + // also like to know about the new peer. + if(getCallPeersVector().size() == 0) + { + callPeer.getMediaHandler().setLocalUserAudioLevelListener( + localAudioLevelDelegator); + } + } + + getCallPeersVector().add(callPeer); + fireCallPeerEvent(callPeer, CallPeerEvent.CALL_PEER_ADDED); + } + + /** + * Removes callPeer from the list of peers in this + * call. The method has no effect if there was no such peer in the + * call. + * + * @param callPeer the CallPeer leaving the call; + */ + private void removeCallPeer(T callPeer) + { + if (!getCallPeersVector().contains(callPeer)) + return; + + getCallPeersVector().remove(callPeer); + callPeer.removeCallPeerListener(this); + + synchronized(localUserAudioLevelListeners) + { + // remove sound level listeners from the peer + callPeer.getMediaHandler().setLocalUserAudioLevelListener(null); + } + + try + { + fireCallPeerEvent(callPeer, + CallPeerEvent.CALL_PEER_REMOVED); + } + finally + { + + /* + * The peer should loose its state once it has finished + * firing its events in order to allow the listeners to undo. + */ + callPeer.setCall(null); + } + + if (getCallPeersVector().size() == 0) + setCallState(CallState.CALL_ENDED); + } + + /** + * Dummy implementation of a method (inherited from CallPeerListener) + * that we don't need. + * + * @param evt unused. + */ + public void peerImageChanged(CallPeerChangeEvent evt) + { + //does not concern us + } + + /** + * Dummy implementation of a method (inherited from CallPeerListener) + * that we don't need. + * + * @param evt unused. + */ + public void peerAddressChanged(CallPeerChangeEvent evt) + { + //does not concern us + } + + /** + * Dummy implementation of a method (inherited from CallPeerListener) + * that we don't need. + * + * @param evt unused. + */ + public void peerTransportAddressChanged( + CallPeerChangeEvent evt) + { + //does not concern us + } + + /** + * Dummy implementation of a method (inherited from CallPeerListener) + * that we don't need. + * + * @param evt unused. + */ + public void peerDisplayNameChanged(CallPeerChangeEvent evt) + { + //does not concern us + } + + /** + * Verifies whether the call peer has entered a state. + * + * @param evt The CallPeerChangeEvent instance containing + * the source event as well as its previous and its new status. + */ + public void peerStateChanged(CallPeerChangeEvent evt) + { + CallPeerState newState = (CallPeerState) evt.getNewValue(); + + if (CallPeerState.DISCONNECTED.equals(newState) + || CallPeerState.FAILED.equals(newState)) + { + removeCallPeer((T) evt.getSourceCallPeer()); + } + else if (CallPeerState.CONNECTED.equals(newState) + || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(newState)) + { + setCallState(CallState.CALL_IN_PROGRESS); + } + else if (CallPeerState.REFERRED.equals(newState)) + { + setCallState(CallState.CALL_REFERRED); + } + } + + /** + * Returns a reference to the OperationSetBasicTelephony + * implementation instance that created this call. + * + * @return a reference to the OperationSetBasicTelephonySipImpl + * instance that created this call. + */ + public U getParentOperationSet() + { + return parentOpSet; + } + + /** + * Gets the indicator which determines whether the local peer represented by + * this Call is acting as a conference focus and thus should send + * the "isfocus" parameter in the Contact headers of its outgoing + * SIP signaling. + * + * @return true if the local peer represented by this Call + * is acting as a conference focus; otherwise, false + */ + public boolean isConferenceFocus() + { + return conferenceFocus; + } + + /** + * Sets the indicator which determines whether the local peer represented by + * this Call is acting as a conference focus and thus should send + * the "isfocus" parameter in the Contact headers of its outgoing + * SIP signaling + * + * @param conferenceFocus true if the local peer represented by + * this Call is to act as a conference focus; otherwise, + * false + */ + void setConferenceFocus(boolean conferenceFocus) + { + if (this.conferenceFocus != conferenceFocus) + { + this.conferenceFocus = conferenceFocus; + + /* + * If this Call switches from being a conference focus to not being + * one, dispose of the audio mixer used when it was a conference + * focus. + */ + if (!this.conferenceFocus) + conferenceAudioMixer = null; + + // fire that the focus property has changed + fireCallChangeEvent( + CallChangeEvent.CALL_FOCUS_CHANGE, + !this.conferenceFocus, this.conferenceFocus); + } + } + + /** + * Gets a MediaDevice which is capable of capture and/or playback + * of media of the specified MediaType, is the default choice of + * the user for a MediaDevice with the specified MediaType + * and is appropriate for the current state of this Call. + *

+ * For example, when the local peer represented by this Call + * instance is acting as a conference focus, the audio device must be a + * mixer. + *

+ * + * @param mediaType the MediaType in which the retrieved + * MediaDevice is to capture and/or play back media + * @return a MediaDevice which is capable of capture and/or + * playback of media of the specified mediaType, is the default + * choice of the user for a MediaDevice with the specified + * mediaType and is appropriate for the current state of this + * Call + */ + MediaDevice getDefaultDevice(MediaType mediaType) + { + MediaService mediaService = SipActivator.getMediaService(); + MediaDevice device = mediaService.getDefaultDevice(mediaType, + mediaUseCase); + + if (MediaType.AUDIO.equals(mediaType) && isConferenceFocus()) + { + if (conferenceAudioMixer == null) + { + if (device != null) + conferenceAudioMixer = mediaService.createMixer(device); + } + return conferenceAudioMixer; + } + return device; + } + + /** + * Adds a specific SoundLevelListener to the list of + * listeners interested in and notified about changes in local sound level + * related information. When the first listener is being registered the + * method also registers its single listener with the call peer media + * handlers so that it would receive level change events and delegate them + * to the listeners that have registered with us. + * + * @param l the SoundLevelListener to add + */ + public void addLocalUserSoundLevelListener(SoundLevelListener l) + { + synchronized(localUserAudioLevelListeners) + { + + if (localUserAudioLevelListeners.size() == 0) + { + //if this is the first listener that's being registered with + //us, we also need to register ourselves as an audio level + //listener with the media handler. we do this so that audio + //level would only be calculated if anyone is interested in + //receiving them. + Iterator cps = getCallPeers(); + while (cps.hasNext()) + { + CallPeerSipImpl callPeerSipImpl = cps.next(); + callPeerSipImpl.getMediaHandler() + .setLocalUserAudioLevelListener( + localAudioLevelDelegator); + } + } + + localUserAudioLevelListeners.add(l); + } + } + + /** + * Removes a specific SoundLevelListener from the list of + * listeners interested in and notified about changes in local sound level + * related information. If l is the last listener that we had here + * we are also going to unregister our own level event delegator in order + * to stop level calculations. + * + * @param l the SoundLevelListener to remove + */ + public void removeLocalUserSoundLevelListener(SoundLevelListener l) + { + synchronized(localUserAudioLevelListeners) + { + localUserAudioLevelListeners.add(l); + + if (localUserAudioLevelListeners.size() == 0) + { + //if this was the last listener that was registered with us then + //no long need to have a delegator registered with the call + //peer media handlers. We therefore remove it so that audio + //level calculations would be ceased. + Iterator cps = getCallPeers(); + while (cps.hasNext()) + { + T callPeerSipImpl = cps.next(); + callPeerSipImpl.getMediaHandler() + .setLocalUserAudioLevelListener(null); + } + } + } + } + + /** + * Notified by its very majesty the media service about changes in the + * audio level of the local user, this listener generates the corresponding + * events and delivers them to the listeners that have registered here. + * + * @param newLevel the new audio level of the local user. + */ + private void fireLocalUserAudioLevelChangeEvent(int newLevel) + { + SoundLevelChangeEvent evt + = new SoundLevelChangeEvent(this, newLevel); + + synchronized( localUserAudioLevelListeners ) + { + for(SoundLevelListener listener : localUserAudioLevelListeners) + listener.soundLevelChanged(evt); + } + } + + /** + * Determines whether this call is mute. + * + * @return true if an audio streams being sent to the call + * peers are currently muted; false, otherwise + */ + public boolean isMute() + { + return this.mute; + } + + /** + * Sets the mute property for this call. + * + * @param newMuteValue the new value of the mute property for this call + */ + public void setMute(boolean newMuteValue) + { + if (this.mute != newMuteValue) + { + this.mute = newMuteValue; + + Iterator peers = getCallPeers(); + while (peers.hasNext()) + { + T peer = peers.next(); + peer.setMute(newMuteValue); + } + } + } +}