- Finish the AbstractOperationSetVideoTelephony class to be as generic as possible.

- Add video support in XMPP/Jingle.
cusax-fix
Sebastien Vincent 16 years ago
parent b073675685
commit 81e1dc3da1

@ -620,7 +620,7 @@ public CreateCallThread(ProtocolProviderService protocolProvider,
public void run()
{
OperationSetBasicTelephony telephonyOpSet
OperationSetBasicTelephony<?> telephonyOpSet
= protocolProvider
.getOperationSet(OperationSetBasicTelephony.class);

@ -22,7 +22,7 @@
*/
public class CallJabberImpl extends MediaAwareCall<
CallPeerJabberImpl,
OperationSetBasicTelephony<ProtocolProviderServiceJabberImpl>,
OperationSetBasicTelephonyJabberImpl,
ProtocolProviderServiceJabberImpl>
{
/**
@ -56,7 +56,6 @@ protected CallJabberImpl(
parentOpSet.getActiveCallsRepository().addCall(this);
}
/**
* Creates a new call peer and sends a RINGING response.
*
@ -139,6 +138,22 @@ public CallPeerJabberImpl initiateSession(String calleeJID,
return callPeer;
}
/**
* Send a <tt>content-modify</tt> message for all current <tt>CallPeer</tt>
* to reflect possible video change in media setup.
*
* @param allowed if the local video is allowed or not
* @throws OperationFailedException if problem occurred during message
* generation or network problem
*/
public void modifyVideoContent(boolean allowed) throws OperationFailedException
{
for(CallPeerJabberImpl peer : getCallPeersVector())
{
peer.sendModifyVideoContent(allowed);
}
}
/**
* Determines if this call contains a peer whose corresponding session has
* the specified <tt>sid</tt>.

@ -11,6 +11,7 @@
import org.jivesoftware.smackx.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
@ -480,4 +481,142 @@ else if (info.getType() == SessionInfoType.unhold
reevalRemoteHoldStatus();
}
}
/**
* Send a <tt>content-modify</tt> to reflect change in video setup (start or
* stop).
*
* @param allowed if the local video is allowed or not
*/
public void sendModifyVideoContent(boolean allowed)
{
ContentPacketExtension ext = new ContentPacketExtension();
SendersEnum senders = getMediaHandler().getDirection("video");
/* adjust the senders attribute depending on current value and if we
* allowed or not local video streaming
*/
if(allowed)
{
if(senders != SendersEnum.none)
{
senders = SendersEnum.both;
}
else if(senders == SendersEnum.none && !isInitiator)
{
senders = SendersEnum.initiator;
}
else if(senders == SendersEnum.none && isInitiator)
{
senders = SendersEnum.responder;
}
}
else
{
if(senders == SendersEnum.both)
{
senders = !isInitiator ? SendersEnum.responder :
SendersEnum.initiator;
}
else
{
senders = SendersEnum.none;
}
}
ext.setSenders(senders);
ext.setCreator(isInitiator ? CreatorEnum.initiator :
CreatorEnum.responder);
ext.setName("video");
JingleIQ contentIQ = JinglePacketFactory
.createContentModify(getProtocolProvider().getOurJID(),
this.peerJID, getJingleSID(), ext);
getProtocolProvider().getConnection().sendPacket(contentIQ);
try
{
getMediaHandler().reinitContent("video", senders);
}
catch(Exception e)
{
logger.warn("Exception occurred when media reinitialization", e);
}
}
/**
* Processes the content-add {@link JingleIQ}.
*
* @param content The {@link JingleIQ} that contains content that remote
* peer wants to be added
*/
public void processContentAdd(JingleIQ content)
{
/* TODO */
}
/**
* Processes the content-accept {@link JingleIQ}.
*
* @param content The {@link JingleIQ} that contains content that remote
* peer has accepted
*/
public void processContentAccept(JingleIQ content)
{
/* TODO */
}
/**
* Processes the content-modify {@link JingleIQ}.
*
* @param content The {@link JingleIQ} that contains content that remote
* peer wants to be modified
*/
public void processContentModify(JingleIQ content)
{
ContentPacketExtension ext = content.getContentList().get(0);
SendersEnum senders = ext.getSenders();
try
{
getMediaHandler().reinitContent(ext.getName(), senders);
}
catch(Exception exc)
{
logger.info("Failed to process an incoming content-modify", exc);
//send an error response;
JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
"Error: " + exc.getMessage());
setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
getProtocolProvider().getConnection().sendPacket(errResp);
return;
}
}
/**
* Processes the content-remove {@link JingleIQ}.
*
* @param content The {@link JingleIQ} that contains content that remote
* peer wants to be removed
*/
public void processContentRemove(JingleIQ content)
{
/* TODO */
}
/**
* Processes the content-reject {@link JingleIQ}.
*
* @param content The {@link JingleIQ}
*/
public void processContentReject(JingleIQ content)
{
/* TODO */
}
}

@ -39,7 +39,6 @@ public class CallPeerMediaHandlerJabberImpl
*/
private final TransportManagerJabberImpl transportManager;
/**
* The current description of the streams that we have going toward the
* remote side. We use {@link LinkedHashMap}s to make sure that we preserve
@ -97,6 +96,18 @@ protected void throwOperationFailedException(String message, int errorCode,
message, errorCode, cause, logger);
}
/**
* Get the direction of a specific content (like audio or video).
*
* @param content content name
* @return the direction of the media
*/
public SendersEnum getDirection(String content)
{
ContentPacketExtension pkt = remoteContentMap.get(content);
return pkt.getSenders();
}
/**
* Creates if necessary, and configures the stream that this
* <tt>MediaHandler</tt> is using for the <tt>MediaType</tt> matching the
@ -391,12 +402,12 @@ public List<ContentPacketExtension> createContentList()
//now add the transport elements
getTransportManager().startCandidateHarvest(mediaDescs);
//XXX ideally we wouldn't wrapup that quickluy. we need to revisit this
//XXX ideally we wouldn't wrapup that quickly. we need to revisit this
return getTransportManager().wrapupHarvest();
}
/**
* Generates an SDP {@link ContentPacketExtension} for the specified
* Generates an Jingle {@link ContentPacketExtension} for the specified
* {@link MediaFormat} list, direction and RTP extensions taking account
* the local streaming preference for the corresponding media type.
*
@ -429,95 +440,148 @@ private ContentPacketExtension createContentForOffer(
}
/**
* Handles the specified <tt>answer</tt> by creating and initializing the
* corresponding <tt>MediaStream</tt>s.
* Reinitialize a media content such as video.
*
* @param answer the SDP <tt>SessionDescription</tt>.
* @param name name of the Jingle content
* @param senders media direction
* @throws OperationFailedException if we fail to handle <tt>content</tt>
* for reasons like failing to initialize media devices or streams.
* @throws IllegalArgumentException if there's a problem with the syntax or
* the semantics of <tt>content</tt>. Method is synchronized in order to
* avoid closing mediaHandler when we are currently in process of
* initializing, configuring and starting streams and anybody interested
* in this operation can synchronize to the mediaHandler instance to wait
* processing to stop (method setState in CallPeer).
*/
public void reinitContent(String name,
ContentPacketExtension.SendersEnum senders)
throws OperationFailedException,
IllegalArgumentException
{
ContentPacketExtension ext = remoteContentMap.get(name);
if(ext != null)
{
ext.setSenders(senders);
processContent(ext);
remoteContentMap.put(name, ext);
}
}
/**
* Process a <tt>ContentPacketExtension</tt> and initialize its
* corresponding <tt>MediaStream</tt>.
*
* @throws OperationFailedException if we fail to handle <tt>answer</tt> for
* reasons like failing to initialize media devices or streams.
* @param content a <tt>ContentPacketExtension</tt>
*
* @throws OperationFailedException if we fail to handle <tt>content</tt>
* for reasons like failing to initialize media devices or streams.
* @throws IllegalArgumentException if there's a problem with the syntax or
* the semantics of <tt>answer</tt>. Method is synchronized in order to
* the semantics of <tt>content</tt>. Method is synchronized in order to
* avoid closing mediaHandler when we are currently in process of
* initializing, configuring and starting streams and anybody interested
* in this operation can synchronize to the mediaHandler instance to wait
* processing to stop (method setState in CallPeer).
*/
public void processAnswer(List<ContentPacketExtension> answer)
private void processContent(ContentPacketExtension content)
throws OperationFailedException,
IllegalArgumentException
IllegalArgumentException
{
for ( ContentPacketExtension content : answer)
{
remoteContentMap.put(content.getName(), content);
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
RtpDescriptionPacketExtension description
= JingleUtils.getRtpDescription(content);
MediaType mediaType
= MediaType.parseString( description.getMedia() );
MediaType mediaType
= MediaType.parseString( description.getMedia() );
//stream target
MediaStreamTarget target
= JingleUtils.extractDefaultTarget(content);
//stream target
MediaStreamTarget target
= JingleUtils.extractDefaultTarget(content);
// no target port - try next media description
if(target.getDataAddress().getPort() == 0)
{
closeStream(mediaType);
continue;
}
// no target port - try next media description
if(target.getDataAddress().getPort() == 0)
{
closeStream(mediaType);
return;
}
List<MediaFormat> supportedFormats = JingleUtils.extractFormats(
description, getDynamicPayloadTypes());
List<MediaFormat> supportedFormats = JingleUtils.extractFormats(
description, getDynamicPayloadTypes());
MediaDevice dev = getDefaultDevice(mediaType);
MediaDevice dev = getDefaultDevice(mediaType);
if(dev == null)
{
closeStream(mediaType);
continue;
}
if(dev == null)
{
closeStream(mediaType);
return;
}
MediaDirection devDirection
= (dev == null) ? MediaDirection.INACTIVE : dev.getDirection();
MediaDirection devDirection
= (dev == null) ? MediaDirection.INACTIVE : dev.getDirection();
// Take the preference of the user with respect to streaming
// mediaType into account.
devDirection
= devDirection.and(getDirectionUserPreference(mediaType));
// Take the preference of the user with respect to streaming
// mediaType into account.
devDirection
= devDirection.and(getDirectionUserPreference(mediaType));
if (supportedFormats.isEmpty())
{
//remote party must have messed up our SDP. throw an exception.
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
"Remote party sent an invalid SDP answer.",
OperationFailedException.ILLEGAL_ARGUMENT, null, logger);
}
if (supportedFormats.isEmpty())
{
//remote party must have messed up our Jingle description.
//throw an exception.
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
"Remote party sent an invalid Jingle answer.",
OperationFailedException.ILLEGAL_ARGUMENT, null, logger);
}
StreamConnector connector
= getTransportManager().getStreamConnector(mediaType);
StreamConnector connector
= getTransportManager().getStreamConnector(mediaType);
//determine the direction that we need to announce.
MediaDirection remoteDirection
= JingleUtils.getDirection(content, getPeer().isInitiator());
//determine the direction that we need to announce.
MediaDirection remoteDirection
= JingleUtils.getDirection(content, getPeer().isInitiator());
MediaDirection direction
= devDirection.getDirectionForAnswer(remoteDirection);
MediaDirection direction
= devDirection.getDirectionForAnswer(remoteDirection);
// update the RTP extensions that we will be exchanging.
List<RTPExtension> remoteRTPExtensions
= JingleUtils.extractRTPExtensions(
description, getRtpExtensionsRegistry());
// update the RTP extensions that we will be exchanging.
List<RTPExtension> remoteRTPExtensions
= JingleUtils.extractRTPExtensions(
description, getRtpExtensionsRegistry());
List<RTPExtension> supportedExtensions
= getExtensionsForType(mediaType);
List<RTPExtension> supportedExtensions
= getExtensionsForType(mediaType);
List<RTPExtension> rtpExtensions = intersectRTPExtensions(
remoteRTPExtensions, supportedExtensions);
List<RTPExtension> rtpExtensions = intersectRTPExtensions(
remoteRTPExtensions, supportedExtensions);
// create the corresponding stream...
initStream(content.getName(), connector, dev,
supportedFormats.get(0), target, direction, rtpExtensions);
// create the corresponding stream...
initStream(content.getName(), connector, dev,
supportedFormats.get(0), target, direction, rtpExtensions);
}
/**
* Handles the specified <tt>answer</tt> by creating and initializing the
* corresponding <tt>MediaStream</tt>s.
*
* @param answer the Jingle answer
*
* @throws OperationFailedException if we fail to handle <tt>answer</tt> for
* reasons like failing to initialize media devices or streams.
* @throws IllegalArgumentException if there's a problem with the syntax or
* the semantics of <tt>answer</tt>. Method is synchronized in order to
* avoid closing mediaHandler when we are currently in process of
* initializing, configuring and starting streams and anybody interested
* in this operation can synchronize to the mediaHandler instance to wait
* processing to stop (method setState in CallPeer).
*/
public void processAnswer(List<ContentPacketExtension> answer)
throws OperationFailedException,
IllegalArgumentException
{
for ( ContentPacketExtension content : answer)
{
remoteContentMap.put(content.getName(), content);
processContent(content);
}
}

@ -111,7 +111,8 @@ else if ((evt.getNewState() == RegistrationState.UNREGISTERED))
public Call createCall(String callee)
throws OperationFailedException
{
return createOutgoingCall(callee);
CallJabberImpl call = new CallJabberImpl(this);
return createOutgoingCall(call, callee);
}
/**
@ -130,13 +131,15 @@ public Call createCall(String callee)
public Call createCall(Contact callee)
throws OperationFailedException
{
return createOutgoingCall(callee.getAddress());
CallJabberImpl call = new CallJabberImpl(this);
return createOutgoingCall(call, callee.getAddress());
}
/**
* Init and establish the specified call.
*
* @param call the <tt>CallJabberImpl</tt> that will be used
* to initiate the call
* @param calleeAddress the address of the callee that we'd like to connect
* with.
*
@ -149,12 +152,14 @@ public Call createCall(Contact callee)
* @throws OperationFailedException with the corresponding code if we fail
* to create the call.
*/
private CallJabberImpl createOutgoingCall(String calleeAddress)
public CallJabberImpl createOutgoingCall(CallJabberImpl call,
String calleeAddress)
throws OperationFailedException
{
if (logger.isInfoEnabled())
logger.info("creating outgoing call...");
if (protocolProvider.getConnection() == null) {
if (protocolProvider.getConnection() == null || call == null)
{
throw new OperationFailedException(
"Failed to create OutgoingJingleSession.\n"
+ "we don't have a valid XMPPConnection."
@ -192,6 +197,7 @@ private CallJabberImpl createOutgoingCall(String calleeAddress)
// check if the remote client supports telephony.
di = protocolProvider.getDiscoveryManager()
.discoverInfo(fullCalleeURI);
if (di.containsFeature(ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE))
{
@ -213,10 +219,7 @@ private CallJabberImpl createOutgoingCall(String calleeAddress)
logger.warn("could not retrieve info for " + fullCalleeURI, ex);
}
//create the actual jingle call
CallJabberImpl call = new CallJabberImpl(this);
// initiate call */
try
{
call.initiateSession(fullCalleeURI, di);
@ -314,8 +317,6 @@ public synchronized void hangupCallPeer(CallPeer peer)
peerJabberImpl.hangup();
}
/**
* Implements method <tt>answerCallPeer</tt>
* from <tt>OperationSetBasicTelephony</tt>.
@ -429,8 +430,6 @@ public void processPacket(Packet packet)
//ack if this is a session-initiate with rtp content or if we are
//the owners of this packet's sid
//first ack all "set" requests.
if(jingleIQ.getType() == IQ.Type.SET)
{
@ -515,6 +514,26 @@ else if (action == JingleAction.SESSION_INFO)
// change status.
callPeer.processSessionInfo(info);
}
else if (action == JingleAction.CONTENT_ACCEPT)
{
callPeer.processContentAccept(jingleIQ);
}
else if (action == JingleAction.CONTENT_ADD)
{
callPeer.processContentAdd(jingleIQ);
}
else if (action == JingleAction.CONTENT_MODIFY)
{
callPeer.processContentModify(jingleIQ);
}
else if (action == JingleAction.CONTENT_REJECT)
{
callPeer.processContentReject(jingleIQ);
}
else if (action == JingleAction.CONTENT_REMOVE)
{
callPeer.processContentRemove(jingleIQ);
}
}
/**

@ -6,10 +6,150 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
/**
* Implements <tt>OperationSetVideoTelephony</tt> in order to give access to
* video-specific functionality in the Jabber protocol implementation.
*
* @author Emil Ivov
*/
public class OperationSetVideoTelephonyJabberImpl
extends AbstractOperationSetVideoTelephony<
OperationSetBasicTelephonyJabberImpl,
ProtocolProviderServiceJabberImpl,
CallJabberImpl,
CallPeerJabberImpl>
{
/**
* The <tt>Logger</tt> used by the
* <tt>OperationSetTelephonyConferencingJabberImpl</tt> class and its
* instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(OperationSetVideoTelephonyJabberImpl.class);
/**
* Initializes a new <tt>OperationSetVideoTelephonyJabberImpl</tt> instance
* which builds upon the telephony-related functionality of a specific
* <tt>OperationSetBasicTelephonyJabberImpl</tt>.
*
* @param basicTelephony the <tt>OperationSetBasicTelephonyJabberImpl</tt>
* the new extension should build upon
*/
public OperationSetVideoTelephonyJabberImpl(
OperationSetBasicTelephonyJabberImpl basicTelephony)
{
super(basicTelephony);
}
/**
* 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 <tt>true</tt> if local video transmission is allowed and
* <tt>false</tt> otherwise.
*
* @throws OperationFailedException if video initialization fails.
*/
@Override
public void setLocalVideoAllowed(Call call, boolean allowed)
throws OperationFailedException
{
super.setLocalVideoAllowed(call, allowed);
((CallJabberImpl)call).modifyVideoContent(allowed);
}
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param uri the address of the callee that we should invite to a new
* call.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(String uri)
throws OperationFailedException
{
return createOutgoingVideoCall(uri);
}
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param callee the address of the callee that we should invite to a new
* call.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(Contact callee) throws OperationFailedException
{
return createOutgoingVideoCall(callee.getAddress());
}
/**
* Check if the remote part supports Jingle video.
*
* @param calleeAddress Contact address
* @return true if contact support Jingle video, false otherwise
*
* @exception OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
private Call createOutgoingVideoCall(String calleeAddress)
throws OperationFailedException
{
if (logger.isInfoEnabled())
logger.info("creating outgoing video call...");
if (parentProvider.getConnection() == null)
{
throw new OperationFailedException(
"Failed to create OutgoingJingleSession.\n"
+ "we don't have a valid XMPPConnection."
, OperationFailedException.INTERNAL_ERROR);
}
CallJabberImpl call = new CallJabberImpl(basicTelephony);
/* enable video */
call.setLocalVideoAllowed(true, getMediaUseCase());
return basicTelephony.createOutgoingCall(call, calleeAddress);
}
/**
* Indicates a user request to answer an incoming call with video enabled
* from the specified CallPeer.
*
* @param peer the call peer that we'd like to answer.
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation.
*/
public void answerVideoCallPeer(CallPeer peer)
throws OperationFailedException
{
CallPeerJabberImpl callPeer = (CallPeerJabberImpl) peer;
/* answer with video */
callPeer.getCall().setLocalVideoAllowed(true, getMediaUseCase());
callPeer.answer();
}
}

@ -934,9 +934,16 @@ protected void initialize(String screenname,
//initialize the telephony operation set
/* disabled until implementation is ready.
OperationSetBasicTelephonyJabberImpl basicTelephony =
new OperationSetBasicTelephonyJabberImpl(this);
addSupportedOperationSet(
OperationSetBasicTelephony.class,
new OperationSetBasicTelephonyJabberImpl(this));
basicTelephony);
// initialize video telephony OperationSet
addSupportedOperationSet(
OperationSetVideoTelephony.class,
new OperationSetVideoTelephonyJabberImpl(basicTelephony));
// Add Jingle features to supported features.
supportedFeatures.add(URN_XMPP_JINGLE);

@ -239,4 +239,186 @@ public static JingleIQ createSessionInitiate(
return sessionInitiate;
}
/**
* Creates a new {@link JingleIQ} with the <tt>content-add</tt> action.
*
* @param from our JID
* @param to the destination JID
* @param sid the ID of the Jingle session that this message will be
* terminating.
* @param contentList the content elements containing media and transport
* descriptions.
*
* @return the newly constructed {@link JingleIQ} <tt>content-add</tt>
* packet.
*/
public static JingleIQ createContentAdd(
String from,
String to,
String sid,
List<ContentPacketExtension> contentList)
{
JingleIQ contentAdd = new JingleIQ();
contentAdd.setTo(to);
contentAdd.setFrom(from);
contentAdd.setInitiator(from);
contentAdd.setType(IQ.Type.SET);
contentAdd.setSID(sid);
contentAdd.setAction(JingleAction.CONTENT_ADD);
for(ContentPacketExtension content : contentList)
{
contentAdd.addContent(content);
}
return contentAdd;
}
/**
* Creates a new {@link JingleIQ} with the <tt>content-accept</tt> action.
*
* @param from our JID
* @param to the destination JID
* @param sid the ID of the Jingle session that this message will be
* terminating.
* @param contentList the content elements containing media and transport
* descriptions.
*
* @return the newly constructed {@link JingleIQ} <tt>content-accept</tt>
* packet.
*/
public static JingleIQ createContentAccept(
String from,
String to,
String sid,
List<ContentPacketExtension> contentList)
{
JingleIQ contentAccept = new JingleIQ();
contentAccept.setTo(to);
contentAccept.setFrom(from);
contentAccept.setInitiator(from);
contentAccept.setType(IQ.Type.SET);
contentAccept.setSID(sid);
contentAccept.setAction(JingleAction.CONTENT_ACCEPT);
for(ContentPacketExtension content : contentList)
{
contentAccept.addContent(content);
}
return contentAccept;
}
/**
* Creates a new {@link JingleIQ} with the <tt>content-reject</tt> action.
*
* @param from our JID
* @param to the destination JID
* @param sid the ID of the Jingle session that this message will be
* terminating.
* @param contentList the content elements containing media and transport
* descriptions.
*
* @return the newly constructed {@link JingleIQ} <tt>content-reject</tt>
* packet.
*/
public static JingleIQ createContentReject(
String from,
String to,
String sid,
List<ContentPacketExtension> contentList)
{
JingleIQ contentReject = new JingleIQ();
contentReject.setTo(to);
contentReject.setFrom(from);
contentReject.setInitiator(from);
contentReject.setType(IQ.Type.SET);
contentReject.setSID(sid);
contentReject.setAction(JingleAction.CONTENT_REJECT);
for(ContentPacketExtension content : contentList)
{
contentReject.addContent(content);
}
return contentReject;
}
/**
* Creates a new {@link JingleIQ} with the <tt>content-modify</tt> action.
*
* @param from our JID
* @param to the destination JID
* @param sid the ID of the Jingle session that this message will be
* terminating.
* @param content the content element containing media and transport
* description.
*
* @return the newly constructed {@link JingleIQ} <tt>content-modify</tt>
* packet.
*/
public static JingleIQ createContentModify(
String from,
String to,
String sid,
ContentPacketExtension content)
{
JingleIQ contentModify = new JingleIQ();
contentModify.setTo(to);
contentModify.setFrom(from);
contentModify.setInitiator(from);
contentModify.setType(IQ.Type.SET);
contentModify.setSID(sid);
contentModify.setAction(JingleAction.CONTENT_MODIFY);
contentModify.addContent(content);
return contentModify;
}
/**
* Creates a new {@link JingleIQ} with the <tt>content-remove</tt> action.
*
* @param from our JID
* @param to the destination JID
* @param sid the ID of the Jingle session that this message will be
* terminating.
* @param contentList the content elements containing media and transport
* descriptions.
*
* @return the newly constructed {@link JingleIQ} <tt>content-remove</tt>
* packet.
*/
public static JingleIQ createContentRemove(
String from,
String to,
String sid,
List<ContentPacketExtension> contentList)
{
JingleIQ contentRemove = new JingleIQ();
contentRemove.setTo(to);
contentRemove.setFrom(from);
contentRemove.setInitiator(from);
contentRemove.setType(IQ.Type.SET);
contentRemove.setSID(sid);
contentRemove.setAction(JingleAction.CONTENT_REMOVE);
for(ContentPacketExtension content : contentList)
{
contentRemove.addContent(content);
}
return contentRemove;
}
}

@ -24,17 +24,13 @@ public class OperationSetDesktopStreamingSipImpl
* which builds upon the telephony-related functionality of a specific
* <tt>OperationSetBasicTelephonySipImpl</tt>.
*
* @param parentProvider the SIP <tt>ProtocolProviderService</tt>
* implementation which has requested the creation of the new instance and
* for which the new instance is to provide video telephony
* @param basicTelephony the <tt>OperationSetBasicTelephonySipImpl</tt>
* the new extension should build upon
*/
public OperationSetDesktopStreamingSipImpl(
ProtocolProviderServiceSipImpl parentProvider,
OperationSetBasicTelephonySipImpl basicTelephony)
{
super(parentProvider, basicTelephony);
super(basicTelephony);
}
/**

@ -6,16 +6,13 @@
*/
package net.java.sip.communicator.impl.protocol.sip;
import java.awt.*;
import java.beans.*;
import java.text.*;
import javax.sip.address.Address;
import net.java.sip.communicator.service.neomedia.MediaUseCase;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
/**
* Implements <tt>OperationSetVideoTelephony</tt> in order to give access to
@ -33,7 +30,11 @@
* @author Lubomir Marinov
*/
public class OperationSetVideoTelephonySipImpl
implements OperationSetVideoTelephony
extends AbstractOperationSetVideoTelephony<
OperationSetBasicTelephonySipImpl,
ProtocolProviderServiceSipImpl,
CallSipImpl,
CallPeerSipImpl>
{
/**
* The <tt>Logger</tt> used by the
@ -43,123 +44,18 @@ public class OperationSetVideoTelephonySipImpl
private static final Logger logger
= Logger.getLogger(OperationSetVideoTelephonySipImpl.class);
/**
* The SIP <tt>ProtocolProviderService</tt> implementation which created
* this instance and for which telephony conferencing services are being
* provided by this instance.
*/
private final ProtocolProviderServiceSipImpl parentProvider;
/**
* The telephony-related functionality this extension builds upon.
*/
private final OperationSetBasicTelephonySipImpl basicTelephony;
/**
* Initializes a new <tt>OperationSetVideoTelephonySipImpl</tt> instance
* which builds upon the telephony-related functionality of a specific
* <tt>OperationSetBasicTelephonySipImpl</tt>.
*
* @param parentProvider the SIP <tt>ProtocolProviderService</tt>
* implementation which has requested the creation of the new instance and
* for which the new instance is to provide video telephony
* @param basicTelephony the <tt>OperationSetBasicTelephonySipImpl</tt>
* the new extension should build upon
*/
public OperationSetVideoTelephonySipImpl(
ProtocolProviderServiceSipImpl parentProvider,
OperationSetBasicTelephonySipImpl basicTelephony)
{
this.basicTelephony = basicTelephony;
this.parentProvider = parentProvider;
}
/**
* Delegates to the <tt>CallPeerMediaHandler</tt> of the specified
* <tt>CallPeer</tt> because the video is provided by it. Because other
* <tt>OperationSetVideoTelephony</tt> implementations may not provide their
* video through the <tt>CallPeerMediaHandler</tt>, this implementation
* promotes itself as the provider of the video by replacing the
* <tt>CallPeerMediaHandler</tt> in the <tt>VideoEvents</tt> it fires.
*
* @param peer the <tt>CallPeer</tt> that we will be registering
* <tt>listener</tt> with.
* @param listener the <tt>VideoListener</tt> that we'd like to register.
*/
public void addVideoListener(CallPeer peer, VideoListener listener)
{
if (listener == null)
throw new NullPointerException("listener");
((CallPeerSipImpl) peer)
.getMediaHandler()
.addVideoListener(
new InternalVideoListener(this, peer, listener));
}
/**
* Implements
* {@link OperationSetVideoTelephony#createLocalVisualComponent(CallPeer,
* VideoListener)}.
*
* @param peer the <tt>CallPeer</tt> that we are sending our local video to.
* @param listener the <tt>VideoListener</tt> where we'd like to retrieve
* the <tt>Component</tt> containing the local video.
* @return the <tt>Component</tt> containing the local video.
* @throws OperationFailedException if we fail extracting the local video.
*/
public Component createLocalVisualComponent(
CallPeer peer,
VideoListener listener)
throws OperationFailedException
{
CallPeerMediaHandlerSipImpl mediaHandler = ((CallPeerSipImpl) peer).getMediaHandler();
return mediaHandler.createLocalVisualComponent();
}
/**
* Implements
* {@link OperationSetVideoTelephony#disposeLocalVisualComponent(CallPeer,
* Component)}.
*
* @param peer the <tt>CallPeer</tt> whose local video component we'd like
* to dispose of.
* @param component the <tt>Component</tt> that we'll be disposing of.
*/
public void disposeLocalVisualComponent(CallPeer peer, Component component)
{
CallPeerMediaHandlerSipImpl mediaHandler = ((CallPeerSipImpl) peer).getMediaHandler();
mediaHandler.disposeLocalVisualComponent();
}
/**
* Gets the visual/video <tt>Component</tt> available in this telephony for
* a specific <tt>CallPeer</tt>.
*
* @param peer the <tt>CallPeer</tt> whose video is to be retrieved
* @return the visual/video <tt>Component</tt> available in this telephony
* for the specified <tt>peer</tt> if any; otherwise, <tt>null</tt>
*/
public Component getVisualComponent(CallPeer peer)
{
return ((CallPeerSipImpl) peer).getMediaHandler().getVisualComponent();
}
/**
* Delegates to the <tt>CallPeerMediaHandler</tt> of the specified
* <tt>CallPeer</tt> because the video is provided by it.
*
* @param peer the <tt>CallPeer</tt> that we'd like to unregister our
* <tt>VideoListener</tt> from.
* @param listener the <tt>VideoListener</tt> that we'd like to unregister.
*/
public void removeVideoListener(CallPeer peer, VideoListener listener)
{
if (listener != null)
((CallPeerSipImpl) peer)
.getMediaHandler()
.removeVideoListener(
new InternalVideoListener(this, peer, listener));
super(basicTelephony);
}
/**
@ -168,102 +64,22 @@ public void removeVideoListener(CallPeer peer, VideoListener listener)
* 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 call the call where we'd like to allow sending local video.
* @param allowed <tt>true</tt> if local video transmission is allowed and
* <tt>false</tt> otherwise.
*
* @throws OperationFailedException if video initialization fails.
*/
@Override
public void setLocalVideoAllowed(Call call, boolean allowed)
throws OperationFailedException
{
((CallSipImpl)call).setLocalVideoAllowed(allowed, MediaUseCase.CALL);
super.setLocalVideoAllowed(call, allowed);
/* reinvite all peers */
((CallSipImpl)call).reInvite();
}
/**
* Determines whether the streaming of local video in a specific
* <tt>Call</tt> 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 <tt>Call</tt> whose video transmission properties we are
* interested in.
*
* @return <tt>true</tt> if the streaming of local video for the specified
* <tt>Call</tt> is allowed; otherwise, <tt>false</tt>
*/
public boolean isLocalVideoAllowed(Call call)
{
return ((CallSipImpl) call).isLocalVideoAllowed();
}
/**
* Determines whether a specific <tt>Call</tt> is currently streaming the
* local video (to a remote destination).
*
* @param call the <tt>Call</tt> whose video transmission we are interested
* in.
*
* @return <tt>true</tt> if the specified <tt>Call</tt> is currently
* streaming the local video (to a remote destination); otherwise,
* <tt>false</tt>
*/
public boolean isLocalVideoStreaming(Call call)
{
return ((CallSipImpl) call).isLocalVideoStreaming();
}
/**
* Adds a specific <tt>PropertyChangeListener</tt> to the list of
* listeners which get notified when the properties (e.g.
* {@link #LOCAL_VIDEO_STREAMING}) associated with a specific
* <tt>Call</tt> change their values.
*
* @param call the <tt>Call</tt> to start listening to the changes of
* the property values of
* @param listener the <tt>PropertyChangeListener</tt> to be notified
* when the properties associated with the specified <tt>Call</tt> change
* their values
*/
public void addPropertyChangeListener(
Call call,
PropertyChangeListener listener)
{
((CallSipImpl) call).addVideoPropertyChangeListener(listener);
}
/**
* Removes a specific <tt>PropertyChangeListener</tt> from the list of
* listeners which get notified when the properties (e.g.
* {@link #LOCAL_VIDEO_STREAMING}) associated with a specific
* <tt>Call</tt> change their values.
*
* @param call the <tt>Call</tt> to stop listening to the changes of the
* property values of
* @param listener the <tt>PropertyChangeListener</tt> to no longer be
* notified when the properties associated with the specified <tt>Call</tt>
* change their values
*/
public void removePropertyChangeListener(
Call call,
PropertyChangeListener listener)
{
((CallSipImpl) call).removeVideoPropertyChangeListener(listener);
}
/**
* Get the <tt>MediaUseCase</tt> of a video telephony operation set.
*
* @return <tt>MediaUseCase.CALL</tt>
*/
public MediaUseCase getMediaUseCase()
{
return MediaUseCase.CALL;
}
/**
* Create a new video call and invite the specified CallPeer to it.
*
@ -334,7 +150,8 @@ public Call createVideoCall(Contact callee) throws OperationFailedException
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation.
*/
public void answerVideoCallPeer(CallPeer peer) throws OperationFailedException
public void answerVideoCallPeer(CallPeer peer)
throws OperationFailedException
{
CallPeerSipImpl callPeer = (CallPeerSipImpl) peer;
@ -342,166 +159,4 @@ public void answerVideoCallPeer(CallPeer peer) throws OperationFailedException
callPeer.getCall().setLocalVideoAllowed(true, getMediaUseCase());
callPeer.answer();
}
/**
* Represents a <tt>VideoListener</tt> which forwards notifications to a
* specific delegate <tt>VideoListener</tt> and hides the original
* <tt>VideoEvent</tt> sender from it by pretending the sender is a
* specific <tt>OperationSetVideoTelephony</tt>. It's necessary in order
* to hide from the <tt>VideoListener</tt>s the fact that the video of
* the SIP protocol implementation is managed by <tt>CallSession</tt>.
*/
private static class InternalVideoListener
implements VideoListener
{
/**
* The <tt>VideoListener</tt> this implementation hides the original
* <tt>VideoEvent</tt> source from.
*/
private final VideoListener delegate;
/**
* The <tt>CallPeer</tt> whose videos {@link #delegate} is
* interested in.
*/
private final CallPeer peer;
/**
* The <tt>OperationSetVideoTelephony</tt> which is to be presented
* as the source of the <tt>VideoEvents</tt> forwarded to
* {@link #delegate}.
*/
private final OperationSetVideoTelephony telephony;
/**
* Initializes a new <tt>InternalVideoListener</tt> which is to
* impersonate the sources of <tt>VideoEvents</tt> with a specific
* <tt>OperationSetVideoTelephony</tt> for a specific
* <tt>VideoListener</tt> interested in the videos of a specific
* <tt>CallPeer</tt>.
*
* @param telephony the <tt>OperationSetVideoTelephony</tt> which is
* to be stated as the source of the <tt>VideoEvent</tt> sent to the
* specified delegate <tt>VideoListener</tt>
* @param peer the <tt>CallPeer</tt> whose videos the specified delegate
* <tt>VideoListener</tt> is interested in
* @param delegate the <tt>VideoListener</tt> which shouldn't know
* that the videos in the SIP protocol implementation is managed by the
* <tt>CallSession</tt> and not by the specified <tt>telephony</tt>
*/
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 <tt>other</tt> 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 <tt>VideoListener</tt> delegate.
*
* @return a hashcode based on the hash codes of the wrapped telephony
* operation set and <tt>VideoListener</tt> 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 <tt>VideoEvent</tt> 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 <tt>VideoEvent</tt> 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 <tt>VideoEvent</tt> to the associated delegate.
*
* @param event the <tt>VideoEvent</tt> 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);
}
}
}

@ -321,7 +321,7 @@ public void fireRegistrationStateChanged(RegistrationState oldState,
String user;
String password;
if (useSipCredetials)
{
{
user = accountID.getAccountPropertyString(
ProtocolProviderFactory.USER_ID);
password = SipActivator.getProtocolProviderFactory().
@ -599,13 +599,13 @@ protected void initialize(String sipAddress,
addSupportedOperationSet(
OperationSetVideoTelephony.class,
new OperationSetVideoTelephonySipImpl(
this, opSetBasicTelephonySipImpl));
opSetBasicTelephonySipImpl));
// OperationSetDesktopStreaming
addSupportedOperationSet(
OperationSetDesktopStreaming.class,
new OperationSetDesktopStreamingSipImpl(
this, opSetBasicTelephonySipImpl));
opSetBasicTelephonySipImpl));
// init DTMF (from JM Heitz)
addSupportedOperationSet(

@ -6,9 +6,12 @@
*/
package net.java.sip.communicator.service.protocol.media;
import net.java.sip.communicator.service.protocol.*;
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 <tt>OperationSetVideoTelephony</tt> in
@ -19,12 +22,18 @@
* example <tt>OperationSetBasicTelephonySipImpl</tt>.
* @param <U> the implementation specific provider class like for example
* <tt>ProtocolProviderServiceSipImpl</tt>.
*
* @param <V> the <tt>MediaAwareCall</tt> implementation like
* <tt>CallSipImpl</tt> or <tt>CallJabberImpl</tt>.
* @param <W> the <tt>MediaAwarePeerCall</tt> implementation like
* <tt>CallPeerSipImpl</tt> or <tt>CallPeerJabberImpl</tt>.
* @author Emil Ivov
* @author Sebastien Vincent
*/
public abstract class AbstractOperationSetVideoTelephony<
T extends OperationSetBasicTelephony<U>,
U extends ProtocolProviderService>
U extends ProtocolProviderService,
V extends MediaAwareCall<W, T, U>,
W extends MediaAwareCallPeer<V, ?, U> >
implements OperationSetVideoTelephony
{
/**
@ -32,12 +41,12 @@ public abstract class AbstractOperationSetVideoTelephony<
* this instance and for which telephony conferencing services are being
* provided by this instance.
*/
private final U parentProvider;
protected final U parentProvider;
/**
* The telephony-related functionality this extension builds upon.
*/
private final T basicTelephony;
protected final T basicTelephony;
/**
* Initializes a new <tt>AbstractOperationSetVideoTelephony</tt> instance
@ -53,5 +62,361 @@ public AbstractOperationSetVideoTelephony(T basicTelephony)
this.parentProvider = basicTelephony.getProtocolProvider();
}
/**
* Delegates to the <tt>CallPeerMediaHandler</tt> of the specified
* <tt>CallPeer</tt> because the video is provided by it. Because other
* <tt>OperationSetVideoTelephony</tt> implementations may not provide their
* video through the <tt>CallPeerMediaHandler</tt>, this implementation
* promotes itself as the provider of the video by replacing the
* <tt>CallPeerMediaHandler</tt> in the <tt>VideoEvents</tt> it fires.
*
* @param peer the <tt>CallPeer</tt> that we will be registering
* <tt>listener</tt> with.
* @param listener the <tt>VideoListener</tt> 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 <tt>CallPeer</tt> that we are sending our local video to.
* @param listener the <tt>VideoListener</tt> where we'd like to retrieve
* the <tt>Component</tt> containing the local video.
* @return the <tt>Component</tt> 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 <tt>CallPeer</tt> whose local video component we'd like
* to dispose of.
* @param component the <tt>Component</tt> 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 <tt>Component</tt> available in this telephony for
* a specific <tt>CallPeer</tt>.
*
* @param peer the <tt>CallPeer</tt> whose video is to be retrieved
* @return the visual/video <tt>Component</tt> available in this telephony
* for the specified <tt>peer</tt> if any; otherwise, <tt>null</tt>
*/
@SuppressWarnings("unchecked") // work with MediaAware* in media package
public Component getVisualComponent(CallPeer peer)
{
return ((W)peer).getMediaHandler().getVisualComponent();
}
/**
* Delegates to the <tt>CallPeerMediaHandler</tt> of the specified
* <tt>CallPeer</tt> because the video is provided by it.
*
* @param peer the <tt>CallPeer</tt> that we'd like to unregister our
* <tt>VideoListener</tt> from.
* @param listener the <tt>VideoListener</tt> 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 <tt>true</tt> if local video transmission is allowed and
* <tt>false</tt> 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
* <tt>Call</tt> 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 <tt>Call</tt> whose video transmission properties we are
* interested in.
*
* @return <tt>true</tt> if the streaming of local video for the specified
* <tt>Call</tt> is allowed; otherwise, <tt>false</tt>
*/
@SuppressWarnings("unchecked") // work with MediaAware* in media package
public boolean isLocalVideoAllowed(Call call)
{
return ((V)call).isLocalVideoAllowed();
}
/**
* Determines whether a specific <tt>Call</tt> is currently streaming the
* local video (to a remote destination).
*
* @param call the <tt>Call</tt> whose video transmission we are interested
* in.
*
* @return <tt>true</tt> if the specified <tt>Call</tt> is currently
* streaming the local video (to a remote destination); otherwise,
* <tt>false</tt>
*/
@SuppressWarnings("unchecked") // work with MediaAware* in media package
public boolean isLocalVideoStreaming(Call call)
{
return ((V)call).isLocalVideoStreaming();
}
/**
* Adds a specific <tt>PropertyChangeListener</tt> to the list of
* listeners which get notified when the properties (e.g.
* {@link #LOCAL_VIDEO_STREAMING}) associated with a specific
* <tt>Call</tt> change their values.
*
* @param call the <tt>Call</tt> to start listening to the changes of
* the property values of
* @param listener the <tt>PropertyChangeListener</tt> to be notified
* when the properties associated with the specified <tt>Call</tt> 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 <tt>PropertyChangeListener</tt> from the list of
* listeners which get notified when the properties (e.g.
* {@link #LOCAL_VIDEO_STREAMING}) associated with a specific
* <tt>Call</tt> change their values.
*
* @param call the <tt>Call</tt> to stop listening to the changes of the
* property values of
* @param listener the <tt>PropertyChangeListener</tt> to no longer be
* notified when the properties associated with the specified <tt>Call</tt>
* change their values
*/
@SuppressWarnings("unchecked") // work with MediaAware* in media package
public void removePropertyChangeListener(
Call call,
PropertyChangeListener listener)
{
((V)call).removeVideoPropertyChangeListener(listener);
}
/**
* Get the <tt>MediaUseCase</tt> of a video telephony operation set.
*
* @return <tt>MediaUseCase.CALL</tt>
*/
public MediaUseCase getMediaUseCase()
{
return MediaUseCase.CALL;
}
/**
* Represents a <tt>VideoListener</tt> which forwards notifications to a
* specific delegate <tt>VideoListener</tt> and hides the original
* <tt>VideoEvent</tt> sender from it by pretending the sender is a
* specific <tt>OperationSetVideoTelephony</tt>. It's necessary in order
* to hide from the <tt>VideoListener</tt>s the fact that the video of
* the SIP protocol implementation is managed by <tt>CallSession</tt>.
*/
private static class InternalVideoListener
implements VideoListener
{
/**
* The <tt>VideoListener</tt> this implementation hides the original
* <tt>VideoEvent</tt> source from.
*/
private final VideoListener delegate;
/**
* The <tt>CallPeer</tt> whose videos {@link #delegate} is
* interested in.
*/
private final CallPeer peer;
/**
* The <tt>OperationSetVideoTelephony</tt> which is to be presented
* as the source of the <tt>VideoEvents</tt> forwarded to
* {@link #delegate}.
*/
private final OperationSetVideoTelephony telephony;
/**
* Initializes a new <tt>InternalVideoListener</tt> which is to
* impersonate the sources of <tt>VideoEvents</tt> with a specific
* <tt>OperationSetVideoTelephony</tt> for a specific
* <tt>VideoListener</tt> interested in the videos of a specific
* <tt>CallPeer</tt>.
*
* @param telephony the <tt>OperationSetVideoTelephony</tt> which is
* to be stated as the source of the <tt>VideoEvent</tt> sent to the
* specified delegate <tt>VideoListener</tt>
* @param peer the <tt>CallPeer</tt> whose videos the specified delegate
* <tt>VideoListener</tt> is interested in
* @param delegate the <tt>VideoListener</tt> which shouldn't know
* that the videos in the SIP protocol implementation is managed by the
* <tt>CallSession</tt> and not by the specified <tt>telephony</tt>
*/
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 <tt>other</tt> 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 <tt>VideoListener</tt> delegate.
*
* @return a hashcode based on the hash codes of the wrapped telephony
* operation set and <tt>VideoListener</tt> 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 <tt>VideoEvent</tt> 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 <tt>VideoEvent</tt> 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 <tt>VideoEvent</tt> to the associated delegate.
*
* @param event the <tt>VideoEvent</tt> 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);
}
}
}

Loading…
Cancel
Save