mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-07 05:53:06 +00:00
Handle call repair in CallSession and MediaSession classes.
This commit is contained in:
parent
52f13d34d0
commit
008ec55b37
20 changed files with 358 additions and 259 deletions
|
|
@ -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<LinphoneCore *>(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<LinphonePrivate::CallSession *>(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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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> &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> &core, const CallSessionParams
|
|||
|
||||
CallSession::CallSession (CallSessionPrivate &p, const shared_ptr<Core> &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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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> &call) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool CorePrivate::canWeAddCall () const {
|
||||
L_Q();
|
||||
if (q->getCallCount() < static_cast<unsigned int>(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<CallSession *>(replacedOp->get_user_pointer());
|
||||
for (const auto &call : calls) {
|
||||
shared_ptr<CallSession> 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<unsigned int>(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;
|
||||
|
|
|
|||
39
src/core/core-listener.h
Normal file
39
src/core/core-listener.h
Normal file
|
|
@ -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_
|
||||
|
|
@ -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> &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> mainDb;
|
||||
|
||||
private:
|
||||
std::list<CoreListener *> listeners;
|
||||
|
||||
std::list<std::shared_ptr<Call>> calls;
|
||||
std::shared_ptr<Call> currentCall;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
#include <mediastreamer2/mscommon.h>
|
||||
|
||||
#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) {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue