/* * 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.awt.*; import java.beans.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; /** * Represents a default implementation of OperationSetVideoTelephony in * order to make it easier for implementers to provide complete solutions while * focusing on implementation-specific details. * * @param the implementation specific telephony operation set class like for * example OperationSetBasicTelephonySipImpl. * @param the implementation specific provider class like for example * ProtocolProviderServiceSipImpl. * @param the MediaAwareCall implementation like * CallSipImpl or CallJabberImpl. * @param the MediaAwarePeerCall implementation like * CallPeerSipImpl or CallPeerJabberImpl. * @author Emil Ivov * @author Sebastien Vincent */ public abstract class AbstractOperationSetVideoTelephony< T extends OperationSetBasicTelephony, U extends ProtocolProviderService, V extends MediaAwareCall, W extends MediaAwareCallPeer > implements OperationSetVideoTelephony { /** * The SIP ProtocolProviderService implementation which created * this instance and for which telephony conferencing services are being * provided by this instance. */ protected final U parentProvider; /** * The telephony-related functionality this extension builds upon. */ protected final T basicTelephony; /** * Initializes a new AbstractOperationSetVideoTelephony instance * which builds upon the telephony-related functionality of a specific * OperationSetBasicTelephony implementation. * * @param basicTelephony the OperationSetBasicTelephony * the new extension should build upon */ public AbstractOperationSetVideoTelephony(T basicTelephony) { this.basicTelephony = basicTelephony; this.parentProvider = basicTelephony.getProtocolProvider(); } /** * Delegates to the CallPeerMediaHandler of the specified * CallPeer because the video is provided by it. Because other * OperationSetVideoTelephony implementations may not provide their * video through the CallPeerMediaHandler, this implementation * promotes itself as the provider of the video by replacing the * CallPeerMediaHandler in the VideoEvents it fires. * * @param peer the CallPeer that we will be registering * listener with. * @param listener the VideoListener that we'd like to register. */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void addVideoListener(CallPeer peer, VideoListener listener) { if (listener == null) throw new NullPointerException("listener"); ((W)peer) .getMediaHandler() .addVideoListener( new InternalVideoListener(this, peer, listener)); } /** * Implements * {@link OperationSetVideoTelephony#createLocalVisualComponent(CallPeer, * VideoListener)}. * * @param peer the CallPeer that we are sending our local video to. * @param listener the VideoListener where we'd like to retrieve * the Component containing the local video. * @return the Component containing the local video. * @throws OperationFailedException if we fail extracting the local video. */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public Component createLocalVisualComponent( CallPeer peer, VideoListener listener) throws OperationFailedException { return ((W)peer).getMediaHandler().createLocalVisualComponent(); } /** * Implements * {@link OperationSetVideoTelephony#disposeLocalVisualComponent(CallPeer, * Component)}. * * @param peer the CallPeer whose local video component we'd like * to dispose of. * @param component the Component that we'll be disposing of. */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void disposeLocalVisualComponent(CallPeer peer, Component component) { ((W)peer).getMediaHandler().disposeLocalVisualComponent(); } /** * Gets the visual/video Component available in this telephony for * a specific CallPeer. * * @param peer the CallPeer whose video is to be retrieved * @return the visual/video Component available in this telephony * for the specified peer if any; otherwise, null */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public Component getVisualComponent(CallPeer peer) { return ((W)peer).getMediaHandler().getVisualComponent(); } /** * Delegates to the CallPeerMediaHandler of the specified * CallPeer because the video is provided by it. * * @param peer the CallPeer that we'd like to unregister our * VideoListener from. * @param listener the VideoListener that we'd like to unregister. */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void removeVideoListener(CallPeer peer, VideoListener listener) { if (listener != null) ((W)peer) .getMediaHandler() .removeVideoListener( new InternalVideoListener(this, peer, listener)); } /** * Implements OperationSetVideoTelephony#setLocalVideoAllowed(Call, * boolean). Modifies the local media setup to reflect the requested setting * for the streaming of the local video and then re-invites all * CallPeers to re-negotiate the modified media setup. * * @param call the call where we'd like to allow sending local video. * @param allowed true if local video transmission is allowed and * false otherwise. * * @throws OperationFailedException if video initialization fails. */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void setLocalVideoAllowed(Call call, boolean allowed) throws OperationFailedException { ((V)call).setLocalVideoAllowed(allowed, MediaUseCase.CALL); } /** * Determines whether the streaming of local video in a specific * Call is currently allowed. The setting does not reflect * the availability of actual video capture devices, it just expresses the * desire of the user to have the local video streamed in the case the * system is actually able to do so. * * @param call the Call whose video transmission properties we are * interested in. * * @return true if the streaming of local video for the specified * Call is allowed; otherwise, false */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public boolean isLocalVideoAllowed(Call call) { return ((V)call).isLocalVideoAllowed(MediaUseCase.CALL); } /** * Determines whether a specific Call is currently streaming the * local video (to a remote destination). * * @param call the Call whose video transmission we are interested * in. * * @return true if the specified Call is currently * streaming the local video (to a remote destination); otherwise, * false */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public boolean isLocalVideoStreaming(Call call) { return ((V)call).isLocalVideoStreaming(); } /** * Adds a specific PropertyChangeListener to the list of * listeners which get notified when the properties (e.g. * {@link #LOCAL_VIDEO_STREAMING}) associated with a specific * Call change their values. * * @param call the Call to start listening to the changes of * the property values of * @param listener the PropertyChangeListener to be notified * when the properties associated with the specified Call change * their values */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void addPropertyChangeListener( Call call, PropertyChangeListener listener) { ((V)call).addVideoPropertyChangeListener(listener); } /** * Removes a specific PropertyChangeListener from the list of * listeners which get notified when the properties (e.g. * {@link #LOCAL_VIDEO_STREAMING}) associated with a specific * Call change their values. * * @param call the Call to stop listening to the changes of the * property values of * @param listener the PropertyChangeListener to no longer be * notified when the properties associated with the specified Call * change their values */ @SuppressWarnings("unchecked") // work with MediaAware* in media package public void removePropertyChangeListener( Call call, PropertyChangeListener listener) { ((V)call).removeVideoPropertyChangeListener(listener); } /** * Get the MediaUseCase of a video telephony operation set. * * @return MediaUseCase.CALL */ public MediaUseCase getMediaUseCase() { return MediaUseCase.CALL; } /** * Represents a VideoListener which forwards notifications to a * specific delegate VideoListener and hides the original * VideoEvent sender from it by pretending the sender is a * specific OperationSetVideoTelephony. It's necessary in order * to hide from the VideoListeners the fact that the video of * the SIP protocol implementation is managed by CallSession. */ private static class InternalVideoListener implements VideoListener { /** * The VideoListener this implementation hides the original * VideoEvent source from. */ private final VideoListener delegate; /** * The CallPeer whose videos {@link #delegate} is * interested in. */ private final CallPeer peer; /** * The OperationSetVideoTelephony which is to be presented * as the source of the VideoEvents forwarded to * {@link #delegate}. */ private final OperationSetVideoTelephony telephony; /** * Initializes a new InternalVideoListener which is to * impersonate the sources of VideoEvents with a specific * OperationSetVideoTelephony for a specific * VideoListener interested in the videos of a specific * CallPeer. * * @param telephony the OperationSetVideoTelephony which is * to be stated as the source of the VideoEvent sent to the * specified delegate VideoListener * @param peer the CallPeer whose videos the specified delegate * VideoListener is interested in * @param delegate the VideoListener which shouldn't know * that the videos in the SIP protocol implementation is managed by the * CallSession and not by the specified telephony */ public InternalVideoListener( OperationSetVideoTelephony telephony, CallPeer peer, VideoListener delegate) { if (peer == null) throw new NullPointerException("peer cannot be null"); this.telephony = telephony; this.peer = peer; this.delegate = delegate; } /** * Compares two InternalVideoListeners and determines they are equal * if they impersonate the sources of VideoEvents with equal * OperationSetVideoTelephonies for equal delegate VideoListeners added * to equal CallPeer-s. * * @param other the object that we'd be compared to. * * @return true if the underlying peer, telephony operation set and * delegate are equal to those of the other instance. */ @Override public boolean equals(Object other) { if (other == this) return true; if ((other == null) || !other.getClass().equals(getClass())) return false; InternalVideoListener otherListener = (InternalVideoListener) other; return otherListener.telephony.equals(telephony) && otherListener.peer.equals(peer) && otherListener.delegate.equals(delegate); } /** * Returns a hashcode based on the hash codes of the wrapped telephony * operation set and VideoListener delegate. * * @return a hashcode based on the hash codes of the wrapped telephony * operation set and VideoListener delegate. */ @Override public int hashCode() { return (telephony.hashCode() << 16) + (delegate.hashCode() >> 16); } /** * Upon receiving a VideoEvent, sends to delegate a new VideoEvent of * the same type and with the same visual Component but with the source * of the event being set to #telephony. Thus the fact that the * CallSession is the original source is hidden from the clients of * OperationSetVideoTelephony. * * @param event the VideoEvent containing the visual component */ public void videoAdded(VideoEvent event) { VideoEvent delegateEvent = new VideoEvent( this, event.getType(), event.getVisualComponent(), event.getOrigin()); delegate.videoAdded(delegateEvent); if (delegateEvent.isConsumed()) event.consume(); } /** * Upon receiving a VideoEvent, sends to delegate a new VideoEvent of * the same type and with the same visual Component but with the source * of the event being set to #telephony. Thus the fact that the * CallSession is the original source is hidden from the clients of * OperationSetVideoTelephony. * * @param event the VideoEvent containing video details and * the event component. */ public void videoRemoved(VideoEvent event) { VideoEvent delegateEvent = new VideoEvent( this, event.getType(), event.getVisualComponent(), event.getOrigin()); delegate.videoRemoved(delegateEvent); if (delegateEvent.isConsumed()) event.consume(); } /** * Forwards a specific VideoEvent to the associated delegate. * * @param event the VideoEvent to be forwarded to the * associated delegate */ public void videoUpdate(VideoEvent event) { /* * TODO Since the specified event is directly fired, the sender is * the original one and not this. */ delegate.videoUpdate(event); } } }