diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index d3790bdcb..28bfcbd3b 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -54,34 +54,11 @@ using namespace LinphonePrivate; static void register_failure(SalOp *op); -static LinphoneCall * look_for_broken_call_to_replace(LinphonePrivate::SalOp *h, LinphoneCore *lc) { - const bctbx_list_t *calls = linphone_core_get_calls(lc); - const bctbx_list_t *it = calls; - while (it != NULL) { -#if 0 - LinphoneCall *replaced_call = NULL; - LinphoneCall *call = (LinphoneCall *)bctbx_list_get_data(it); - SalOp *replaced_op = sal_call_get_replaces(h); - if (replaced_op) replaced_call = (LinphoneCall*)sal_op_get_user_pointer(replaced_op); - if ((call->broken && sal_call_compare_op(h, call->op)) - || ((replaced_call == call) && (strcmp(sal_op_get_from(h), sal_op_get_from(replaced_op)) == 0) && (strcmp(sal_op_get_to(h), sal_op_get_to(replaced_op)) == 0))) { - return call; - } -#endif - it = bctbx_list_next(it); - } - - return NULL; -} - static void call_received(SalCallOp *h) { /* Look if this INVITE is for a call that has already been notified but broken because of network failure */ LinphoneCore *lc = reinterpret_cast(h->get_sal()->get_user_pointer()); - LinphoneCall *replacedCall = look_for_broken_call_to_replace(h, lc); - if (replacedCall) { - linphone_call_replace_op(replacedCall, h); + if (L_GET_PRIVATE_FROM_C_OBJECT(lc)->inviteReplacesABrokenCall(h)) return; - } LinphoneAddress *fromAddr = nullptr; const char *pAssertedId = sal_custom_header_find(h->get_recv_custom_header(), "P-Asserted-Identity"); @@ -314,13 +291,12 @@ static void call_released(SalOp *op) { } static void call_cancel_done(SalOp *op) { -#if 0 - LinphoneCall *call = (LinphoneCall *)sal_op_get_user_pointer(op); - if (call->reinvite_on_cancel_response_requested == TRUE) { - call->reinvite_on_cancel_response_requested = FALSE; - linphone_call_reinvite_to_recover_from_connection_loss(call); + LinphonePrivate::CallSession *session = reinterpret_cast(op->get_user_pointer()); + if (!session) { + ms_warning("Cancel done reported on already terminated CallSession"); + return; } -#endif + L_GET_PRIVATE(session)->cancelDone(); } static void auth_failure(SalOp *op, SalAuthInfo* info) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 0cc6b5c37..bc45e07e2 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6140,39 +6140,15 @@ static void set_sip_network_reachable(LinphoneCore* lc,bool_t is_sip_reachable, if (!lc->sip_network_reachable){ linphone_core_invalidate_friend_subscriptions(lc); lc->sal->reset_transports(); - /*mark all calls as broken, so that they can be either dropped immediately or restaured when network will be back*/ -#if 0 - bctbx_list_for_each(lc->calls, (MSIterateFunc) linphone_call_set_broken); -#endif } } -void linphone_core_repair_calls(LinphoneCore *lc){ -#if 0 - if (lc->calls && lp_config_get_int(lc->config, "sip", "repair_broken_calls", 1) && lc->media_network_reachable){ - /*if we are registered and there were broken calls due to a past network disconnection, attempt to repair them*/ - bctbx_list_for_each(lc->calls, (MSIterateFunc) linphone_call_repair_if_broken); - } -#endif -} - static void set_media_network_reachable(LinphoneCore* lc, bool_t is_media_reachable){ if (lc->media_network_reachable==is_media_reachable) return; // no change, ignore. ms_message("Media network reachability state is now [%s]",is_media_reachable?"UP":"DOWN"); lc->media_network_reachable=is_media_reachable; - if (!lc->media_network_reachable){ - /*mark all calls as broken, so that they can be either dropped immediately or restaured when network will be back*/ -#if 0 - bctbx_list_for_each(lc->calls, (MSIterateFunc) linphone_call_set_broken); -#endif - }else{ - if (lp_config_get_int(lc->config, "net", "recreate_sockets_when_network_is_up", 0)){ -#if 0 - bctbx_list_for_each(lc->calls, (MSIterateFunc)linphone_call_refresh_sockets); -#endif - } - linphone_core_repair_calls(lc); + if (lc->media_network_reachable){ if (lc->bw_controller){ ms_bandwidth_controller_reset_state(lc->bw_controller); } diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index fcc3c6911..bb1c98d14 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -54,9 +54,6 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress * to); void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state); LinphonePlayer *linphone_call_build_player(LinphoneCall*call); -void linphone_call_refresh_sockets(LinphoneCall *call); -void linphone_call_replace_op(LinphoneCall *call, LinphonePrivate::SalOp *op); -void linphone_call_reinvite_to_recover_from_connection_loss(LinphoneCall *call); LinphonePrivate::SalCallOp *linphone_call_get_op(const LinphoneCall *call); LinphoneProxyConfig * linphone_call_get_dest_proxy(const LinphoneCall *call); @@ -335,9 +332,6 @@ LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc); void ec_calibrator_destroy(EcCalibrator *ecc); -void linphone_call_set_broken(LinphoneCall *call); -void linphone_call_repair_if_broken(LinphoneCall *call); -void linphone_core_repair_calls(LinphoneCore *lc); int linphone_core_preempt_sound_resources(LinphoneCore *lc); int _linphone_call_pause(LinphoneCall *call); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 134c2bdf1..c93b02240 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -1310,10 +1310,8 @@ void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrat cfg->state=state; } - if (lc){ + if (lc) linphone_core_notify_registration_state_changed(lc,cfg,state,message); - linphone_core_repair_calls(lc); - } } else { /*state already reported*/ } diff --git a/coreapi/vtables.c b/coreapi/vtables.c index aef7785d9..2088a8296 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -18,6 +18,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "c-wrapper/c-wrapper.h" +#include "core/core-p.h" + #include "private.h" #include "linphone/wrapper_utils.h" @@ -100,6 +103,7 @@ void linphone_core_notify_call_encryption_changed(LinphoneCore *lc, LinphoneCall } void linphone_core_notify_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message){ + L_GET_PRIVATE_FROM_C_OBJECT(lc)->notifyRegistrationStateChanged(cfg, cstate, message); NOTIFY_IF_EXIST(registration_state_changed, lc,cfg,cstate,message); cleanup_dead_vtable_refs(lc); } @@ -239,6 +243,7 @@ void linphone_core_notify_configuring_status(LinphoneCore *lc, LinphoneConfiguri } void linphone_core_notify_network_reachable(LinphoneCore *lc, bool_t reachable) { + L_GET_PRIVATE_FROM_C_OBJECT(lc)->notifyNetworkReachable(!!reachable); NOTIFY_IF_EXIST(network_reachable, lc,reachable); cleanup_dead_vtable_refs(lc); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a901d93a3..d68c0435a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES content/file-content.h content/file-transfer-content.h core/core-accessor.h + core/core-listener.h core/core-p.h core/core.h core/paths/paths.h diff --git a/src/c-wrapper/api/c-call.cpp b/src/c-wrapper/api/c-call.cpp index 274f7e97f..0ff3d58a4 100644 --- a/src/c-wrapper/api/c-call.cpp +++ b/src/c-wrapper/api/c-call.cpp @@ -319,188 +319,15 @@ int linphone_call_start_invite (LinphoneCall *call, const LinphoneAddress *desti return 0; } -void linphone_call_replace_op (LinphoneCall *call, LinphonePrivate::SalOp *op) { -#if 0 - SalOp *oldop = call->op; - LinphoneCallState oldstate = linphone_call_get_state(call); - call->op = op; - sal_op_set_user_pointer(call->op, call); - sal_call_set_local_media_description(call->op, call->localdesc); - switch (linphone_call_get_state(call)) { - case LinphoneCallIncomingEarlyMedia: - case LinphoneCallIncomingReceived: - sal_call_notify_ringing(call->op, (linphone_call_get_state(call) == LinphoneCallIncomingEarlyMedia) ? TRUE : FALSE); - break; - case LinphoneCallConnected: - case LinphoneCallStreamsRunning: - sal_call_accept(call->op); - break; - default: - ms_warning("linphone_call_replace_op(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); - break; - } - switch (oldstate) { - case LinphoneCallIncomingEarlyMedia: - case LinphoneCallIncomingReceived: - sal_op_set_user_pointer(oldop, nullptr); /* To make the call does not get terminated by terminating this op. */ - /* Do not terminate a forked INVITE */ - if (sal_call_get_replaces(op)) { - sal_call_terminate(oldop); - } else { - sal_op_kill_dialog(oldop); - } - break; - case LinphoneCallConnected: - case LinphoneCallStreamsRunning: - sal_call_terminate(oldop); - sal_op_kill_dialog(oldop); - break; - default: - break; - } - sal_op_release(oldop); -#endif -} - // ============================================================================= // Private functions. // ============================================================================= -#if 0 -static void linphone_call_repair_by_invite_with_replaces (LinphoneCall *call) { - const char *call_id = sal_op_get_call_id(call->op); - const char *from_tag = sal_call_get_local_tag(call->op); - const char *to_tag = sal_call_get_remote_tag(call->op); - sal_op_kill_dialog(call->op); - linphone_call_create_op(call); - sal_call_set_replaces(call->op, call_id, from_tag, to_tag); - linphone_call_start_invite(call, nullptr); -} -#endif - MediaStream *linphone_call_get_stream (LinphoneCall *call, LinphoneStreamType type) { return L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(type); } -void linphone_call_set_broken (LinphoneCall *call) { -#if 0 - switch(call->state){ - /*for all the early states, we prefer to drop the call*/ - case LinphoneCallOutgoingInit: - case LinphoneCallOutgoingProgress: - case LinphoneCallOutgoingRinging: - case LinphoneCallOutgoingEarlyMedia: - case LinphoneCallIncomingReceived: - case LinphoneCallIncomingEarlyMedia: - /*during the early states, the SAL layer reports the failure from the dialog or transaction layer, - * hence, there is nothing special to do*/ - //break; - case LinphoneCallStreamsRunning: - case LinphoneCallUpdating: - case LinphoneCallPausing: - case LinphoneCallResuming: - case LinphoneCallPaused: - case LinphoneCallPausedByRemote: - case LinphoneCallUpdatedByRemote: - /*during these states, the dialog is established. A failure of a transaction is not expected to close it. - * Instead we have to repair the dialog by sending a reINVITE*/ - call->broken = TRUE; - call->need_localip_refresh = TRUE; - break; - default: - ms_error("linphone_call_set_broken() unimplemented case."); - break; - } -#endif -} - -void linphone_call_reinvite_to_recover_from_connection_loss (LinphoneCall *call) { -#if 0 - LinphoneCallParams *params; - ms_message("LinphoneCall[%p] is going to be updated (reINVITE) in order to recover from lost connectivity", call); - if (call->ice_session){ - ice_session_reset(call->ice_session, IR_Controlling); - } - params = linphone_core_create_call_params(call->core, call); - linphone_call_update(call, params); - linphone_call_params_unref(params); -#endif -} - -void linphone_call_repair_if_broken (LinphoneCall *call) { -#if 0 - SalErrorInfo sei; - if (!call->broken) return; - if (!call->core->media_network_reachable) return; - - memset(&sei, 0, sizeof(sei)); - - /*Make sure that the proxy from which we received this call, or to which we routed this call is registered first*/ - if (call->dest_proxy){ - /*in all other cases, ie no proxy config, or a proxy config for which no registration was requested, we can start the - * call repair immediately.*/ - if (linphone_proxy_config_register_enabled(call->dest_proxy) - && linphone_proxy_config_get_state(call->dest_proxy) != LinphoneRegistrationOk) return; - } - - switch (call->state){ - case LinphoneCallUpdating: - case LinphoneCallPausing: - if (sal_call_dialog_request_pending(call->op)) { - /* Need to cancel first re-INVITE as described in section 5.5 of RFC 6141 */ - sal_call_cancel_invite(call->op); - call->reinvite_on_cancel_response_requested = TRUE; - } - break; - case LinphoneCallStreamsRunning: - case LinphoneCallPaused: - case LinphoneCallPausedByRemote: - if (!sal_call_dialog_request_pending(call->op)) { - linphone_call_reinvite_to_recover_from_connection_loss(call); - } - break; - case LinphoneCallUpdatedByRemote: - if (sal_call_dialog_request_pending(call->op)) { - sal_error_info_set(&sei, SalReasonServiceUnavailable,"SIP", 0, nullptr, nullptr); - sal_call_decline_with_error_info(call->op, &sei,nullptr); - } - linphone_call_reinvite_to_recover_from_connection_loss(call); - break; - case LinphoneCallOutgoingInit: - case LinphoneCallOutgoingProgress: - sal_call_cancel_invite(call->op); - call->reinvite_on_cancel_response_requested = TRUE; - break; - case LinphoneCallOutgoingEarlyMedia: - case LinphoneCallOutgoingRinging: - linphone_call_repair_by_invite_with_replaces(call); - break; - case LinphoneCallIncomingEarlyMedia: - case LinphoneCallIncomingReceived: - /* Keep the call broken until a forked INVITE is received from the server. */ - break; - default: - ms_warning("linphone_call_repair_if_broken(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); - call->broken = FALSE; - break; - } - sal_error_info_reset(&sei); -#endif -} - -void linphone_call_refresh_sockets (LinphoneCall *call) { -#if 0 - int i; - for (i=0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i){ - MSMediaStreamSessions *mss = &call->sessions[i]; - if (mss->rtp_session){ - rtp_session_refresh_sockets(mss->rtp_session); - } - } -#endif -} - LinphonePrivate::SalCallOp * linphone_call_get_op (const LinphoneCall *call) { return L_GET_PRIVATE_FROM_C_OBJECT(call)->getOp(); } diff --git a/src/conference/session/call-session-p.h b/src/conference/session/call-session-p.h index e8d8c2f73..5a84788c8 100644 --- a/src/conference/session/call-session-p.h +++ b/src/conference/session/call-session-p.h @@ -29,7 +29,7 @@ LINPHONE_BEGIN_NAMESPACE -class CallSessionPrivate : public ObjectPrivate { +class CallSessionPrivate : public ObjectPrivate, public CoreListener { public: CallSessionPrivate () = default; @@ -43,16 +43,19 @@ public: CallSessionParams *getCurrentParams () const { return currentParams; } LinphoneProxyConfig * getDestProxy () const { return destProxy; } SalCallOp * getOp () const { return op; } + bool isBroken () const { return broken; } void setParams (CallSessionParams *csp); virtual void abort (const std::string &errorMsg); virtual void accepted (); void ackBeingSent (LinphoneHeaders *headers); virtual void ackReceived (LinphoneHeaders *headers); + void cancelDone (); virtual bool failure (); void infoReceived (SalBodyHandler *bodyHandler); void pingReply (); virtual void remoteRinging (); + void replaceOp (SalCallOp *newOp); virtual void terminated (); void updated (bool isUpdate); void updatedByRemote (); @@ -77,6 +80,10 @@ protected: void setContactOp (); + // CoreListener + void onNetworkReachable (bool reachable) override; + void onRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const std::string &message) override; + private: void completeLog (); void createOp (); @@ -84,6 +91,10 @@ private: LinphoneAddress * getFixedContact () const; + virtual void reinviteToRecoverFromConnectionLoss (); + void repairByInviteWithReplaces (); + void repairIfBroken (); + protected: CallSessionListener *listener = nullptr; @@ -107,9 +118,12 @@ protected: bool pingReplied = false; int pingTime = 0; + bool broken = false; bool deferIncomingNotification = false; bool deferUpdate = false; + bool needLocalIpRefresh = false; bool nonOpError = false; /* Set when the LinphoneErrorInfo was set at higher level than sal */ + bool reinviteOnCancelResponseRequested = false; private: L_DECLARE_PUBLIC(CallSession); diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index aaa317a27..efcb27718 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -26,7 +26,7 @@ #include "conference/params/call-session-params-p.h" #include "conference/session/call-session-p.h" #include "conference/session/call-session.h" -#include "core/core.h" +#include "core/core-p.h" #include "logger/logger.h" @@ -241,6 +241,13 @@ void CallSessionPrivate::ackReceived (LinphoneHeaders *headers) { listener->onAckReceived(q->getSharedFromThis(), headers); } +void CallSessionPrivate::cancelDone () { + if (reinviteOnCancelResponseRequested) { + reinviteOnCancelResponseRequested = false; + reinviteToRecoverFromConnectionLoss(); + } +} + bool CallSessionPrivate::failure () { L_Q(); const SalErrorInfo *ei = op->get_error_info(); @@ -338,6 +345,47 @@ void CallSessionPrivate::remoteRinging () { setState(LinphoneCallOutgoingRinging, "Remote ringing"); } +void CallSessionPrivate::replaceOp (SalCallOp *newOp) { + L_Q(); + SalCallOp *oldOp = op; + LinphoneCallState oldState = state; + op = newOp; + op->set_user_pointer(q); + op->set_local_media_description(oldOp->get_local_media_description()); + switch (state) { + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallIncomingReceived: + op->notify_ringing((state == LinphoneCallIncomingEarlyMedia) ? true : false); + break; + case LinphoneCallConnected: + case LinphoneCallStreamsRunning: + op->accept(); + break; + default: + lWarning() << "CallSessionPrivate::replaceOp(): don't know what to do in state [" << linphone_call_state_to_string(state) << "]"; + break; + } + switch (oldState) { + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallIncomingReceived: + op->set_user_pointer(nullptr); // In order for the call session to not get terminated by terminating this op + // Do not terminate a forked INVITE + if (op->get_replaces()) + oldOp->terminate(); + else + oldOp->kill_dialog(); + break; + case LinphoneCallConnected: + case LinphoneCallStreamsRunning: + oldOp->terminate(); + oldOp->kill_dialog(); + break; + default: + break; + } + oldOp->release(); +} + void CallSessionPrivate::terminated () { switch (state) { case LinphoneCallEnd: @@ -640,6 +688,45 @@ void CallSessionPrivate::setContactOp () { // ----------------------------------------------------------------------------- +void CallSessionPrivate::onNetworkReachable (bool reachable) { + if (reachable) { + repairIfBroken(); + } else { + switch(state) { + // For all the early states, we prefer to drop the call + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingEarlyMedia: + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + // During the early states, the SAL layer reports the failure from the dialog or transaction layer, + // hence, there is nothing special to do + case LinphoneCallStreamsRunning: + case LinphoneCallUpdating: + case LinphoneCallPausing: + case LinphoneCallResuming: + case LinphoneCallPaused: + case LinphoneCallPausedByRemote: + case LinphoneCallUpdatedByRemote: + // During these states, the dialog is established. A failure of a transaction is not expected to close it. + // Instead we have to repair the dialog by sending a reINVITE + broken = true; + needLocalIpRefresh = true; + break; + default: + lError() << "CallSessionPrivate::onNetworkReachable(): unimplemented case"; + break; + } + } +} + +void CallSessionPrivate::onRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const std::string &message) { + repairIfBroken(); +} + +// ----------------------------------------------------------------------------- + void CallSessionPrivate::completeLog () { L_Q(); log->duration = computeDuration(); /* Store duration since connected */ @@ -699,11 +786,96 @@ LinphoneAddress * CallSessionPrivate::getFixedContact () const { return result; } +// ----------------------------------------------------------------------------- + +void CallSessionPrivate::reinviteToRecoverFromConnectionLoss () { + L_Q(); + lInfo() << "CallSession [" << q << "] is going to be updated (reINVITE) in order to recover from lost connectivity"; + q->update(params); +} + +void CallSessionPrivate::repairByInviteWithReplaces () { + L_Q(); + const char *callId = op->get_call_id(); + const char *fromTag = op->get_local_tag(); + const char *toTag = op->get_remote_tag(); + op->kill_dialog(); + createOp(); + op->set_replaces(callId, fromTag, toTag); + q->startInvite(nullptr); +} + +void CallSessionPrivate::repairIfBroken () { + L_Q(); + LinphoneCore *lc = q->getCore()->getCCore(); + LinphoneConfig *config = linphone_core_get_config(lc); + if (!lp_config_get_int(config, "sip", "repair_broken_calls", 1) || !lc->media_network_reachable || !broken) + return; + + // If we are registered and this session has been broken due to a past network disconnection, + // attempt to repair it + + // Make sure that the proxy from which we received this call, or to which we routed this call is registered first + if (destProxy) { + // In all other cases, ie no proxy config, or a proxy config for which no registration was requested, + // we can start the call session repair immediately. + if (linphone_proxy_config_register_enabled(destProxy) + && (linphone_proxy_config_get_state(destProxy) != LinphoneRegistrationOk)) + return; + } + + SalErrorInfo sei; + memset(&sei, 0, sizeof(sei)); + switch (state) { + case LinphoneCallUpdating: + case LinphoneCallPausing: + if (op->dialog_request_pending()) { + // Need to cancel first re-INVITE as described in section 5.5 of RFC 6141 + op->cancel_invite(); + reinviteOnCancelResponseRequested = true; + } + break; + case LinphoneCallStreamsRunning: + case LinphoneCallPaused: + case LinphoneCallPausedByRemote: + if (!op->dialog_request_pending()) + reinviteToRecoverFromConnectionLoss(); + break; + case LinphoneCallUpdatedByRemote: + if (op->dialog_request_pending()) { + sal_error_info_set(&sei, SalReasonServiceUnavailable, "SIP", 0, nullptr, nullptr); + op->decline_with_error_info(&sei, nullptr); + } + reinviteToRecoverFromConnectionLoss(); + break; + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + op->cancel_invite(); + reinviteOnCancelResponseRequested = true; + break; + case LinphoneCallOutgoingEarlyMedia: + case LinphoneCallOutgoingRinging: + repairByInviteWithReplaces(); + break; + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallIncomingReceived: + // Keep the call broken until a forked INVITE is received from the server + break; + default: + lWarning() << "CallSessionPrivate::repairIfBroken: don't know what to do in state [" + << linphone_call_state_to_string(state); + broken = false; + break; + } + sal_error_info_reset(&sei); +} + // ============================================================================= CallSession::CallSession (const shared_ptr &core, const CallSessionParams *params, CallSessionListener *listener) : Object(*new CallSessionPrivate), CoreAccessor(core) { L_D(); + getCore()->getPrivate()->registerListener(d); d->listener = listener; if (params) d->setParams(new CallSessionParams(*params)); @@ -713,11 +885,13 @@ CallSession::CallSession (const shared_ptr &core, const CallSessionParams CallSession::CallSession (CallSessionPrivate &p, const shared_ptr &core) : Object(p), CoreAccessor(core) { L_D(); + getCore()->getPrivate()->registerListener(d); d->init(); } CallSession::~CallSession () { L_D(); + getCore()->getPrivate()->unregisterListener(d); if (d->currentParams) delete d->currentParams; if (d->params) diff --git a/src/conference/session/call-session.h b/src/conference/session/call-session.h index 7062f0ec1..d1e5ab231 100644 --- a/src/conference/session/call-session.h +++ b/src/conference/session/call-session.h @@ -25,6 +25,7 @@ #include "conference/conference.h" #include "conference/params/call-session-params.h" #include "conference/session/call-session-listener.h" +#include "core/core-listener.h" #include "sal/call-op.h" // ============================================================================= @@ -41,6 +42,7 @@ class LINPHONE_PUBLIC CallSession : public Object, public CoreAccessor { friend class ClientGroupChatRoom; friend class ClientGroupChatRoomPrivate; friend class Conference; + friend class CorePrivate; friend class ServerGroupChatRoom; friend class ServerGroupChatRoomPrivate; diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h index 5e19fa222..b0d9d942a 100644 --- a/src/conference/session/media-session-p.h +++ b/src/conference/session/media-session-p.h @@ -87,6 +87,9 @@ public: SalCallOp * getOp () const { return op; } void setAudioMuted (bool value) { audioMuted = value; } + // CoreListener + void onNetworkReachable (bool reachable) override; + private: static OrtpJitterBufferAlgorithm jitterBufferNameToAlgo (const std::string &name); @@ -235,6 +238,9 @@ private: void accept (const MediaSessionParams *params); LinphoneStatus acceptUpdate (const CallSessionParams *csp, LinphoneCallState nextState, const std::string &stateInfo) override; + void refreshSockets (); + void reinviteToRecoverFromConnectionLoss () override; + #ifdef VIDEO_ENABLED void videoStreamEventCb (const MSFilter *f, const unsigned int eventId, const void *args); #endif // ifdef VIDEO_ENABLED diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 59a7accc3..8b8ed3962 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -539,6 +539,18 @@ int MediaSessionPrivate::getStreamIndex (MediaStream *ms) const { // ----------------------------------------------------------------------------- +void MediaSessionPrivate::onNetworkReachable (bool reachable) { + L_Q(); + if (reachable) { + LinphoneConfig *config = linphone_core_get_config(q->getCore()->getCCore()); + if (lp_config_get_int(config, "net", "recreate_sockets_when_network_is_up", 0)) + refreshSockets(); + } + CallSessionPrivate::onNetworkReachable(reachable); +} + +// ----------------------------------------------------------------------------- + OrtpJitterBufferAlgorithm MediaSessionPrivate::jitterBufferNameToAlgo (const string &name) { if (name == "basic") return OrtpJitterBufferBasic; if (name == "rls") return OrtpJitterBufferRecursiveLeastSquare; @@ -3693,9 +3705,7 @@ LinphoneStatus MediaSessionPrivate::pause () { lError() << "No reason to pause this call, it is already paused or inactive"; return -1; } -#if 0 - call->broken = FALSE; -#endif + broken = false; setState(LinphoneCallPausing, "Pausing call"); makeLocalMediaDescription(); op->set_local_media_description(localDesc); @@ -3945,6 +3955,24 @@ LinphoneStatus MediaSessionPrivate::acceptUpdate (const CallSessionParams *csp, // ----------------------------------------------------------------------------- +void MediaSessionPrivate::refreshSockets () { + for (int i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + MSMediaStreamSessions *mss = &sessions[i]; + if (mss->rtp_session) + rtp_session_refresh_sockets(mss->rtp_session); + } +} + +void MediaSessionPrivate::reinviteToRecoverFromConnectionLoss () { + L_Q(); + lInfo() << "MediaSession [" << q << "] is going to be updated (reINVITE) in order to recover from lost connectivity"; + if (iceAgent->hasSession()) + iceAgent->resetSession(IR_Controlling); + q->update(getParams()); +} + +// ----------------------------------------------------------------------------- + #ifdef VIDEO_ENABLED void MediaSessionPrivate::videoStreamEventCb (const MSFilter *f, const unsigned int eventId, const void *args) { L_Q(); @@ -4261,9 +4289,7 @@ LinphoneStatus MediaSession::resume () { lInfo() << "Resuming MediaSession " << this; } d->automaticallyPaused = false; -#if 0 - call->broken = FALSE; -#endif + d->broken = false; /* Stop playing music immediately. If remote side is a conference it * prevents the participants to hear it while the 200OK comes back. */ if (d->audioStream) @@ -4389,9 +4415,7 @@ LinphoneStatus MediaSession::update (const MediaSessionParams *msp, const string lWarning() << "CallSession::update() is given the current params, this is probably not what you intend to do!"; d->iceAgent->checkSession(IR_Controlling, true); if (msp) { -#if 0 - call->broken = FALSE; -#endif + d->broken = false; d->setState(nextState, "Updating call"); d->setParams(new MediaSessionParams(*msp)); if (d->iceAgent->prepare(d->localDesc, false)) { diff --git a/src/core/core-call.cpp b/src/core/core-call.cpp index 3dd816c73..745577cd1 100644 --- a/src/core/core-call.cpp +++ b/src/core/core-call.cpp @@ -21,6 +21,7 @@ #include "core-p.h" #include "call/call-p.h" +#include "conference/session/call-session-p.h" #include "logger/logger.h" // TODO: Remove me later. @@ -44,6 +45,35 @@ int CorePrivate::addCall (const shared_ptr &call) { return 0; } +bool CorePrivate::canWeAddCall () const { + L_Q(); + if (q->getCallCount() < static_cast(q->getCCore()->max_calls)) + return true; + lInfo() << "Maximum amount of simultaneous calls reached!"; + return false; +} + +bool CorePrivate::inviteReplacesABrokenCall (SalCallOp *op) { + CallSession *replacedSession = nullptr; + SalCallOp *replacedOp = op->get_replaces(); + if (replacedOp) + replacedSession = reinterpret_cast(replacedOp->get_user_pointer()); + for (const auto &call : calls) { + shared_ptr session = call->getPrivate()->getActiveSession(); + if (session + && ((session->getPrivate()->isBroken() && op->compare_op(session->getPrivate()->getOp())) + || ((replacedSession == session.get()) + && (strcmp(op->get_from(), replacedOp->get_from()) == 0) + && (strcmp(op->get_to(), replacedOp->get_to()) == 0))) + ) { + session->getPrivate()->replaceOp(op); + return true; + } + } + + return false; +} + bool CorePrivate::isAlreadyInCallWithAddress (const Address &addr) const { for (const auto &call : calls) { if (call->getRemoteAddress().weakEqual(addr)) @@ -58,14 +88,6 @@ void CorePrivate::iterateCalls (time_t currentRealTime, bool oneSecondElapsed) c } } -bool CorePrivate::canWeAddCall () const { - L_Q(); - if (q->getCallCount() < static_cast(q->getCCore()->max_calls)) - return true; - lInfo() << "Maximum amount of simultaneous calls reached!"; - return false; -} - void CorePrivate::notifySoundcardUsage (bool used) { L_Q(); MSSndCard *card = q->getCCore()->sound_conf.capt_sndcard; diff --git a/src/core/core-listener.h b/src/core/core-listener.h new file mode 100644 index 000000000..789b9e6df --- /dev/null +++ b/src/core/core-listener.h @@ -0,0 +1,39 @@ +/* + * core-listener.h + * Copyright (C) 2010-2017 Belledonne Communications SARL + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CORE_LISTENER_H_ +#define _CORE_LISTENER_H_ + +#include "linphone/types.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class CoreListener { +public: + virtual ~CoreListener () = default; + + virtual void onNetworkReachable (bool reachable) {} + virtual void onRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const std::string &message) {} +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _CORE_LISTENER_H_ diff --git a/src/core/core-p.h b/src/core/core-p.h index d5cec3542..e0904c757 100644 --- a/src/core/core-p.h +++ b/src/core/core-p.h @@ -23,19 +23,28 @@ #include "core.h" #include "db/main-db.h" #include "object/object-p.h" +#include "sal/call-op.h" // ============================================================================= LINPHONE_BEGIN_NAMESPACE +class CoreListener; + class CorePrivate : public ObjectPrivate { public: - void init(); - void uninit(); + void init (); + void registerListener (CoreListener *listener); + void unregisterListener (CoreListener *listener); + void uninit (); + + void notifyNetworkReachable (bool reachable); + void notifyRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const std::string &message); int addCall (const std::shared_ptr &call); bool canWeAddCall () const; bool hasCalls () const { return !calls.empty(); } + bool inviteReplacesABrokenCall (SalCallOp *op); bool isAlreadyInCallWithAddress (const Address &addr) const; void iterateCalls (time_t currentRealTime, bool oneSecondElapsed) const; void notifySoundcardUsage (bool used); @@ -50,6 +59,8 @@ public: std::unique_ptr mainDb; private: + std::list listeners; + std::list> calls; std::shared_ptr currentCall; diff --git a/src/core/core.cpp b/src/core/core.cpp index 8977573dd..035b56f7c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -20,7 +20,8 @@ #include #include "call/call.h" -#include "core-p.h" +#include "core/core-listener.h" +#include "core/core-p.h" #include "logger/logger.h" #include "paths/paths.h" @@ -59,6 +60,14 @@ void CorePrivate::init () { insertChatRoom(chatRoom); } +void CorePrivate::registerListener (CoreListener *listener) { + listeners.push_back(listener); +} + +void CorePrivate::unregisterListener (CoreListener *listener) { + listeners.remove(listener); +} + void CorePrivate::uninit () { L_Q(); while (!calls.empty()) { @@ -68,6 +77,18 @@ void CorePrivate::uninit () { } } +// ----------------------------------------------------------------------------- + +void CorePrivate::notifyNetworkReachable (bool reachable) { + for (const auto &listener : listeners) + listener->onNetworkReachable(reachable); +} + +void CorePrivate::notifyRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const string &message) { + for (const auto &listener : listeners) + listener->onRegistrationStateChanged(cfg, state, message); +} + // ============================================================================= Core::Core () : Object(*new CorePrivate) {} diff --git a/src/core/core.h b/src/core/core.h index 76f6ccb2f..cf82c334c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -41,6 +41,7 @@ class IdentityAddress; class LINPHONE_PUBLIC Core : public Object { friend class CallPrivate; + friend class CallSession; friend class ChatMessagePrivate; friend class ChatRoom; friend class ChatRoomPrivate; diff --git a/src/nat/ice-agent.cpp b/src/nat/ice-agent.cpp index d94aa46b3..9aef0281f 100644 --- a/src/nat/ice-agent.cpp +++ b/src/nat/ice-agent.cpp @@ -177,6 +177,12 @@ void IceAgent::prepareIceForStream (MediaStream *ms, bool createChecklist) { media_stream_set_ice_check_list(ms, cl); } +void IceAgent::resetSession (IceRole role) { + if (!iceSession) + return; + ice_session_reset(iceSession, role); +} + void IceAgent::restartSession (IceRole role) { if (!iceSession) return; diff --git a/src/nat/ice-agent.h b/src/nat/ice-agent.h index 42c293312..5d69ca864 100644 --- a/src/nat/ice-agent.h +++ b/src/nat/ice-agent.h @@ -59,6 +59,7 @@ public: bool isControlling () const; bool prepare (const SalMediaDescription *localDesc, bool incomingOffer); void prepareIceForStream (MediaStream *ms, bool createChecklist); + void resetSession (IceRole role); void restartSession (IceRole role); void startConnectivityChecks (); void stopIceForInactiveStreams (SalMediaDescription *desc); diff --git a/src/sal/call-op.h b/src/sal/call-op.h index 1144d78d3..80acdf958 100644 --- a/src/sal/call-op.h +++ b/src/sal/call-op.h @@ -30,6 +30,7 @@ public: SalCallOp(Sal *sal): SalOp(sal) {} ~SalCallOp() override; + SalMediaDescription *get_local_media_description () const { return local_media; } int set_local_media_description(SalMediaDescription *desc); int set_local_body(const Content &body); int set_local_body(const Content &&body);