diff --git a/lib/installer-exclude/JainSipApi1.2.jar b/lib/installer-exclude/JainSipApi1.2.jar index 05f3b4286..7e1126f5f 100644 Binary files a/lib/installer-exclude/JainSipApi1.2.jar and b/lib/installer-exclude/JainSipApi1.2.jar differ diff --git a/lib/installer-exclude/JainSipRi1.2.jar b/lib/installer-exclude/JainSipRi1.2.jar index 27be2bc3c..48d84fe0a 100644 Binary files a/lib/installer-exclude/JainSipRi1.2.jar and b/lib/installer-exclude/JainSipRi1.2.jar differ diff --git a/lib/installer-exclude/nist-sdp-1.0.jar b/lib/installer-exclude/nist-sdp-1.0.jar index 7a8b82f70..e264639b9 100644 Binary files a/lib/installer-exclude/nist-sdp-1.0.jar and b/lib/installer-exclude/nist-sdp-1.0.jar differ diff --git a/lib/logging.properties b/lib/logging.properties index 85b86d01e..3a5b31b7f 100644 --- a/lib/logging.properties +++ b/lib/logging.properties @@ -27,7 +27,7 @@ handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler # can be overriden by a facility specific level # Note that the ConsoleHandler also has a separate level # setting to limit messages printed to the console. -.level= INFO +.level= WARN ############################################################ # Handler specific properties. @@ -39,13 +39,13 @@ java.util.logging.FileHandler.pattern = ./log/sip-communicator%u.log java.util.logging.FileHandler.limit = 5000000 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter -java.util.logging.FileHandler.level = FINEST +java.util.logging.FileHandler.level = INFO # Limit the message that are printed on the console to FINEST and above (all). -java.util.logging.ConsoleHandler.level = FINEST +java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 74b4c95d8..6c12c15b1 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -663,14 +663,14 @@ gtalkRegisterNewAccountText=In case you don't have a Google Talk account, click # key binding chooser keybindings=Keybindings -chat-nextTab=Next Tab -chat-previousTab=Previous Tab +chat-nextTab=Next Chat Tab +chat-previousTab=Previous Chat Tab chat-copy=Copy chat-paste=Paste -chat-openSmilies=Open Smilies -chat-openHistory=Open History -chat-close=Close +chat-openSmilies=Show Smilies +chat-openHistory=Show History +chat-close=Close Chat Window chat-cut=Cut main-rename=Rename -main-nextTab=Next Tab -main-previousTab=Previous Tab \ No newline at end of file +main-nextTab=Next Contact List Tab +main-previousTab=Previous Contact List Tab diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index eb3924318..3eef5e916 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -55,9 +55,6 @@ * bye the remote party and we only need to configure our processor and * RTPManagers. * - * @todo implement SendStreamListener. - * @todo implement ReceiveStreamListener. - * * @author Emil Ivov * @author Ryan Ricard * @author Ken Larson @@ -461,7 +458,7 @@ private void printFlowStatistics(RTPManager rtpManager) //print flow statistics. GlobalTransmissionStats s = rtpManager.getGlobalTransmissionStats(); - logger.info( + logger.debug( "global transmission stats (" + rtpManagerDescription + "): \n" + "bytes sent: " + s.getBytesSent() + "\n" + "local colls: " + s.getLocalColls() + "\n" + @@ -473,7 +470,7 @@ private void printFlowStatistics(RTPManager rtpManager) GlobalReceptionStats rs = rtpManager.getGlobalReceptionStats(); - logger.info( + logger.debug( "global reception stats (" + rtpManagerDescription + "): \n" + "bad RTCP packets: " + rs.getBadRTCPPkts() + "\n" + "bad RTP packets: " + rs.getBadRTPkts() + "\n" + @@ -1931,6 +1928,7 @@ public void callStateChanged(CallChangeEvent evt) { try { + logger.debug("call connected. starting streaming"); startStreaming(); mediaServCallback.getMediaControl(getCall()) .startProcessingMedia(this); @@ -1941,9 +1939,10 @@ public void callStateChanged(CallChangeEvent evt) logger.error("Failed to start streaming.", ex); } } - else if( evt.getNewValue() == CallState.CALL_ENDED + else if( evt.getNewValue() == CallState.CALL_ENDED && evt.getNewValue() != evt.getOldValue()) { + logger.warn("Stopping streaming."); stopStreaming(); mediaServCallback.getMediaControl(getCall()) .stopProcessingMedia(this); @@ -1960,7 +1959,6 @@ else if( evt.getNewValue() == CallState.CALL_ENDED playersIter.remove(); } - //close all video frames that we have created in this session Iterator videoFramesIter = videoFrames.iterator(); while(videoFramesIter.hasNext()) diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java index 951e32c88..9a2e35418 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -190,17 +190,15 @@ public void participantDisplayNameChanged(CallParticipantChangeEvent evt) */ public void participantStateChanged(CallParticipantChangeEvent evt) { - if(((CallParticipantState)evt.getNewValue()) - == CallParticipantState.DISCONNECTED - || ((CallParticipantState)evt.getNewValue()) - == CallParticipantState.FAILED) + CallParticipantState newState = (CallParticipantState)evt.getNewValue(); + if(newState == CallParticipantState.DISCONNECTED + || newState == CallParticipantState.FAILED) { removeCallParticipant( (CallParticipantSipImpl)evt.getSourceCallParticipant()); } - else if (((CallParticipantState)evt.getNewValue()) - == CallParticipantState.CONNECTED - && getCallState().equals(CallState.CALL_INITIALIZATION)) + else if ((newState == CallParticipantState.CONNECTED + || newState == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA)) { setCallState(CallState.CALL_IN_PROGRESS); } 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 9dd24ba27..6eca61e99 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -679,6 +679,11 @@ else if (response.getStatusCode() == Response.RINGING) { processRinging(clientTransaction, response); } + //Session Progress + if (response.getStatusCode() == Response.SESSION_PROGRESS) + { + processSessionProgress(clientTransaction, response); + } //Trying else if (response.getStatusCode() == Response.TRYING) { @@ -787,6 +792,111 @@ private void processRinging(ClientTransaction clientTransaction, callParticipant.setState(CallParticipantState.ALERTING_REMOTE_SIDE); } + /** + * Handles early media in 183 Session Progress responses. Retrieves the SDP + * and makes sure that we start transmitting and playing early media that + * we receive. Puts the call into a CONNECTING_WITH_EARLY_MEDIA state. + * + * @param clientTransaction the ClientTransaction that the response + * arrived in. + * @param sessionProgress the 183 Response to process + */ + private void processSessionProgress(ClientTransaction clientTransaction, + Response sessionProgress) + { + + Dialog dialog = clientTransaction.getDialog(); + //find the call + CallParticipantSipImpl callParticipant + = activeCallsRepository.findCallParticipant(dialog); + + if (callParticipant.getState() + == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA) + { + // This can happen if we are receigin early media for a second time. + logger.warn("Ignoring invite 183 since call participant is " + +"already exchanging early media."); + return; + } + + if (sessionProgress.getContentLength().getContentLength() == 0) + { + logger.warn("Ignoring a 183 with no content"); + return; + } + + ContentTypeHeader contentTypeHeader = (ContentTypeHeader) + sessionProgress.getHeader(ContentTypeHeader.NAME); + + if(!contentTypeHeader.getContentType().equalsIgnoreCase("application") + || !contentTypeHeader.getContentSubType().equalsIgnoreCase("sdp")) + { + // This can happen if we are receigin early media for a second time. + logger.warn("Ignoring invite 183 since call participant is " + +"already exchanging early media."); + return; + } + + //set sdp content before setting call state as that is where + //listeners get alerted and they need the sdp + callParticipant.setSdpDescription( + new String(sessionProgress.getRawContent())); + + //notify the media manager of the sdp content + CallSession callSession + = ((CallSipImpl)callParticipant.getCall()).getMediaCallSession(); + + if(callSession == null) + { + //unlikely to happen because it would mean we didn't send an offer + //in the invite and we always send one. + logger.warn("Could not find call session."); + return; + } + + try + { + callSession.processSdpAnswer( + callParticipant, + new String(sessionProgress.getRawContent())); + } + catch (ParseException exc) + { + logger.error("There was an error parsing the SDP description of " + + callParticipant.getDisplayName() + + "(" + callParticipant.getAddress() + ")" + , exc); + callParticipant.setState(CallParticipantState.FAILED + , "There was an error parsing the SDP description of " + + callParticipant.getDisplayName() + + "(" + callParticipant.getAddress() + ")"); + } + catch (MediaException exc) + { + logger.error("We failed to process the SDP description of " + + callParticipant.getDisplayName() + + "(" + callParticipant.getAddress() + ")" + + ". Error was: " + + exc.getMessage() + , exc); + callParticipant.setState(CallParticipantState.FAILED + , "We failed to process the SDP description of " + + callParticipant.getDisplayName() + + "(" + callParticipant.getAddress() + ")" + + ". Error was: " + + exc.getMessage()); + } + + //set the call url in case there was one + /** @todo this should be done in CallSession, once we move + * it here.*/ + callParticipant.setCallInfoURL(callSession.getCallInfoURL()); + + //change status + callParticipant.setState( + CallParticipantState.CONNECTING_WITH_EARLY_MEDIA); + } + /** * Sets to CONNECTED that state of the corresponding call participant and * sends an ACK. @@ -1794,9 +1904,12 @@ public synchronized void hangupCallParticipant(CallParticipant participant) sayBye(callParticipant); callParticipant.setState(CallParticipantState.DISCONNECTED); } - else if (participantState.equals(CallParticipantState.CONNECTING) - || participantState - .equals(CallParticipantState.ALERTING_REMOTE_SIDE)) + else if (callParticipant.getState() + .equals(CallParticipantState.CONNECTING) + || callParticipant.getState() + .equals(CallParticipantState.CONNECTING_WITH_EARLY_MEDIA) + || callParticipant.getState() + .equals(CallParticipantState.ALERTING_REMOTE_SIDE)) { if (callParticipant.getFirstTransaction() != null) { @@ -1935,6 +2048,28 @@ private void sayBye(CallParticipantSipImpl callParticipant) try { bye = callParticipant.getDialog().createRequest(Request.BYE); + + //we have to set the via headers our selves because otherwise + //jain sip would send them with a 0.0.0.0 address + InetAddress destinationInetAddress = null; + String host = ( (SipURI) bye.getRequestURI()).getHost(); + try + { + destinationInetAddress = InetAddress.getByName(host); + } + catch (UnknownHostException ex) + { + throw new IllegalArgumentException( + host + " is not a valid internet address " + + ex.getMessage()); + } + + ArrayList viaHeaders= protocolProvider.getLocalViaHeaders( + destinationInetAddress, + protocolProvider.getRegistrarConnection() + .getRegistrarListeningPoint()); + bye.setHeader((ViaHeader)viaHeaders.get(0)); + bye.addHeader(protocolProvider.getSipCommUserAgentHeader()); } catch (SipException ex) { diff --git a/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java b/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java index f961310ae..0df1d1396 100644 --- a/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java +++ b/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java @@ -24,7 +24,7 @@ public abstract class KeybindingSet * @return mapping of keystrokes to the string representation of the actions * they perform */ - public abstract LinkedHashMap getBindings(); + public abstract HashMap getBindings(); /** * Resets the bindings and notifies the observer's listeners if they've diff --git a/src/net/java/sip/communicator/service/protocol/CallParticipantState.java b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java index d749a7b99..7971908a0 100644 --- a/src/net/java/sip/communicator/service/protocol/CallParticipantState.java +++ b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java @@ -85,6 +85,21 @@ public class CallParticipantState */ public static final CallParticipantState CONNECTING = new CallParticipantState(_CONNECTING); + + /** + * This constant value indicates a String representation of the CONNECTING + * call state but in cases where early media is being exchanged. + *
This constant has the String value "Connecting". + */ + public static final String _CONNECTING_WITH_EARLY_MEDIA = "Connecting*"; + + /** + * This constant value indicates that the state of the call participant is + * is CONNECTING - which means that a network connection to that participant + * is currently being established. + */ + public static final CallParticipantState CONNECTING_WITH_EARLY_MEDIA = + new CallParticipantState( _CONNECTING_WITH_EARLY_MEDIA ); /** * This constant value indicates a String representation of the diff --git a/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java b/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java index 159e5dc17..0dac4f887 100644 --- a/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java +++ b/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java @@ -156,6 +156,10 @@ private JabberStatusEnum(String iconPath) this.doNotDisturbStatus = new JabberPresenceStatus(30, DO_NOT_DISTURB, loadIcon(iconPath + "/status16x16-dnd.png")); + + this.extendedAwayStatus = + new JabberPresenceStatus(35, EXTENDED_AWAY, loadIcon(iconPath + + "/status16x16-xa.png")); this.awayStatus = new JabberPresenceStatus(40, AWAY, loadIcon(iconPath @@ -168,10 +172,6 @@ private JabberStatusEnum(String iconPath) this.freeForChatStatus = new JabberPresenceStatus(85, FREE_FOR_CHAT, loadIcon(iconPath + "/status16x16-ffc.png")); - - this.extendedAwayStatus = - new JabberPresenceStatus(85, EXTENDED_AWAY, loadIcon(iconPath - + "/status16x16-xa.png")); this.unknownStatus = new JabberPresenceStatus(1, UNKNOWN, loadIcon(iconPath