diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
index f81a665e6..7d7dc0332 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
@@ -80,7 +80,7 @@ public class CallPeerMediaHandler
*/
private static int nextMediaPortToTry = minMediaPort;
- private boolean onHold = false;
+ private boolean locallyOnHold = false;
/**
* The RTP/RTCP socket couple that this media handler should use to send
@@ -197,25 +197,26 @@ public boolean isMute()
}
/**
- * Specifies that this handler's MediaStreams should be on hold
- * so that this would be taken into account and the streams put in
- * sendonly or inactive mode when the next update offer is generated.
- * Note that the stream has no immediate effect and would only affect
- * the flows after an update description is regenerated.
+ * Puts all MediaStreams in this handler locally on or off hold
+ * (according to the value of locallyOnHold). This would also be
+ * taken into account when the next update offer is generated.
*
- * @param onHold true if we are to make our audio stream start
- * transmitting silence and false if we are to end the transmission
- * of silence and use our stream's MediaDevice again.
+ * @param locallyOnHold true if we are to make our audio stream
+ * stop transmitting and false if we are to start transmitting
+ * again.
*/
- public void setOnHold(boolean onHold)
+ public void setLocallyOnHold(boolean locallyOnHold)
{
- this.onHold = onHold;
+ this.locallyOnHold = locallyOnHold;
- if(onHold)
+ if(locallyOnHold)
{
if(audioStream != null)
+ {
audioStream.setDirection(audioStream.getDirection()
.and(MediaDirection.SENDONLY));
+ audioStream.setMute(true);
+ }
if(videoStream != null)
videoStream.setDirection(videoStream.getDirection()
.and(MediaDirection.SENDONLY));
@@ -223,25 +224,67 @@ public void setOnHold(boolean onHold)
else
{
if(audioStream != null)
+ {
audioStream.setDirection(audioStream.getDirection()
.or(MediaDirection.SENDONLY));
+ audioStream.setMute(false);
+ }
if(videoStream != null)
+ {
videoStream.setDirection(videoStream.getDirection()
.or(MediaDirection.SENDONLY));
+ //videoStream.setMute(true);
+ }
+ }
+ }
+ /**
+ * Closes and null-ifies all streams and connectors and readies this media
+ * handler for garbage collection (or reuse).
+ */
+ public void close()
+ {
+ if (this.audioStream != null)
+ {
+ audioStream.close();
+ audioStream = null;
+ }
+
+ if (this.videoStream != null)
+ {
+ videoStream.close();
+ videoStream = null;
}
+
+ if (this.audioStreamConnector != null)
+ {
+ audioStreamConnector.getDataSocket().close();
+ audioStreamConnector.getControlSocket().close();
+ audioStreamConnector = null;
+ }
+
+ if (this.videoStreamConnector != null)
+ {
+ videoStreamConnector.getDataSocket().close();
+ videoStreamConnector.getControlSocket().close();
+ videoStreamConnector = null;
+ }
+
+ locallyOnHold = false;
}
/**
- * Determines whether this handler's streams should be placed on hold during
- * the next session update.
+ * Determines whether this handler's streams have been placed on hold.
*
- * @return true if this handler's streams should be placed on hold
- * next time we update this session and false otherwise.
+ * @return true if this handler's streams have been placed on hold
+ * and false otherwise.
*/
- public boolean isOnHold()
+ public boolean isLocallyOnHold()
{
- return onHold;
+ return locallyOnHold;
+ //no need to actually check stream directions because we only update
+ //them through the setLocallyOnHold() method so if the value of the
+ //locallyOnHold field has changed, so have stream directions.
}
/**
@@ -312,7 +355,7 @@ private Vector createMediaDescriptions()
MediaDirection audioDirection
= aDev.getDirection().and(audioDirectionUserPreference);
- if(onHold)
+ if(locallyOnHold)
audioDirection = audioDirection.and(MediaDirection.SENDONLY);
if(audioDirection != MediaDirection.INACTIVE);
@@ -331,7 +374,7 @@ private Vector createMediaDescriptions()
MediaDirection videoDirection
= vDev.getDirection().and(videoDirectionUserPreference);
- if(onHold)
+ if(locallyOnHold)
videoDirection = videoDirection.and(MediaDirection.SENDONLY);
if(videoDirection != MediaDirection.INACTIVE);
@@ -456,26 +499,72 @@ private void registerDynamicPTsWithStream(MediaStream stream)
}
}
+ public String processOffer(String offerString)
+ throws OperationFailedException
+ {
+ SessionDescription offer = SdpUtils.parseSdpString(offerString);
+ if (localSess == null)
+ return processFirstOffer(offer).toString();
+ else
+ return processUpdateOffer(offer, localSess).toString();
+ }
+
public SessionDescription processFirstOffer(SessionDescription offer)
throws OperationFailedException,
IllegalArgumentException
{
this.remoteSess = offer;
- Vector remoteDescriptions
- = SdpUtils.extractMediaDescriptions(offer);
+ Vector answerDescriptions
+ = createMediaDescriptionsForAnswer(offer);
+
+ //wrap everything up in a session description
+ SessionDescription answer = SdpUtils.createSessionDescription(
+ getLastUsedLocalHost(), getUserName(), answerDescriptions);
+
+ this.localSess = answer;
+
+ return localSess;
+ }
+
+ public SessionDescription processUpdateOffer(
+ SessionDescription newOffer,
+ SessionDescription previousAnswer)
+ throws OperationFailedException,
+ IllegalArgumentException
+ {
+ this.remoteSess = newOffer;
+
+ Vector answerDescriptions
+ = createMediaDescriptionsForAnswer(newOffer);
+
+ // wrap everything up in a session description
+ SessionDescription newAnswer = SdpUtils.createSessionUpdateDescription(
+ previousAnswer, getLastUsedLocalHost(), answerDescriptions);
+
+ this.localSess = newAnswer;
+
+ return localSess;
+ }
+
+ private Vector createMediaDescriptionsForAnswer(
+ SessionDescription offer)
+ throws OperationFailedException
+ {
+ Vector remoteDescriptions = SdpUtils
+ .extractMediaDescriptions(offer);
MediaService mediaService = SipActivator.getMediaService();
- //prepare to generate answers to all the incoming descriptions
+ // prepare to generate answers to all the incoming descriptions
Vector answerDescriptions
- = new Vector(remoteDescriptions.size());
+ = new Vector( remoteDescriptions.size() );
this.setCallInfoURL(SdpUtils.getCallInfoURL(offer));
boolean atLeastOneValidDescription = false;
- for ( MediaDescription mediaDescription : remoteDescriptions)
+ for (MediaDescription mediaDescription : remoteDescriptions)
{
MediaType mediaType = SdpUtils.getMediaType(mediaDescription);
@@ -485,52 +574,46 @@ public SessionDescription processFirstOffer(SessionDescription offer)
MediaDevice dev = mediaService.getDefaultDevice(mediaType);
MediaDirection devDirection = dev.getDirection();
- //stream target
- MediaStreamTarget target
- = SdpUtils.extractDefaultTarget(mediaDescription, offer);
+ // stream target
+ MediaStreamTarget target = SdpUtils.extractDefaultTarget(
+ mediaDescription, offer);
if (supportedFormats == null || supportedFormats.size() == 0
|| dev == null || devDirection == MediaDirection.INACTIVE
|| target.getDataAddress().getPort() == 0)
{
- //mark stream as dead and go on bravely
- answerDescriptions.add(
- SdpUtils.createDisablingAnswer(mediaDescription));
+ // mark stream as dead and go on bravely
+ answerDescriptions.add(SdpUtils
+ .createDisablingAnswer(mediaDescription));
continue;
}
StreamConnector connector = getStreamConnector(mediaType);
- //determine the direction that we need to announce.
- MediaDirection remoteDirection
- = SdpUtils.getDirection(mediaDescription);
+ // determine the direction that we need to announce.
+ MediaDirection remoteDirection = SdpUtils
+ .getDirection(mediaDescription);
- MediaDirection direction
- = devDirection.getDirectionForAnswer(remoteDirection);
+ MediaDirection direction = devDirection
+ .getDirectionForAnswer(remoteDirection);
- //create the corresponding stream
- initStream( connector, dev, supportedFormats.get(0),
- target, direction);
+ // create the corresponding stream
+ initStream(connector, dev, supportedFormats.get(0), target,
+ direction);
- //create the answer description
- answerDescriptions.add( createMediaDescription(
- supportedFormats, connector, direction));
+ // create the answer description
+ answerDescriptions.add(createMediaDescription(supportedFormats,
+ connector, direction));
atLeastOneValidDescription = true;
}
- if(!atLeastOneValidDescription)
+ if (!atLeastOneValidDescription)
throw new OperationFailedException("Offer contained no valid "
- + "media descriptions.",
- OperationFailedException.ILLEGAL_ARGUMENT);
-
- //wrap everything up in a session description
- SessionDescription answer = SdpUtils.createSessionDescription(
- getLastUsedLocalHost(), getUserName(), answerDescriptions);
-
- this.localSess = answer;
+ + "media descriptions.",
+ OperationFailedException.ILLEGAL_ARGUMENT);
- return localSess;
+ return answerDescriptions;
}
public void processAnswer(SessionDescription answer)
@@ -778,6 +861,23 @@ private void initializePortNumbers()
}
}
+ /**
+ * Determines whether the remote party has placed all our streams on hold.
+ *
+ * @return true if all our streams have been placed on hold (i.e.
+ * if none of them is currently sending and false otherwise.
+ */
+ public boolean isRemotelyOnHold()
+ {
+ if(audioStream != null && audioStream.getDirection().allowsSending())
+ return false;
+
+ if(videoStream != null && videoStream.getDirection().allowsSending())
+ return false;
+
+ return true;
+ }
+
/**
* Returns a URL pointing ta a location with call control
* information for this peer or null if no such URL is
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
index ab9ca290c..034fafa7a 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
@@ -508,11 +508,16 @@ public void processReInvite(ServerTransaction serverTransaction)
try
{
response = messageFactory.createResponse(Response.OK, invite);
- attachSdpAnswer(response);
+ String sdpAnswer = getMediaHandler()
+ .processOffer( getSdpDescription() );
- logger.trace("will send an OK response: ");
+ response.setContent( sdpAnswer, getProtocolProvider()
+ .getHeaderFactory().createContentTypeHeader(
+ "application", "sdp"));
+
+ logger.trace("will send an OK response: " + response);
serverTransaction.sendResponse(response);
- logger.debug("sent a an OK response: "+ response);
+ logger.debug("OK response sent");
}
catch (Exception ex)//no need to distinguish among exceptions.
{
@@ -524,125 +529,67 @@ public void processReInvite(ServerTransaction serverTransaction)
return;
}
- try
- {
- updateMediaFlags();
- }
- catch (OperationFailedException ex)
- {
- logger.error("Error after sending response " + response, ex);
- }
+ reevalRemoteHoldStatus();
}
/**
- * Creates an SDP description that could be sent to peer and adds
- * it to response. Provides a hook for this instance to take last
- * configuration steps on a specific Response before it is sent to
- * a specific CallPeer as part of the execution of.
- *
- * @param response the Response to be sent to the peer
- *
- * @throws OperationFailedException if we fail parsing call peer's media.
- * @throws ParseException if we try to attach invalid SDP to response.
+ * Updates the state of this CallPeer to match the remotely-on-hold
+ * status of our media handler.
*/
- private void attachSdpAnswer(Response response)
- throws OperationFailedException, ParseException
+ private void reevalRemoteHoldStatus()
{
- /*
- * At the time of this writing, we're only getting called because a
- * response to a call-hold invite is to be sent.
- */
- /**
- * @todo update to neomedia.
- CallSession callSession = getMediaCallSession();
+ boolean remotelyOnHold = getMediaHandler().isRemotelyOnHold();
- String sdpAnswer = null;
- try
+ CallPeerState state = getState();
+ if (CallPeerState.ON_HOLD_LOCALLY.equals(state))
{
- sdpAnswer = callSession.processSdpOffer(this, getSdpDescription());
+ if (remotelyOnHold)
+ setState(CallPeerState.ON_HOLD_MUTUALLY);
}
- catch (MediaException ex)
+ else if (CallPeerState.ON_HOLD_MUTUALLY.equals(state))
{
- ProtocolProviderServiceSipImpl.throwOperationFailedException(
- "Failed to create SDP answer to put-on/off-hold request.",
- OperationFailedException.INTERNAL_ERROR, ex, logger);
+ if (!remotelyOnHold)
+ setState(CallPeerState.ON_HOLD_LOCALLY);
}
-
- response.setContent(
- sdpAnswer,
- getProtocolProvider().getHeaderFactory()
- .createContentTypeHeader("application", "sdp"));
- */
- }
-
- /**
- * Updates the media flags for this peer according to the value of the SDP
- * field.
- *
- * @throws OperationFailedException if we fail parsing callPeer's media.
- */
- private void updateMediaFlags()
- throws OperationFailedException
- {
- /*
- * At the time of this writing, we're only getting called because a
- * response to a call-hold invite is to be sent.
- */
- /**
- * @todo update to neomedia.
- CallSession callSession = getMediaCallSession();
-
- int mediaFlags = 0;
- try
+ else if (CallPeerState.ON_HOLD_REMOTELY.equals(state))
{
- mediaFlags = callSession.getSdpOfferMediaFlags(getSdpDescription());
+ if (!remotelyOnHold)
+ setState(CallPeerState.CONNECTED);
}
- catch (MediaException ex)
+ else if (remotelyOnHold)
{
- ProtocolProviderServiceSipImpl.throwOperationFailedException(
- "Failed to create SDP answer to put-on/off-hold request.",
- OperationFailedException.INTERNAL_ERROR, ex, logger);
+ setState(CallPeerState.ON_HOLD_REMOTELY);
}
- */
- /*
- * Comply with the request of the SDP offer with respect to putting on
- * hold.
- */
- /**
- * @todo update to neomedia.
- boolean on = ((mediaFlags & CallSession.ON_HOLD_REMOTELY) != 0);
+ }
- callSession.putOnHold(on, false);
+ /**
+ * Updates the state of this CallPeer to match the locally-on-hold
+ * status of our media handler.
+ */
+ private void reevalLocalHoldStatus()
+ {
+ boolean locallyOnHold = getMediaHandler().isLocallyOnHold();
CallPeerState state = getState();
if (CallPeerState.ON_HOLD_LOCALLY.equals(state))
{
- if (on)
- setState(CallPeerState.ON_HOLD_MUTUALLY);
+ if (!locallyOnHold)
+ setState(CallPeerState.CONNECTED);
}
else if (CallPeerState.ON_HOLD_MUTUALLY.equals(state))
{
- if (!on)
- setState(CallPeerState.ON_HOLD_LOCALLY);
+ if (!locallyOnHold)
+ setState(CallPeerState.ON_HOLD_REMOTELY);
}
else if (CallPeerState.ON_HOLD_REMOTELY.equals(state))
{
- if (!on)
- setState(CallPeerState.CONNECTED);
+ if (locallyOnHold)
+ setState(CallPeerState.ON_HOLD_MUTUALLY);
}
- else if (on)
+ else if (locallyOnHold)
{
- setState(CallPeerState.ON_HOLD_REMOTELY);
+ setState(CallPeerState.ON_HOLD_LOCALLY);
}
- */
- /*
- * Reflect the request of the SDP offer with respect to the modification
- * of the availability of media.
- */
- /**
- * @todo update to neomedia.
- callSession.setReceiveStreaming(mediaFlags);
- */
}
/**
@@ -1289,7 +1236,7 @@ public synchronized void answer()
/**
* Puts the CallPeer represented by this instance on or off hold.
*
- * @param on true to have the CallPeer put on hold;
+ * @param onHold true to have the CallPeer put on hold;
* false, otherwise
*
* @throws OperationFailedException if we fail to construct or send the
@@ -1300,7 +1247,7 @@ public void putOnHold(boolean onHold)
{
CallPeerMediaHandler mediaHandler = getMediaHandler();
- mediaHandler.setOnHold(onHold);
+ mediaHandler.setLocallyOnHold(onHold);
try
{
@@ -1313,36 +1260,7 @@ public void putOnHold(boolean onHold)
OperationFailedException.INTERNAL_ERROR, ex, logger);
}
- /*
- * Putting on hold isn't a negotiation (i.e. the issuing side takes the
- * decision and executes it) so we're muting now regardless of the
- * desire of the peer to accept the offer.
- */
- /**
- * @todo update to neomedia.
- callSession.putOnHold(on, true);
- */
-
- CallPeerState state = getState();
- if (CallPeerState.ON_HOLD_LOCALLY.equals(state))
- {
- if (!onHold)
- setState(CallPeerState.CONNECTED);
- }
- else if (CallPeerState.ON_HOLD_MUTUALLY.equals(state))
- {
- if (!onHold)
- setState(CallPeerState.ON_HOLD_REMOTELY);
- }
- else if (CallPeerState.ON_HOLD_REMOTELY.equals(state))
- {
- if (onHold)
- setState(CallPeerState.ON_HOLD_MUTUALLY);
- }
- else if (onHold)
- {
- setState(CallPeerState.ON_HOLD_LOCALLY);
- }
+ reevalLocalHoldStatus();
}
/**
@@ -1395,23 +1313,19 @@ public void invite()
try
{
inviteTran = (ClientTransaction)getLatestInviteTransaction();
- }
- catch(ClassCastException exc)
- {
- throw new OperationFailedException(
- "Can't invite someone that is actually inviting us",
- OperationFailedException.INTERNAL_ERROR, exc);
- }
- attachSdpOffer(inviteTran.getRequest());
+ ContentTypeHeader contentTypeHeader = getProtocolProvider()
+ .getHeaderFactory().createContentTypeHeader(
+ "application", "sdp");
+
+ inviteTran.getRequest().setContent(getMediaHandler().createOffer(),
+ contentTypeHeader);
- try
- {
inviteTran.sendRequest();
if (logger.isDebugEnabled())
logger.debug("sent request:\n" + inviteTran.getRequest());
}
- catch (SipException ex)
+ catch (Exception ex)
{
ProtocolProviderServiceSipImpl.throwOperationFailedException(
"An error occurred while sending invite request",
@@ -1419,42 +1333,6 @@ public void invite()
}
}
- /**
- * Creates an SDP offer destined to callPeer and attaches it to
- * the invite request.
- *
- * @param invite the invite Request that we'd like to attach an
- * SDP offer to.
- *
- * @throws OperationFailedException if we fail constructing the session
- * description.
- */
- private void attachSdpOffer(Request invite)
- throws OperationFailedException
- {
- try
- {
- ContentTypeHeader contentTypeHeader = getProtocolProvider()
- .getHeaderFactory().createContentTypeHeader(
- "application", "sdp");
-
- invite.setContent(getMediaHandler().createOffer(),
- contentTypeHeader);
- }
- catch (IllegalArgumentException ex)
- {
- ProtocolProviderServiceSipImpl.throwOperationFailedException(
- "Failed to obtain an InetAddress for " + ex.getMessage(),
- OperationFailedException.NETWORK_FAILURE, ex, logger);
- }
- catch (ParseException ex)
- {
- ProtocolProviderServiceSipImpl.throwOperationFailedException(
- "Failed to parse sdp data while creating invite request!",
- OperationFailedException.INTERNAL_ERROR, ex, logger);
- }
- }
-
/**
* Modifies the local media setup to reflect the requested setting for the
* streaming of the local video and then re-invites the peer represented by
@@ -1593,4 +1471,13 @@ private CallPeerMediaHandler getMediaHandler()
{
return mediaHandler;
}
+
+ /**
+ * Overrides the
+ */
+ public void setState(CallPeerState newState, String reason)
+ {
+ super.setState(newState, reason);
+ this.getMediaHandler().close();
+ }
}
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
index 0269f6507..ad6141e77 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
+++ b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
@@ -629,7 +629,7 @@ public void removeCallPeerConferenceListener(
* Adds a specific StreamSoundLevelListener to the list of
* listeners interested in and notified about changes in stream sound level
* related information.
- *
+ *
* @param listener the StreamSoundLevelListener to add
*/
public void addStreamSoundLevelListener(StreamSoundLevelListener listener)
@@ -645,7 +645,7 @@ public void addStreamSoundLevelListener(StreamSoundLevelListener listener)
* Removes a specific StreamSoundLevelListener of the list of
* listeners interested in and notified about changes in stream sound level
* related information.
- *
+ *
* @param listener the StreamSoundLevelListener to remove
*/
public void removeStreamSoundLevelListener(
@@ -662,7 +662,7 @@ public void removeStreamSoundLevelListener(
* Adds a specific ConferenceMembersSoundLevelListener to the list
* of listeners interested in and notified about changes in conference
* members sound level.
- *
+ *
* @param listener the ConferenceMembersSoundLevelListener to add
*/
public void addConferenceMembersSoundLevelListener(
@@ -679,7 +679,7 @@ public void addConferenceMembersSoundLevelListener(
* Removes a specific ConferenceMembersSoundLevelListener of the
* list of listeners interested in and notified about changes in conference
* members sound level.
- *
+ *
* @param listener the ConferenceMembersSoundLevelListener to
* remove
*/