From 6c7c56271a261af359a587a06916d8309f47003f Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Sat, 17 Mar 2018 12:05:49 +0100 Subject: [PATCH] Implement deferred ICE reinvite response when the ICE session isn't yet completed. --- src/conference/session/call-session-p.h | 1 + src/conference/session/call-session.cpp | 7 +++--- src/conference/session/media-session-p.h | 2 +- src/conference/session/media-session.cpp | 26 +++++++++++++++++++- src/nat/ice-agent.cpp | 31 ++++++++++++++++++++++++ src/nat/ice-agent.h | 5 ++++ 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/conference/session/call-session-p.h b/src/conference/session/call-session-p.h index fc62e4e43..d8dbed101 100644 --- a/src/conference/session/call-session-p.h +++ b/src/conference/session/call-session-p.h @@ -134,6 +134,7 @@ protected: bool broken = false; bool deferIncomingNotification = false; bool deferUpdate = false; + bool deferUpdateInternal = false; bool needLocalIpRefresh = false; bool nonOpError = false; /* Set when the LinphoneErrorInfo was set at higher level than sal */ bool notifyRinging = true; diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index 2aad40f1a..2b6d0da8d 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -467,9 +467,10 @@ void CallSessionPrivate::updated (bool isUpdate) { void CallSessionPrivate::updatedByRemote () { L_Q(); setState(CallSession::State::UpdatedByRemote,"Call updated by remote"); - if (deferUpdate) { - if (state == CallSession::State::UpdatedByRemote) - lInfo() << "CallSession [" << q << "]: UpdatedByRemoted was signaled but defered. LinphoneCore expects the application to call CallSession::acceptUpdate() later"; + if (deferUpdate || deferUpdateInternal) { + if (state == CallSession::State::UpdatedByRemote && !deferUpdateInternal){ + lInfo() << "CallSession [" << q << "]: UpdatedByRemoted was signaled but defered. LinphoneCore expects the application to call linphone_call_accept_update() later"; + } } else { if (state == CallSession::State::UpdatedByRemote) q->acceptUpdate(nullptr); diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h index 7b014e138..7013e7b18 100644 --- a/src/conference/session/media-session-p.h +++ b/src/conference/session/media-session-p.h @@ -254,7 +254,6 @@ private: int sendDtmf (); void stunAuthRequestedCb (const char *realm, const char *nonce, const char **username, const char **password, const char **ha1); - private: static const std::string ecStateStore; static const int ecStateMaxLen; @@ -325,6 +324,7 @@ private: bool automaticallyPaused = false; bool pausedByApp = false; bool recordActive = false; + bool incomingIceReinvitePending = false; std::string onHoldFile; diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 32ae463c6..c240343f7 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -382,6 +382,8 @@ void MediaSessionPrivate::updated (bool isUpdate) { CallSessionPrivate::updated(isUpdate); } + + void MediaSessionPrivate::updating (bool isUpdate) { L_Q(); SalMediaDescription *rmd = op->get_remote_media_description(); @@ -702,6 +704,10 @@ shared_ptr MediaSessionPrivate::getMe () const { void MediaSessionPrivate::setState (CallSession::State newState, const string &message) { L_Q(); + SalMediaDescription *rmd; + + lInfo()<<"MediaSessionPrivate::setState"; + /* Take a ref on the session otherwise it might get destroyed during the call to setState */ shared_ptr sessionRef = q->getSharedFromThis(); if ((newState != state) && (newState != CallSession::State::StreamsRunning)) @@ -709,6 +715,21 @@ void MediaSessionPrivate::setState (CallSession::State newState, const string &m CallSessionPrivate::setState(newState, message); if (listener) listener->onCallSessionStateChangedForReporting(q->getSharedFromThis()); + switch(newState){ + case CallSession::State::UpdatedByRemote: + /*Handle specifically the case of an incoming ICE-concluded reINVITE*/ + lInfo()<<"Checking for ICE reINVITE"; + rmd = op->get_remote_media_description(); + if (iceAgent && rmd != nullptr && iceAgent->checkIceReinviteNeedsDeferedResponse(rmd)){ + deferUpdate = true; + deferUpdateInternal = true; + incomingIceReinvitePending = true; + lInfo()<<"CallSession [" << q << "]: ICE reinvite received, but one or more check-lists are not completed. Response will be sent later, when ICE has completed"; + } + break; + default: + break; + } } // ----------------------------------------------------------------------------- @@ -2191,11 +2212,14 @@ void MediaSessionPrivate::handleIceEvents (OrtpEvent *ev) { OrtpEventData *evd = ortp_event_get_data(ev); if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) { if (iceAgent->hasCompletedCheckList()) { - /* At least one ICE session has succeeded, so perform a call update */ + /* The ICE session has succeeded, so perform a call update */ if (iceAgent->isControlling() && q->getCurrentParams()->getPrivate()->getUpdateCallWhenIceCompleted()) { MediaSessionParams newParams(*getParams()); newParams.getPrivate()->setInternalCallUpdate(true); q->update(&newParams); + }else if (!iceAgent->isControlling() && incomingIceReinvitePending){ + q->acceptUpdate(nullptr); + incomingIceReinvitePending = false; } startDtlsOnAllStreams(); } diff --git a/src/nat/ice-agent.cpp b/src/nat/ice-agent.cpp index 4c98340fd..726312f9f 100644 --- a/src/nat/ice-agent.cpp +++ b/src/nat/ice-agent.cpp @@ -738,4 +738,35 @@ void IceAgent::updateIceStateInCallStatsForStream (LinphoneCallStats *stats, Ice } } +bool IceAgent::checkIceReinviteNeedsDeferedResponse(SalMediaDescription *md){ + int i,j; + IceCheckList *cl; + + if (!iceSession) return false; + + if (ice_session_state(iceSession) != IS_Running ) return false; + + for (i = 0; i < md->nb_streams; i++) { + SalStreamDescription *stream = &md->streams[i]; + cl = ice_session_check_list(iceSession, i); + + if (cl==NULL) continue; + if (stream->ice_mismatch == TRUE) { + return false; + } + if (stream->rtp_port == 0) { + continue; + } + + if (ice_check_list_state(cl) != ICL_Running) continue; + + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { + const SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[j]; + if (remote_candidate->addr[0] != '\0') return true; + + } + } + return false; +} + LINPHONE_END_NAMESPACE diff --git a/src/nat/ice-agent.h b/src/nat/ice-agent.h index 3af487df1..0e702010b 100644 --- a/src/nat/ice-agent.h +++ b/src/nat/ice-agent.h @@ -66,6 +66,11 @@ public: void updateFromRemoteMediaDescription (const SalMediaDescription *localDesc, const SalMediaDescription *remoteDesc, bool isOffer); void updateIceStateInCallStats (); void updateLocalMediaDescriptionFromIce (SalMediaDescription *desc); + /* + * Checks if an incoming offer with ICE needs a delayed answer, because the ice session hasn't completed yet with + * connecvity checks. + */ + bool checkIceReinviteNeedsDeferedResponse(SalMediaDescription *md); private: void addLocalIceCandidates (int family, const char *addr, IceCheckList *audioCl, IceCheckList *videoCl, IceCheckList *textCl);