diff options
author | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2009-03-16 12:48:14 +0000 |
---|---|---|
committer | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2009-03-16 12:48:14 +0000 |
commit | a29f088db924ab5912776029ea560d9162f0f9d5 (patch) | |
tree | a7486086af23f09d85411f24f907a5c559cd0c5d /src/net/java | |
parent | a4bd4879917e82161b8cd8adbfa89119fa104ad6 (diff) | |
download | jitsi-a29f088db924ab5912776029ea560d9162f0f9d5.zip jitsi-a29f088db924ab5912776029ea560d9162f0f9d5.tar.gz jitsi-a29f088db924ab5912776029ea560d9162f0f9d5.tar.bz2 |
Supports enabling/disabling the streaming of the local video to the remote call participants.
Diffstat (limited to 'src/net/java')
6 files changed, 327 insertions, 181 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java index 43bebab..1d7aa19 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java @@ -27,7 +27,7 @@ import net.java.sip.communicator.util.swing.border.*; public class CallDialog
extends SIPCommFrame
implements ActionListener,
- MouseListener
+ MouseListener
{
private static final String DIAL_BUTTON = "DIAL_BUTTON";
@@ -71,10 +71,8 @@ public class CallDialog muteButton = new MuteButton(call);
dialButton.setName(DIAL_BUTTON);
-
dialButton.setToolTipText(
GuiActivator.getResources().getI18NString("service.gui.DIALPAD"));
-
dialButton.addActionListener(this);
dialButton.addMouseListener(this);
@@ -83,15 +81,14 @@ public class CallDialog contentPane.add(buttonsPanel, BorderLayout.SOUTH);
hangupButton.setName(HANGUP_BUTTON);
-
hangupButton.setToolTipText(
GuiActivator.getResources().getI18NString("service.gui.HANG_UP"));
-
hangupButton.addActionListener(this);
settingsPanel.add(dialButton);
settingsPanel.add(holdButton);
settingsPanel.add(muteButton);
+ settingsPanel.add(new LocalVideoButton(getCall()));
buttonsPanel.add(settingsPanel, BorderLayout.WEST);
buttonsPanel.add(hangupButton, BorderLayout.EAST);
diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index 3bcfbe4..5b8be00 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -196,13 +196,7 @@ public class CallSessionImpl * The flag which signals that this side of the call has put the other on * hold. */ - private static final byte ON_HOLD_LOCALLY = 1 << 1; - - /** - * The flag which signals that the other side of the call has put this on - * hold. - */ - private static final byte ON_HOLD_REMOTELY = 1 << 2; + private static final byte ON_HOLD_LOCALLY = 1 << 0; /** * Indicates an audio session type. @@ -395,7 +389,7 @@ public class CallSessionImpl throws MediaException { //start all audio streams - boolean startedAtLeastOneStream = false; + boolean startedAtLeastOneAudioStream = false; RTPManager rtpManager = getAudioRtpManager(); List<SendStream> sendStreams = rtpManager.getSendStreams(); @@ -411,7 +405,7 @@ public class CallSessionImpl /** @todo are we sure we want to connect here? */ stream.getDataSource().connect(); stream.start(); - startedAtLeastOneStream = true; + startedAtLeastOneAudioStream = true; } catch (IOException ex) { @@ -425,6 +419,8 @@ public class CallSessionImpl } //start video streams + boolean startedAtLeastOneVideoStream = false; + rtpManager = getVideoRtpManager(); if(mediaServCallback.getMediaControl(getCall()).isLocalVideoAllowed() && ((sendStreams = rtpManager.getSendStreams()) != null) @@ -438,24 +434,24 @@ public class CallSessionImpl try { stream.start(); - startedAtLeastOneStream = true; + startedAtLeastOneVideoStream = true; } catch (IOException ex) { logger.warn("Failed to start stream.", ex); } } - - if (startedAtLeastOneStream) - setLocalVideoStreaming(true); } else { logger.trace("No video send streams will be started."); } + setLocalVideoStreaming(startedAtLeastOneVideoStream); - if(!startedAtLeastOneStream && sendStreams.size() > 0) + if(!startedAtLeastOneAudioStream + && !startedAtLeastOneVideoStream + && (sendStreams.size() > 0)) { stopStreaming(); throw new MediaException("Failed to start streaming" @@ -841,17 +837,10 @@ public class CallSessionImpl mediaDescription.setAttribute(newAttribute, null); } - /** - * Determines whether a specific SDP description <tt>String</tt> offers - * this party to be put on hold. - * - * @param sdpOffer the SDP description <tt>String</tt> to be examined for - * an offer to this party to be put on hold - * @return <tt>true</tt> if the specified SDP description <tt>String</tt> - * offers this party to be put on hold; <tt>false</tt>, otherwise - * @throws MediaException + /* + * Implements CallSession#getSdpOfferMediaFlags(String). */ - public boolean isSdpOfferToHold(String sdpOffer) throws MediaException + public int getSdpOfferMediaFlags(String sdpOffer) throws MediaException { SessionDescription description = null; try @@ -878,44 +867,96 @@ public class CallSessionImpl MediaException.INTERNAL_ERROR, ex); } + int mediaFlags = 0; + + /* Determine whether we're being put on hold. */ boolean isOfferToHold = true; for (Iterator<MediaDescription> mediaDescriptionIter = mediaDescriptions.iterator(); - mediaDescriptionIter.hasNext() - && isOfferToHold;) + mediaDescriptionIter.hasNext() && isOfferToHold;) { MediaDescription mediaDescription = mediaDescriptionIter.next(); - Vector<Attribute> attributes + Collection<Attribute> attributes = mediaDescription.getAttributes(false); isOfferToHold = false; if (attributes != null) { for (Iterator<Attribute> attributeIter = attributes.iterator(); - attributeIter.hasNext() - && !isOfferToHold;) + attributeIter.hasNext() && !isOfferToHold;) { try { String attribute = attributeIter.next().getName(); if ("sendonly".equalsIgnoreCase(attribute) - || "inactive".equalsIgnoreCase(attribute)) - { + || "inactive".equalsIgnoreCase(attribute)) isOfferToHold = true; - } } catch (SdpParseException ex) { throw new MediaException( - "Failed to get SDP media description attribute name", - MediaException.INTERNAL_ERROR, - ex); + "Failed to get SDP media description attribute name", + MediaException.INTERNAL_ERROR, + ex); } } } } - return isOfferToHold; + if (isOfferToHold) + mediaFlags |= ON_HOLD_REMOTELY; + + /* Determine which of the media is to be received. */ + for (MediaDescription mediaDescription : mediaDescriptions) + { + String mediaType; + + try + { + mediaType = mediaDescription.getMedia().getMediaType(); + } + catch (SdpParseException ex) + { + throw new MediaException( + "Failed to determine SDP description media type", + MediaException.INTERNAL_ERROR, + ex); + } + + int mediaFlag = 0; + + if ("audio".equalsIgnoreCase(mediaType)) + mediaFlag = RECEIVE_AUDIO; + else if ("video".equalsIgnoreCase(mediaType)) + mediaFlag = RECEIVE_VIDEO; + + mediaFlags |= mediaFlag; + + Collection<Attribute> attributes + = mediaDescription.getAttributes(false); + + if (attributes != null) + for (Attribute attribute : attributes) + { + try + { + String name = attribute.getName(); + + if ("recvonly".equalsIgnoreCase(name) + || "inactive".equalsIgnoreCase(name)) + mediaFlags &= ~mediaFlag; + } + catch (SdpParseException ex) + { + throw new MediaException( + "Failed to get SDP media description attribute name", + MediaException.INTERNAL_ERROR, + ex); + } + } + } + + return mediaFlags; } /** @@ -1070,7 +1111,11 @@ public class CallSessionImpl CallParticipantState responderState = responder.getState(); if (CallParticipantState.CONNECTED.equals(responderState) || CallParticipantState.isOnHold(responderState)) + { + mediaServCallback.getMediaControl(getCall()) + .startProcessingMedia(this); startStreaming(); + } } /** @@ -1476,9 +1521,8 @@ public class CallSessionImpl } else { - if (((lastIntendedDestination == null) && (intendedDestination != null)) - || ((lastIntendedDestination != null) && !lastIntendedDestination - .equals(intendedDestination))) + if ((intendedDestination != null) + && !intendedDestination.equals(lastIntendedDestination)) { startStreaming = stopStreaming(); audioRtpManager = RTPManager.newInstance(); @@ -2420,29 +2464,11 @@ public class CallSessionImpl this); // close all players that we have created in this session - Iterator<Player> playersIter = players.iterator(); - - while (playersIter.hasNext()) - { - Player player = playersIter.next(); - player.stop(); - - /* - * The player is being disposed so let the (interested) - * listeners know its Player#getVisualComponent() (if any) - * should be released. - */ - Component visualComponent = getVisualComponent(player); - if (visualComponent != null) - { - fireVideoEvent(VideoEvent.VIDEO_REMOVED, visualComponent, - VideoEvent.REMOTE); - } + Player[] players + = this.players.toArray(new Player[this.players.size()]); - player.deallocate(); - player.close(); - playersIter.remove(); - } + for (Player player : players) + disposePlayer(player); // remove ourselves as listeners from the call evt.getSourceCall().removeCallChangeListener(this); @@ -2462,6 +2488,26 @@ public class CallSessionImpl } } + private void disposePlayer(Player player) + { + player.stop(); + + /* + * The player is being disposed so let the (interested) listeners know + * its Player#getVisualComponent() (if any) should be released. + */ + Component visualComponent = getVisualComponent(player); + + player.deallocate(); + player.close(); + + players.remove(player); + + if (visualComponent != null) + fireVideoEvent(VideoEvent.VIDEO_REMOVED, visualComponent, + VideoEvent.REMOTE); + } + /** * Indicates that a change has occurred in the status of the source * CallParticipant. @@ -2758,8 +2804,8 @@ public class CallSessionImpl } else if (ce instanceof ControllerErrorEvent) { - logger - .error("The following error was reported while starting a player" + logger.error( + "The following error was reported while starting a player" + ce); } else if (ce instanceof ControllerClosedEvent) @@ -3247,6 +3293,26 @@ public class CallSessionImpl return localVideoStreaming; } + /* + * Implements CallSession#setReceiveStreaming(int). + */ + public void setReceiveStreaming(int mediaFlags) + { + if ((mediaFlags & RECEIVE_VIDEO) == 0) + { + Player[] players + = this.players.toArray(new Player[this.players.size()]); + + for (Player player : players) + { + Component visualComponent = getVisualComponent(player); + + if (visualComponent != null) + disposePlayer(player); + } + } + } + private class LocalVisualComponentData { public final VideoListener listener; diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java index 3ace374..800cc40 100644 --- a/src/net/java/sip/communicator/impl/media/MediaControl.java +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -10,6 +10,7 @@ import java.awt.Dimension; import java.io.*; import java.net.*; import java.util.*; + import javax.media.*; import javax.media.control.*; import javax.media.format.*; @@ -90,7 +91,7 @@ public class MediaControl * The processor that will be handling content coming from our capture data * sources. */ - private Processor sourceProcessor = null; + public Processor sourceProcessor = null; /** * The list of readers currently using our processor. @@ -248,7 +249,7 @@ public class MediaControl setVideoDataSource((SourceCloneable) cloneableVideoDataSource); } - // Create the av data source + // Create the audio/video data source. if (audioDataSource != null && videoDataSource != null) { try @@ -263,10 +264,12 @@ public class MediaControl { logger.fatal( "Failed to create a media data source!" - + "Media transmission won't be enabled!", exc); - throw new InternalError("Failed to create a media data source!" - + "Media transmission won't be enabled!" - + exc.getMessage()); + + "Media transmission won't be enabled!", + exc); + throw new InternalError( + "Failed to create a media data source!" + + "Media transmission won't be enabled!" + + exc.getMessage()); } } else if (audioDataSource != null) @@ -279,6 +282,8 @@ public class MediaControl //avDataSource may be null (Bug report Vince Fourcade) if (avDataSource != null) initProcessor(avDataSource); + else + sourceProcessor = null; } /** @@ -321,9 +326,7 @@ public class MediaControl //avDataSource may be null (Bug report Vince Fourcade) if (avDataSource != null) - { initProcessor(avDataSource); - } } @@ -352,11 +355,11 @@ public class MediaControl { logger.error( "An internal error occurred while" - + " trying to connec to to datasource!" + + " trying to connec to the datasource!" , ex); throw new MediaException( "An internal error occurred while" - + " trying to connec to to datasource!" + + " trying to connec to the datasource!" , MediaException.INTERNAL_ERROR , ex); } @@ -389,7 +392,7 @@ public class MediaControl { throw new MediaException( "Media manager could not configure processor\n" - + "for the specified data source", + + "for the specified data source", MediaException.INTERNAL_ERROR); } @@ -398,12 +401,12 @@ public class MediaControl { logger.error( "Media manager could not create a processor\n" - + "for the specified data source" + + "for the specified data source" , ex ); throw new MediaException( "Media manager could not create a processor\n" - + "for the specified data source" + + "for the specified data source" , MediaException.INTERNAL_ERROR , ex); } @@ -411,15 +414,15 @@ public class MediaControl { logger.error( "Media manager could not connect " - + "to the specified data source" + + "to the specified data source" , ex); throw new MediaException("Media manager could not connect " - + "to the specified data source" + + "to the specified data source" , MediaException.INTERNAL_ERROR , ex); } - sourceProcessor.setContentDescriptor(new ContentDescriptor( - ContentDescriptor.RAW_RTP)); + sourceProcessor.setContentDescriptor( + new ContentDescriptor(ContentDescriptor.RAW_RTP)); /* * The lists of the supported audio and video encodings will have to be @@ -612,61 +615,57 @@ public class MediaControl * @throws MediaException if creating the data source fails for some reason. */ public DataSource createDataSourceForEncodings( - Hashtable<String, List<String>> encodingSets) + Map<String, List<String>> encodingSets) throws MediaException { if (sourceProcessor == null) { logger.error("Processor is null."); throw new MediaException("The source Processor has not been " - + "initialized." + + "initialized." , MediaException.INTERNAL_ERROR); } // Wait for the sourceProcessor to configure - boolean processorIsReady = true; if (sourceProcessor.getState() < Processor.Configured) { - processorIsReady = processorUtility - .waitForState(sourceProcessor, Processor.Configured); - } - if (!processorIsReady) - { - logger.error("Couldn't configure sourceProcessor"); - throw new MediaException("Couldn't configure sourceProcessor" - , MediaException.INTERNAL_ERROR); + if (!processorUtility.waitForState(sourceProcessor, + Processor.Configured)) + { + logger.error("Couldn't configure sourceProcessor"); + throw new MediaException("Couldn't configure sourceProcessor" + , MediaException.INTERNAL_ERROR); + } } // Get the tracks from the sourceProcessor TrackControl[] tracks = sourceProcessor.getTrackControls(); - // Do we have atleast one track? - if (tracks == null || tracks.length < 1) + // Do we have at least one track? + if ((tracks == null) || (tracks.length < 1)) { logger.error("Couldn't find any tracks in sourceProcessor"); throw new MediaException( "Couldn't find any tracks in sourceProcessor" , MediaException.INTERNAL_ERROR); } - // Set the output content descriptor to RAW_RTP - // This will limit the supported formats reported from - // Track.getSupportedFormats to only valid RTP formats. - ContentDescriptor cd = new ContentDescriptor(ContentDescriptor. - RAW_RTP); - sourceProcessor.setContentDescriptor(cd); - Format supported[]; - Format chosenFormat; + if (logger.isDebugEnabled() + && (sourceProcessor.getState() > Processor.Configured)) + logger.debug( + "sourceProcessor is in state " + + sourceProcessor.getState() + + "which is > Processor.Configured" + + "and then TrackControl.setFormat(Format) may not work."); boolean atLeastOneTrack = false; // Program the tracks. for (int i = 0; i < tracks.length; i++) { if (tracks[i].isEnabled()) { - supported = tracks[i].getSupportedFormats(); + Format[] supported = tracks[i].getSupportedFormats(); if (logger.isDebugEnabled()) { logger.debug("Available encodings are:"); for (int j = 0; j < supported.length; j++) { - logger.debug("track[" + (i + 1) + "] format[" + - (j + 1) + "]=" + logger.debug("track[" + i + "] format[" + j + "]=" + supported[j].getEncoding()); } } @@ -685,7 +684,7 @@ public class MediaControl encodingSets); if (index != -1) { - chosenFormat = assertSize( + Format chosenFormat = assertSize( (VideoFormat)supported[index]); tracks[i].setFormat(chosenFormat); @@ -711,12 +710,15 @@ public class MediaControl encodingSets); if (index != -1) { - tracks[i].setFormat(supported[index]); + Format setFormat + = tracks[i].setFormat(supported[index]); if (logger.isDebugEnabled()) { - logger.debug("Track " + i + - " is set to transmit as: " - + supported[index]); + logger.debug( + "Track " + + i + + " is set to transmit as: " + + setFormat); } atLeastOneTrack = true; } @@ -747,9 +749,8 @@ public class MediaControl } // Realize the sourceProcessor. This will internally create a flow // graph and attempt to create an output datasource - processorIsReady = processorUtility.waitForState(sourceProcessor - , Controller.Realized); - if (!processorIsReady) + if (!processorUtility.waitForState(sourceProcessor, + Controller.Realized)) { logger.error("Couldn't realize sourceProcessor"); throw new MediaException("Couldn't realize sourceProcessor" @@ -888,23 +889,24 @@ public class MediaControl * @return the index of the format corresponding to the first encoding that * had a marching format in the <tt>availableFormats</tt> array. */ - protected int findFirstMatchingFormat(Format[] availableFormats, - Hashtable<String, List<String>> requestedEncodings) + protected int findFirstMatchingFormat( + Format[] availableFormats, + Map<String, List<String>> requestedEncodings) { if (availableFormats == null || requestedEncodings == null) { return -1; } - Enumeration<List<String>> formatSets = requestedEncodings.elements(); - while (formatSets.hasMoreElements()) + for (List<String> requestedEncodingSet : requestedEncodings.values()) { - for (String currentSetElement : formatSets.nextElement()) + for (String requestedEncoding : requestedEncodingSet) { for (int i = 0; i < availableFormats.length; i++) { - if (availableFormats[i].getEncoding().equals( - currentSetElement)) + String availableEncoding = availableFormats[i].getEncoding(); + + if (availableEncoding.equals(requestedEncoding)) { return i; } @@ -914,7 +916,6 @@ public class MediaControl return -1; } - /** * Returns an array of Strings containing video formats in the order of * preference. @@ -945,7 +946,7 @@ public class MediaControl * that we don't pull the plug from underneath their feet. * * @param reader a reference to the object calling this method, that we - * could use for keeping the number of simulaneous active readers. + * could use for keeping the number of simultaneous active readers. */ public void startProcessingMedia(Object reader) { @@ -1158,10 +1159,19 @@ public class MediaControl localVideoAllowed = allowed; if (sourceProcessor != null) + { sourceProcessor.stop(); + if (sourceProcessor.getState() == Processor.Realized) + { + DataSource dataOutput = sourceProcessor.getDataOutput(); + + if (dataOutput != null) + dataOutput.disconnect(); + } + sourceProcessor.deallocate(); + sourceProcessor.close(); + } initCaptureDevices(); - if (sourceProcessor.getState() != Processor.Started) - sourceProcessor.start(); } } diff --git a/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java b/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java index e6a77b0..0318d4e 100644 --- a/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java +++ b/src/net/java/sip/communicator/impl/media/codec/EncodingConfiguration.java @@ -32,20 +32,22 @@ public class EncodingConfiguration * SDP Codes of all video formats that JMF supports. */ private final String[] availableVideoEncodings = new String[] - { Integer.toString(Constants.H264_RTP_SDP), - // javax.media.format.VideoFormat.H263_RTP + { + Integer.toString(Constants.H264_RTP_SDP), + // javax.media.format.VideoFormat.H263_RTP Integer.toString(SdpConstants.H263), // javax.media.format.VideoFormat.JPEG_RTP Integer.toString(SdpConstants.JPEG), // javax.media.format.VideoFormat.H261_RTP - Integer.toString(SdpConstants.H261) }; + Integer.toString(SdpConstants.H261) + }; /** * SDP Codes of all audio formats that JMF supports. */ private final String[] availableAudioEncodings = new String[] { - // ILBC + // ILBC Integer.toString(97), // javax.media.format.AudioFormat.G723_RTP Integer.toString(SdpConstants.G723), @@ -58,13 +60,14 @@ public class EncodingConfiguration // javax.media.format.AudioFormat.DVI_RTP; Integer.toString(SdpConstants.DVI4_16000), // javax.media.format.AudioFormat.ALAW; - Integer.toString(SdpConstants.PCMA), Integer.toString(110), + Integer.toString(SdpConstants.PCMA), + Integer.toString(110), // javax.media.format.AudioFormat.G728_RTP; Integer.toString(SdpConstants.G728) - // javax.media.format.AudioFormat.G729_RTP + // javax.media.format.AudioFormat.G729_RTP // g729 is not suppported by JMF // Integer.toString(SdpConstants.G729) - }; + }; private final Set<String> supportedVideoEncodings = new TreeSet<String>(new EncodingComparator()); @@ -82,7 +85,7 @@ public class EncodingConfiguration private final Map<String, Integer> encodingPreferences = new Hashtable<String, Integer>(); - private static final String[] customCodecs = + private static final String[] CUSTOM_CODECS = new String[] { FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.alaw.Encoder" @@ -269,14 +272,12 @@ public class EncodingConfiguration */ public void registerCustomCodecs() { - // use a set to check if the codecs are already - // registered in jmf.properties - Set<String> registeredPlugins = new HashSet<String>(); - - registeredPlugins.addAll(PlugInManager.getPlugInList(null, null, - PlugInManager.CODEC)); + // Register the custom codec which haven't already been registered. + Collection<String> registeredPlugins = new HashSet<String>( + PlugInManager.getPlugInList(null, null, PlugInManager.CODEC)); + boolean commit = false; - for (String className : customCodecs) + for (String className : CUSTOM_CODECS) { if (registeredPlugins.contains(className)) { @@ -284,34 +285,50 @@ public class EncodingConfiguration } else { + commit = true; + + boolean registered; + Throwable exception = null; + try { - Object instance = Class.forName(className).newInstance(); - - boolean result = - PlugInManager.addPlugIn(className, ((Codec) instance) - .getSupportedInputFormats(), ((Codec) instance) - .getSupportedOutputFormats(null), + Codec codec = (Codec) + Class.forName(className).newInstance(); + + registered = + PlugInManager.addPlugIn( + className, + codec.getSupportedInputFormats(), + codec.getSupportedOutputFormats(null), PlugInManager.CODEC); - logger.debug("Codec : " + className - + " is successfully registered : " + result); } catch (Throwable ex) { - logger.debug("Codec : " + className - + " is NOT succsefully registered", ex); + registered = false; + exception = ex; } + if (registered) + logger.debug( + "Codec " + + className + + " is successfully registered"); + else + logger.debug( + "Codec " + + className + + " is NOT succsefully registered", exception); } } - try - { - PlugInManager.commit(); - } - catch (IOException ex) - { - logger.error("Cannot commit to PlugInManager", ex); - } + if (commit) + try + { + PlugInManager.commit(); + } + catch (IOException ex) + { + logger.error("Cannot commit to PlugInManager", ex); + } // Register the custom codec formats with the RTP manager once at // initialization. This is needed for the Sun JMF implementation. It @@ -399,7 +416,7 @@ public class EncodingConfiguration } /** - * Comaparator sorting the sets according the settings in + * Comaparator sorting the sets according to the settings in * encodingPreferences. */ private class EncodingComparator 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 67484a8..624ba3b 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -885,7 +885,7 @@ public class OperationSetBasicTelephonySipImpl if (callParticipant.getState() == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA) { - // This can happen if we are receigin early media for a second time. + // This can happen if we are receiving early media for a second time. logger.warn("Ignoring invite 183 since call participant is " + "already exchanging early media."); return; @@ -1026,7 +1026,8 @@ public class OperationSetBasicTelephonySipImpl /* * Receiving an Invite OK is allowed even when the participant is - * already connected for the purposes of call hold. + * already connected. Examples include call hold, enabling/disabling the + * streaming of local video while in a call. */ Request ack = null; @@ -1074,7 +1075,7 @@ public class OperationSetBasicTelephonySipImpl // listeners get alerted and they need the sdp // ignore sdp if we have already received one in early media if(!CallParticipantState.CONNECTING_WITH_EARLY_MEDIA. - equals(callParticipant.getState())) + equals(callParticipant.getState())) callParticipant.setSdpDescription(new String(ok.getRawContent())); // notify the media manager of the sdp content @@ -1128,13 +1129,17 @@ public class OperationSetBasicTelephonySipImpl } } - // ignore sdp process if we have already received one in early media + /* + * We used to not process the SDP if we had already received one in + * early media. But functionality using re-invites (e.g. toggling + * the streaming of local video while in a call) may need to process + * the SDP (e.g. because of re-negotiating the media after toggling + * the streaming of local video). + */ CallParticipantState callParticipantState = callParticipant.getState(); - if ((callParticipantState != CallParticipantState.CONNECTED) - && !CallParticipantState.isOnHold(callParticipantState) - && !CallParticipantState.CONNECTING_WITH_EARLY_MEDIA. - equals(callParticipantState)) + if (!CallParticipantState.CONNECTING_WITH_EARLY_MEDIA + .equals(callParticipantState)) { callSession.processSdpAnswer(callParticipant, callParticipant .getSdpDescription()); @@ -1145,7 +1150,7 @@ public class OperationSetBasicTelephonySipImpl */ callParticipant.setCallInfoURL(callSession.getCallInfoURL()); } - //at this point we have already sent our ack so in adition to logging + //at this point we have already sent our ack so in addition to logging //an error we also need to hangup the call participant. catch (Exception exc)//Media or parse exception. { @@ -1791,8 +1796,10 @@ public class OperationSetBasicTelephonySipImpl try { sdpAnswer = - callSession.createSdpDescriptionForHold(sdpOffer, callSession - .isSdpOfferToHold(sdpOffer)); + callSession.createSdpDescriptionForHold( + sdpOffer, + (callSession.getSdpOfferMediaFlags(sdpOffer) + & CallSession.ON_HOLD_REMOTELY) != 0); } catch (MediaException ex) { @@ -1831,12 +1838,12 @@ public class OperationSetBasicTelephonySipImpl CallParticipantSipImpl sipParticipant = (CallParticipantSipImpl) participant; - boolean on = false; + int mediaFlags = 0; try { - on = + mediaFlags = callSession - .isSdpOfferToHold(sipParticipant.getSdpDescription()); + .getSdpOfferMediaFlags(sipParticipant.getSdpDescription()); } catch (MediaException ex) { @@ -1845,6 +1852,12 @@ public class OperationSetBasicTelephonySipImpl OperationFailedException.INTERNAL_ERROR, ex); } + /* + * Comply with the request of the SDP offer with respect to putting on + * hold. + */ + boolean on = ((mediaFlags & CallSession.ON_HOLD_REMOTELY) != 0); + callSession.putOnHold(on, false); CallParticipantState state = sipParticipant.getState(); @@ -1867,6 +1880,12 @@ public class OperationSetBasicTelephonySipImpl { sipParticipant.setState(CallParticipantState.ON_HOLD_REMOTELY); } + + /* + * Reflect the request of the SDP offer with respect to the modification + * of the availability of media. + */ + callSession.setReceiveStreaming(mediaFlags); } /** diff --git a/src/net/java/sip/communicator/service/media/CallSession.java b/src/net/java/sip/communicator/service/media/CallSession.java index b6d2582..8e3d915 100644 --- a/src/net/java/sip/communicator/service/media/CallSession.java +++ b/src/net/java/sip/communicator/service/media/CallSession.java @@ -100,16 +100,53 @@ public interface CallSession boolean onHold) throws MediaException; /** + * The media flag which signals that the other side of the call has put this + * on hold. + */ + public static final byte ON_HOLD_REMOTELY = 1 << 1; + + /** + * The media flag which signals that audio streams being received are to be + * handled (e.g. played). + */ + public static final byte RECEIVE_AUDIO = 1 << 2; + + /** + * The media flag which signals that video streams being received are to be + * handled (e.g. played). + */ + public static final byte RECEIVE_VIDEO = 1 << 3; + + /** * Determines whether a specific SDP description <tt>String</tt> offers - * this party to be put on hold. + * this party to be put on hold and which media types are offered to be + * received. * * @param sdpOffer the SDP description <tt>String</tt> to be examined for - * an offer to this party to be put on hold - * @return <tt>true</tt> if the specified SDP description <tt>String</tt> - * offers this party to be put on hold; <tt>false</tt>, otherwise + * an offer to this party to be put on hold and media types to be + * received + * @return an <tt>int</tt> bit mask containing + * <code>ON_HOLD_REMOTELY</code> if the specified SDP description + * offers this party to be put on hold, <code>RECEIVE_AUDIO</code> + * and/or <code>RECEIVE_VIDEO</code> if audio and/or video, + * respectively, are to be received * @throws MediaException */ - public boolean isSdpOfferToHold(String sdpOffer) throws MediaException; + public int getSdpOfferMediaFlags(String sdpOffer) throws MediaException; + + /** + * Modifies the current setup of the stream receiving in accord with a + * specific set of media flags (which are usually obtained through + * {@link #getSdpOfferMediaFlags(String)}. For example, if + * <code>RECEIVE_VIDEO</code> isn't present and video is currently being + * received and played, stops its receiving and playback. + * + * @param mediaFlags an <code>int</code> bit mask containing any of the + * media-related flags such as <code>RECEIVE_AUDIO</code> and + * <code>RECEIVE_VIDEO</code> and thus specifying which media + * types are to be received + */ + public void setReceiveStreaming(int mediaFlags); /** * Puts the media of this <tt>CallSession</tt> on/off hold depending on |