diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index c50223575..c4a95a8d8 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -50,7 +50,7 @@ static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *old return result; } -void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) { +void linphone_core_update_streams_destinations(LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) { SalStreamDescription *new_audiodesc = NULL; SalStreamDescription *new_videodesc = NULL; char *rtp_addr, *rtcp_addr; @@ -124,55 +124,55 @@ void linphone_call_update_frozen_payloads(LinphoneCall *call, SalMediaDescriptio } } -void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state){ - SalMediaDescription *oldmd=call->resultdesc; - int md_changed=0; - +void linphone_call_update_streams(LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state) { + LinphoneCore *lc = linphone_call_get_core(call); + SalMediaDescription *oldmd = call->resultdesc; + int md_changed = 0; if (!((call->state == LinphoneCallIncomingEarlyMedia) && (linphone_core_get_ring_during_incoming_early_media(lc)))) { linphone_core_stop_ringing(lc); } if (!new_md) { - ms_error("linphone_core_update_streams() called with null media description"); + ms_error("linphone_call_update_streams() called with null media description"); return; } linphone_call_update_biggest_desc(call, call->localdesc); sal_media_description_ref(new_md); - call->resultdesc=new_md; - if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){ + call->resultdesc = new_md; + if ((call->audiostream && (call->audiostream->ms.state == MSStreamStarted)) || (call->videostream && (call->videostream->ms.state == MSStreamStarted))) { clear_early_media_destinations(call); - /* we already started media: check if we really need to restart it*/ - if (oldmd){ + /* We already started media: check if we really need to restart it */ + if (oldmd) { md_changed = media_parameters_changed(call, oldmd, new_md); if ((md_changed & ( SAL_MEDIA_DESCRIPTION_CODEC_CHANGED |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED |SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED |SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED - |SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION ))){ + |SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION ))) { ms_message("Media descriptions are different, need to restart the streams."); - } else if ( call->playing_ringbacktone) { + } else if (call->playing_ringbacktone) { ms_message("Playing ringback tone, will restart the streams."); } else { if (md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) { - if (call->all_muted){ + if (call->all_muted) { ms_message("Early media finished, unmuting inputs..."); - /*we were in early media, now we want to enable real media */ + /* We were in early media, now we want to enable real media */ call->all_muted = FALSE; - if (call->audiostream) - linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc)); + if (call->audiostream) linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc)); #ifdef VIDEO_ENABLED - if (call->videostream && call->camera_enabled) + if (call->videostream && call->camera_enabled) { linphone_call_enable_camera(call, linphone_call_camera_enabled(call)); + } #endif } /*FIXME ZRTP, might be restarted in any cases ? */ ms_message("No need to restart streams, SDP is unchanged."); goto end; - }else { + } else { if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED) { ms_message("Network parameters have changed, update them."); - linphone_core_update_streams_destinations(lc, call, oldmd, new_md); + linphone_core_update_streams_destinations(call, oldmd, new_md); } if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) { ms_message("Crypto parameters have changed, update them."); @@ -182,34 +182,35 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia } } } - linphone_call_stop_media_streams (call); - if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED){ - ms_message("Media ip type has changed, destroying sessions context on call [%p]",call); + linphone_call_stop_media_streams(call); + if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED) { + ms_message("Media ip type has changed, destroying sessions context on call [%p]", call); ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); } - linphone_call_init_media_streams (call); + linphone_call_init_media_streams(call); } - if (call->audiostream==NULL){ - /*this happens after pausing the call locally. The streams are destroyed and then we wait the 200Ok to recreate them*/ - linphone_call_init_media_streams (call); + if (call->audiostream == NULL) { + /* This happens after pausing the call locally. The streams are destroyed and then we wait the 200Ok to recreate them */ + linphone_call_init_media_streams(call); } - if (call->params->real_early_media && call->state==LinphoneCallOutgoingEarlyMedia){ + if (call->params->real_early_media && (call->state == LinphoneCallOutgoingEarlyMedia)) { prepare_early_media_forking(call); } linphone_call_start_media_streams(call, target_state); - if (call->state==LinphoneCallPausing && call->paused_by_app && bctbx_list_size(lc->calls)==1){ - linphone_core_play_named_tone(lc,LinphoneToneCallOnHold); + if ((call->state == LinphoneCallPausing) && call->paused_by_app && (bctbx_list_size(lc->calls) == 1)) { + linphone_core_play_named_tone(lc, LinphoneToneCallOnHold); } linphone_call_update_frozen_payloads(call, new_md); - end: - if (oldmd) - sal_media_description_unref(oldmd); + +end: + if (oldmd) sal_media_description_unref(oldmd); } + #if 0 static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){ bctbx_list_t *elem; @@ -476,7 +477,7 @@ static void call_ringing(SalOp *h){ linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); linphone_core_stop_ringing(lc); ms_message("Doing early media..."); - linphone_core_update_streams(lc,call,md, call->state); + linphone_call_update_streams(call, md, call->state); if ((linphone_call_params_get_audio_direction(linphone_call_get_current_params(call)) == LinphoneMediaDirectionInactive) && call->audiostream) { if (lc->ringstream != NULL) return; /* Already ringing! */ start_remote_ring(lc, call); @@ -523,7 +524,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o #ifdef BUILD_UPNP if (call->upnp_session != NULL && rmd) { - linphone_core_update_upnp_from_remote_media_description(call, rmd); + linphone_call_update_upnp_from_remote_media_description(call, rmd); } #endif //BUILD_UPNP @@ -582,8 +583,8 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o if (next_state != LinphoneCallIdle){ linphone_call_update_remote_session_id_and_ver(call); - linphone_core_update_ice_state_in_call_stats(call); - linphone_core_update_streams(lc, call, md, next_state); + linphone_call_update_ice_state_in_call_stats(call); + linphone_call_update_streams(call, md, next_state); linphone_call_fix_call_parameters(call, rmd); linphone_call_set_state(call, next_state, next_state_str); }else{ @@ -599,7 +600,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: ms_error("Incompatible SDP answer received, need to abort the call"); - linphone_core_abort_call(lc,call,_("Incompatible, check codecs or security settings...")); + linphone_call_abort(call, _("Incompatible, check codecs or security settings...")); break; /*otherwise we are able to resume previous state*/ default: @@ -641,7 +642,7 @@ static void call_accepted(SalOp *op){ static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("We have been resumed.")); - _linphone_core_accept_call_update(lc,call,NULL,LinphoneCallStreamsRunning,"Connected (streams running)"); + _linphone_call_accept_update(call,NULL,LinphoneCallStreamsRunning,"Connected (streams running)"); } static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ @@ -653,7 +654,7 @@ static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ if (lp_config_get_int(lc->config, "sip", "inactive_video_on_pause", 0)) { linphone_call_params_set_video_direction(params, LinphoneMediaDirectionInactive); } - _linphone_core_accept_call_update(lc,call,params,LinphoneCallPausedByRemote,"Call paused by remote"); + _linphone_call_accept_update(call,params,LinphoneCallPausedByRemote,"Call paused by remote"); linphone_call_params_unref(params); } @@ -695,7 +696,7 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t case LinphoneCallIncomingEarlyMedia: if (is_update) { linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); - _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); + _linphone_call_accept_update(call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); } break; case LinphoneCallStreamsRunning: @@ -709,7 +710,7 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t break; case LinphoneCallPaused: /*we'll remain in pause state but accept the offer anyway according to default parameters*/ - _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state)); + _linphone_call_accept_update(call,NULL,call->state,linphone_call_state_to_string(call->state)); break; case LinphoneCallUpdating: case LinphoneCallPausing: @@ -908,7 +909,7 @@ static void call_failure(SalOp *op){ linphone_address_unref(call->log->to); } call->log->to = linphone_address_ref(redirection_to); - linphone_core_restart_invite(lc, call); + linphone_call_restart_invite(call); return; } } @@ -941,19 +942,19 @@ static void call_failure(SalOp *op){ if (call->params->avpf_enabled == TRUE) { if (i == 0) ms_message("Retrying call [%p] with SAVP", call); call->params->avpf_enabled = FALSE; - linphone_core_restart_invite(lc, call); + linphone_call_restart_invite(call); return; } else if (!linphone_core_is_media_encryption_mandatory(lc)) { if (i == 0) ms_message("Retrying call [%p] with AVP", call); call->params->media_encryption = LinphoneMediaEncryptionNone; memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); - linphone_core_restart_invite(lc, call); + linphone_call_restart_invite(call); return; } } else if (call->params->avpf_enabled == TRUE) { if (i == 0) ms_message("Retrying call [%p] with AVP", call); call->params->avpf_enabled = FALSE; - linphone_core_restart_invite(lc, call); + linphone_call_restart_invite(call); return; } } @@ -1216,7 +1217,7 @@ static void ping_reply(SalOp *op){ if (call){ if (call->state==LinphoneCallOutgoingInit){ call->ping_replied=TRUE; - linphone_core_proceed_with_invite_if_ready(call->core,call,NULL); + linphone_call_proceed_with_invite_if_ready(call, NULL); } } else diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 520eeb4b9..b0e1d58b6 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -445,7 +445,7 @@ int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){ linphone_call_params_unref(params); } else{ ms_message("Pausing call to actually remove from conference"); - err=_linphone_core_pause_call(m_core,call); + err=_linphone_call_pause(call); } return err; } @@ -529,7 +529,7 @@ int LocalConference::enter() { return -1; } if (m_core->current_call != NULL) { - _linphone_core_pause_call(m_core, m_core->current_call); + _linphone_call_pause(m_core->current_call); } if (m_localParticipantStream==NULL) addLocalEndpoint(); return 0; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 9f9f60b1c..e59851dea 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -630,12 +630,12 @@ void linphone_call_update_local_media_description_from_ice_or_upnp(LinphoneCall /*set this to FALSE once flexisip are updated everywhere, let's say in December 2016.*/ bool_t use_nortpproxy = lp_config_get_int(lc->config, "sip", "ice_uses_nortpproxy", TRUE); _update_local_media_description_from_ice(call->localdesc, call->ice_session, use_nortpproxy); - linphone_core_update_ice_state_in_call_stats(call); + linphone_call_update_ice_state_in_call_stats(call); } #ifdef BUILD_UPNP if(call->upnp_session != NULL) { - linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); - linphone_core_update_upnp_state_in_call_stats(call); + linphone_call_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + linphone_call_update_upnp_state_in_call_stats(call); } #endif //BUILD_UPNP } @@ -1504,7 +1504,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro if(!lc->rtp_conf.disable_upnp) { call->upnp_session = linphone_upnp_session_new(call); if (call->upnp_session != NULL) { - if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) { + if (linphone_call_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) { /* uPnP port mappings failed, proceed with the call anyway. */ linphone_call_delete_upnp_session(call); } @@ -4365,7 +4365,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ default: break; } - linphone_core_update_ice_state_in_call_stats(call); + linphone_call_update_ice_state_in_call_stats(call); } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { if (evd->info.ice_processing_successful==FALSE) { ms_warning("No STUN answer from [%s], continuing without STUN",linphone_core_get_stun_server(call->core)); @@ -4373,14 +4373,14 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ linphone_call_on_ice_gathering_finished(call); switch (call->state) { case LinphoneCallUpdating: - linphone_core_start_update_call(call->core, call); + linphone_call_start_update(call); break; case LinphoneCallUpdatedByRemote: - linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate)); + linphone_call_start_accept_update(call, call->prevstate,linphone_call_state_to_string(call->prevstate)); break; case LinphoneCallOutgoingInit: linphone_call_stop_media_streams_for_ice_gathering(call); - linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + linphone_call_proceed_with_invite_if_ready(call, NULL); break; case LinphoneCallIdle: linphone_call_stop_media_streams_for_ice_gathering(call); @@ -4393,8 +4393,8 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ } } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) { if (call->state==LinphoneCallUpdatedByRemote){ - linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate)); - linphone_core_update_ice_state_in_call_stats(call); + linphone_call_start_accept_update(call, call->prevstate, linphone_call_state_to_string(call->prevstate)); + linphone_call_update_ice_state_in_call_stats(call); } } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) { ice_session_restart(call->ice_session, IR_Controlling); @@ -4917,6 +4917,734 @@ RtpTransport* linphone_call_get_meta_rtcp_transport(LinphoneCall *call, int stre return meta_rtcp; } +int linphone_call_pause(LinphoneCall *call) { + int err = _linphone_call_pause(call); + if (err == 0) call->paused_by_app = TRUE; + return err; +} + +/* Internal version that does not play tone indication*/ +int _linphone_call_pause(LinphoneCall *call) { + LinphoneCore *lc; + const char *subject = NULL; + + if ((call->state != LinphoneCallStreamsRunning) && (call->state != LinphoneCallPausedByRemote)) { + ms_warning("Cannot pause this call, it is not active."); + return -1; + } + if (sal_media_description_has_dir(call->resultdesc, SalStreamSendRecv)) { + subject = "Call on hold"; + } else if (sal_media_description_has_dir(call->resultdesc, SalStreamRecvOnly)) { + subject = "Call on hold for me too"; + } else { + ms_error("No reason to pause this call, it is already paused or inactive."); + return -1; + } + + lc = linphone_call_get_core(call); + call->broken = FALSE; + linphone_call_set_state(call, LinphoneCallPausing, "Pausing call"); + linphone_call_make_local_media_description(call); +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + linphone_call_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif // BUILD_UPNP + sal_call_set_local_media_description(call->op, call->localdesc); + if (sal_call_update(call->op, subject, FALSE) != 0) { + linphone_core_notify_display_warning(lc, _("Could not pause the call")); + } + lc->current_call = NULL; + linphone_core_notify_display_status(lc, _("Pausing the current call...")); + if (call->audiostream || call->videostream || call->textstream) + linphone_call_stop_media_streams(call); + call->paused_by_app = FALSE; + return 0; +} + +int linphone_call_resume(LinphoneCall *call) { + LinphoneCore *lc; + const char *subject = "Call resuming"; + char *remote_address; + char *display_status; + + if (call->state != LinphoneCallPaused) { + ms_warning("we cannot resume a call that has not been established and paused before"); + return -1; + } + lc = linphone_call_get_core(call); + if (call->params->in_conference == FALSE) { + if (linphone_core_sound_resources_locked(lc)) { + ms_warning("Cannot resume call %p because another call is locking the sound resources.", call); + return -1; + } + linphone_core_preempt_sound_resources(lc); + ms_message("Resuming call %p", call); + } + + call->was_automatically_paused = FALSE; + call->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 (call->audiostream) audio_stream_play(call->audiostream, NULL); + + linphone_call_make_local_media_description(call); +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + linphone_call_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif // BUILD_UPNP + if (!lc->sip_conf.sdp_200_ack) { + sal_call_set_local_media_description(call->op, call->localdesc); + } else { + sal_call_set_local_media_description(call->op, NULL); + } + sal_media_description_set_dir(call->localdesc, SalStreamSendRecv); + if (call->params->in_conference && !call->current_params->in_conference) subject = "Conference"; + if (sal_call_update(call->op, subject, FALSE) != 0) { + return -1; + } + linphone_call_set_state(call, LinphoneCallResuming,"Resuming"); + if (call->params->in_conference == FALSE) + lc->current_call = call; + remote_address = linphone_call_get_remote_address_as_string(call); + display_status = ms_strdup_printf("Resuming the call with with %s", remote_address); + ms_free(remote_address); + linphone_core_notify_display_status(lc, display_status); + ms_free(display_status); + + if (lc->sip_conf.sdp_200_ack) { + /* We are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive. */ + sal_call_set_local_media_description(call->op, call->localdesc); + } + return 0; +} + +static void terminate_call(LinphoneCall *call) { + LinphoneCore *lc = linphone_call_get_core(call); + + if ((call->state == LinphoneCallIncomingReceived) && (call->non_op_error.reason != SalReasonRequestTimeout)) + call->non_op_error.reason=SalReasonDeclined; + + /* Stop ringing */ + linphone_core_stop_ringing(lc); + linphone_call_stop_media_streams(call); + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif // BUILD_UPNP + + linphone_core_notify_display_status(lc, _("Call ended") ); + linphone_call_set_state(call, LinphoneCallEnd, "Call terminated"); +} + +int linphone_call_terminate(LinphoneCall *call) { + ms_message("Terminate call [%p] which is currently in state %s", call, linphone_call_state_to_string(call->state)); + switch (call->state) { + case LinphoneCallReleased: + case LinphoneCallEnd: + case LinphoneCallError: + ms_warning("No need to terminate a call [%p] in state [%s]", call, linphone_call_state_to_string(call->state)); + return -1; + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + return linphone_call_decline(call, LinphoneReasonDeclined); + case LinphoneCallOutgoingInit: + /* In state OutgoingInit, op has to be destroyed */ + sal_op_release(call->op); + call->op = NULL; + break; + default: + sal_call_terminate(call->op); + break; + } + terminate_call(call); + return 0; +} + +int linphone_call_redirect(LinphoneCall *call, const char *redirect_uri) { + char *real_url = NULL; + LinphoneCore *lc; + LinphoneAddress *real_parsed_url; + + if (call->state != LinphoneCallIncomingReceived) { + ms_error("Bad state for call redirection."); + return -1; + } + + lc = linphone_call_get_core(call); + real_parsed_url = linphone_core_interpret_url(lc, redirect_uri); + if (!real_parsed_url) { + /* Bad url */ + ms_error("Bad redirect URI: %s", redirect_uri ? redirect_uri : "NULL"); + return -1; + } + + real_url = linphone_address_as_string(real_parsed_url); + sal_call_decline(call->op, SalReasonRedirect, real_url); + ms_free(real_url); + sal_error_info_set(&call->non_op_error, SalReasonRedirect, 603, "Call redirected", NULL); + terminate_call(call); + linphone_address_unref(real_parsed_url); + return 0; +} + +int linphone_call_decline(LinphoneCall * call, LinphoneReason reason) { + if ((call->state != LinphoneCallIncomingReceived) && (call->state != LinphoneCallIncomingEarlyMedia)) { + ms_error("Cannot decline a call that is in state %s", linphone_call_state_to_string(call->state)); + return -1; + } + + sal_call_decline(call->op, linphone_reason_to_sal(reason), NULL); + terminate_call(call); + return 0; +} + +int linphone_call_accept(LinphoneCall *call) { + return linphone_call_accept_with_params(call, NULL); +} + +int linphone_call_accept_with_params(LinphoneCall *call, const LinphoneCallParams *params) { + LinphoneCore *lc; + SalOp *replaced; + SalMediaDescription *new_md; + bool_t was_ringing = FALSE; + bctbx_list_t *iterator, *copy; + + switch (call->state) { + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + break; + default: + ms_error("linphone_core_accept_call_with_params() call [%p] is in state [%s], operation not permitted.", + call, linphone_call_state_to_string(call->state)); + return -1; + } + + lc = linphone_call_get_core(call); + for (iterator = copy = bctbx_list_copy(linphone_core_get_calls(lc)); iterator != NULL; iterator = bctbx_list_next(iterator)) { + LinphoneCall *a_call = (LinphoneCall *)bctbx_list_get_data(iterator); + if (a_call == call) continue; + switch (a_call->state) { + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingEarlyMedia: + ms_message("Already existing call [%p] in state [%s], canceling it before accepting new call [%p]", + a_call, linphone_call_state_to_string(a_call->state), call); + linphone_call_terminate(a_call); + break; + default: + break; /* Nothing to do */ + } + } + bctbx_list_free(copy); + + /* Check if this call is supposed to replace an already running one */ + replaced = sal_call_get_replaces(call->op); + if (replaced) { + LinphoneCall *rc = (LinphoneCall *)sal_op_get_user_pointer(replaced); + if (rc) { + ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.", call, rc); + linphone_call_terminate(rc); + } + } + + if (lc->current_call != call) { + linphone_core_preempt_sound_resources(lc); + } + + /* Stop ringing */ + if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) { + ms_message("Stop ringing"); + linphone_core_stop_ringing(lc); + was_ringing = TRUE; + } + if (call->ringing_beep) { + linphone_core_stop_dtmf(lc); + call->ringing_beep = FALSE; + } + + /* Try to be best-effort in giving real local or routable contact address */ + linphone_call_set_contact_op(call); + if (params) { + linphone_call_set_new_params(call, params); + linphone_call_prepare_ice(call, TRUE); + linphone_call_make_local_media_description(call); + sal_call_set_local_media_description(call->op, call->localdesc); + sal_op_set_sent_custom_header(call->op, params->custom_headers); + } + + /* Give a chance a set card prefered sampling frequency */ + if (call->localdesc->streams[0].max_rate > 0) { + ms_message("Configuring prefered card sampling rate to [%i]", call->localdesc->streams[0].max_rate); + if (lc->sound_conf.play_sndcard) + ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate); + if (lc->sound_conf.capt_sndcard) + ms_snd_card_set_preferred_sample_rate(lc->sound_conf.capt_sndcard, call->localdesc->streams[0].max_rate); + } + + if (!was_ringing && (call->audiostream->ms.state == MSStreamInitialized) && !lc->use_files) { + audio_stream_prepare_sound(call->audiostream, lc->sound_conf.play_sndcard, lc->sound_conf.capt_sndcard); + } + + linphone_call_update_remote_session_id_and_ver(call); + + sal_call_accept(call->op); + linphone_core_notify_display_status(lc, _("Connected.")); + lc->current_call = call; + linphone_call_set_state(call, LinphoneCallConnected, "Connected"); + new_md = sal_call_get_final_media_description(call->op); + linphone_call_stop_ice_for_inactive_streams(call, new_md); + if (new_md) { + linphone_call_update_streams(call, new_md, LinphoneCallStreamsRunning); + linphone_call_set_state(call, LinphoneCallStreamsRunning, "Connected (streams running)"); + } else { + call->expect_media_in_ack = TRUE; + } + + ms_message("Call answered"); + return 0; +} + +int linphone_call_accept_early_media(LinphoneCall* call) { + return linphone_call_accept_early_media_with_params(call, NULL); +} + +int linphone_call_accept_early_media_with_params(LinphoneCall *call, const LinphoneCallParams *params) { + SalMediaDescription* md; + + if (call->state != LinphoneCallIncomingReceived) { + ms_error("Bad state %s for linphone_core_accept_early_media_with_params()", linphone_call_state_to_string(call->state)); + return -1; + } + + /* Try to be best-effort in giving real local or routable contact address for 100Rel case */ + linphone_call_set_contact_op(call); + + /* If parameters are passed, update the media description */ + if (params) { + linphone_call_set_new_params(call, params); + linphone_call_make_local_media_description(call); + sal_call_set_local_media_description(call->op, call->localdesc); + sal_op_set_sent_custom_header(call->op, params->custom_headers); + } + + sal_call_notify_ringing(call->op, TRUE); + + linphone_call_set_state(call, LinphoneCallIncomingEarlyMedia, "Incoming call early media"); + md = sal_call_get_final_media_description(call->op); + if (md) linphone_call_update_streams(call, md, call->state); + return 0; +} + +int linphone_call_update(LinphoneCall *call, const LinphoneCallParams *params) { + int err = 0; + LinphoneCallState nextstate; + LinphoneCallState initial_state = call->state; + const LinphoneCallParams *current_params; + LinphoneCore *lc; + +#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) + bool_t has_video = FALSE; +#endif + + switch (initial_state) { + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingEarlyMedia: + nextstate = LinphoneCallEarlyUpdating; + break; + case LinphoneCallStreamsRunning: + case LinphoneCallPausedByRemote: + case LinphoneCallUpdatedByRemote: + nextstate = LinphoneCallUpdating; + break; + case LinphoneCallPaused: + nextstate = LinphoneCallPausing; + break; + case LinphoneCallOutgoingProgress: + case LinphoneCallPausing: + case LinphoneCallResuming: + case LinphoneCallUpdating: + nextstate = initial_state; + break; + default: + ms_error("linphone_call_update() is not allowed in [%s] state", linphone_call_state_to_string(call->state)); + return -1; + } + + lc = linphone_call_get_core(call); + current_params = linphone_call_get_current_params(call); + if ((current_params != NULL) && (current_params == params)) { + ms_warning("linphone_call_update() is given the current params of the call, this probably not what you intend to do!"); + } + + linphone_call_check_ice_session(call, IR_Controlling, TRUE); + + if (params != NULL) { + call->broken = FALSE; + linphone_call_set_state(call, nextstate, "Updating call"); +#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) + has_video = call->params->has_video; + + /* Video removal */ + if ((call->videostream != NULL) && !params->has_video) { + if (call->upnp_session != NULL) { + if (linphone_call_update_upnp(call) < 0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } + } + } +#endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */ + linphone_call_set_new_params(call, params); + err = linphone_call_prepare_ice(call, FALSE); + if (err == 1) { + ms_message("Defer call update to gather ICE candidates"); + return 0; + } + +#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) + /* Video adding */ + if (!has_video && call->params->has_video) { + if(call->upnp_session != NULL) { + ms_message("Defer call update to add uPnP port mappings"); + video_stream_prepare_video(call->videostream); + if (linphone_call_update_upnp(call) < 0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else { + return err; + } + } + } +#endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */ + if ((err = linphone_call_start_update(call)) && (call->state != initial_state)) { + /* Restore initial state */ + linphone_call_set_state(call, initial_state, "Restore initial state"); + } + } else { +#ifdef VIDEO_ENABLED + if ((call->videostream != NULL) && (call->state == LinphoneCallStreamsRunning)) { + video_stream_set_sent_video_size(call->videostream, linphone_core_get_preferred_video_size(lc)); + video_stream_set_fps(call->videostream, linphone_core_get_preferred_framerate(lc)); + if (call->camera_enabled && (call->videostream->cam != lc->video_conf.device)) { + video_stream_change_camera(call->videostream, lc->video_conf.device); + } else { + video_stream_update_video_params(call->videostream); + } + } +#endif + } + + return err; +} + +int linphone_call_start_update(LinphoneCall *call) { + const char *subject; + int err; + bool_t no_user_consent = call->params->no_user_consent; + LinphoneCore *lc = linphone_call_get_core(call); + + linphone_call_fill_media_multicast_addr(call); + + if (!no_user_consent) linphone_call_make_local_media_description(call); +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + linphone_call_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif // BUILD_UPNP + if (call->params->in_conference) { + subject = "Conference"; + } else if (call->params->internal_call_update) { + subject = "ICE processing concluded"; + } else if (no_user_consent) { + subject = "Refreshing"; + } else { + subject = "Media change"; + } + linphone_core_notify_display_status(lc, _("Modifying call parameters...")); + if (!lc->sip_conf.sdp_200_ack) { + sal_call_set_local_media_description(call->op, call->localdesc); + } else { + sal_call_set_local_media_description(call->op, NULL); + } + if (call->dest_proxy && call->dest_proxy->op) { + /* Give a chance to update the contact address if connectivity has changed */ + sal_op_set_contact_address(call->op, sal_op_get_contact_address(call->dest_proxy->op)); + } else { + sal_op_set_contact_address(call->op, NULL); + } + err = sal_call_update(call->op, subject, no_user_consent); + if (lc->sip_conf.sdp_200_ack) { + /* We are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive. */ + sal_call_set_local_media_description(call->op, call->localdesc); + } + return err; +} + +int linphone_call_defer_update(LinphoneCall *call) { + if (call->state != LinphoneCallUpdatedByRemote) { + ms_error("linphone_call_defer_update() not done in state LinphoneCallUpdatedByRemote"); + return -1; + } + + if (call->expect_media_in_ack) { + ms_error("linphone_call_defer_update() is not possible during a late offer incoming reINVITE (INVITE without SDP)"); + return -1; + } + + call->defer_update=TRUE; + return 0; +} + +int linphone_call_start_accept_update(LinphoneCall *call, LinphoneCallState next_state, const char *state_info) { + SalMediaDescription *md; + + if ((call->ice_session != NULL) && (ice_session_nb_losing_pairs(call->ice_session) > 0)) { + /* Defer the sending of the answer until there are no losing pairs left */ + return 0; + } + + linphone_call_make_local_media_description(call); + linphone_call_update_remote_session_id_and_ver(call); + sal_call_set_local_media_description(call->op, call->localdesc); + sal_call_accept(call->op); + md = sal_call_get_final_media_description(call->op); + linphone_call_stop_ice_for_inactive_streams(call, md); + if (md && !sal_media_description_empty(md)) { + linphone_call_update_streams(call, md, next_state); + } + linphone_call_set_state(call, next_state, state_info); + return 0; +} + +int _linphone_call_accept_update(LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info) { + SalMediaDescription *remote_desc; + bool_t keep_sdp_version; + LinphoneCore *lc = linphone_call_get_core(call); +#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) + bool_t old_has_video = call->params->has_video; +#endif + + remote_desc = sal_call_get_remote_media_description(call->op); + keep_sdp_version = lp_config_get_int(lc->config, "sip", "keep_sdp_version", 0); + if (keep_sdp_version && (remote_desc->session_id == call->remote_session_id) && (remote_desc->session_ver == call->remote_session_ver)) { + /* Remote has sent an INVITE with the same SDP as before, so send a 200 OK with the same SDP as before. */ + ms_warning("SDP version has not changed, send same SDP as before."); + sal_call_accept(call->op); + linphone_call_set_state(call, next_state, state_info); + return 0; + } + if (params == NULL) { + if (!sal_call_is_offerer(call->op)) { + /* Reset call param for multicast because this param is only relevant when offering */ + linphone_call_params_enable_audio_multicast(call->params, FALSE); + linphone_call_params_enable_video_multicast(call->params, FALSE); + } + } else { + linphone_call_set_new_params(call, params); + } + + if (call->params->has_video && !linphone_core_video_enabled(lc)) { + ms_warning("Requested video but video support is globally disabled. Refusing video."); + call->params->has_video = FALSE; + } + if (call->current_params->in_conference) { + ms_warning("Video isn't supported in conference"); + call->params->has_video = FALSE; + } + /* Update multicast params according to call params */ + linphone_call_fill_media_multicast_addr(call); + + linphone_call_check_ice_session(call, IR_Controlled, TRUE); + linphone_call_init_media_streams(call); /* So that video stream is initialized if necessary */ + if (linphone_call_prepare_ice(call, TRUE) == 1) { + return 0; /* Deferred until completion of ICE gathering */ + } + +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + linphone_call_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op)); +#ifdef VIDEO_ENABLED + if ((call->params->has_video) && (call->params->has_video != old_has_video)) { + video_stream_prepare_video(call->videostream); + if (linphone_call_update_upnp(call) < 0) { + /* uPnP update failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else return 0; + } +#endif // VIDEO_ENABLED + } +#endif // BUILD_UPNP + + linphone_call_start_accept_update(call, next_state, state_info); + return 0; +} + +int linphone_call_accept_update(LinphoneCall *call, const LinphoneCallParams *params) { + if (call->state != LinphoneCallUpdatedByRemote) { + ms_error("linphone_call_accept_update(): invalid state %s to call this function.", linphone_call_state_to_string(call->state)); + return -1; + } + if (call->expect_media_in_ack) { + ms_error("linphone_call_accept_update() is not possible during a late offer incoming reINVITE (INVITE without SDP)"); + return -1; + } + return _linphone_call_accept_update(call, params, call->prevstate, linphone_call_state_to_string(call->prevstate)); +} + +int linphone_call_transfer(LinphoneCall *call, const char *refer_to) { + char *real_url = NULL; + LinphoneCore *lc = linphone_call_get_core(call); + LinphoneAddress *real_parsed_url = linphone_core_interpret_url(lc, refer_to); + + if (!real_parsed_url) { + /* bad url */ + return -1; + } + //lc->call = NULL; // Do not do that you will lose the call afterward... + real_url = linphone_address_as_string(real_parsed_url); + sal_call_refer(call->op, real_url); + ms_free(real_url); + linphone_address_unref(real_parsed_url); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); + return 0; +} + +int linphone_call_transfer_to_another(LinphoneCall *call, LinphoneCall *dest) { + int result = sal_call_refer_with_replaces (call->op, dest->op); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); + return result; +} + +int linphone_call_abort(LinphoneCall *call, const char *error) { + LinphoneCore *lc = linphone_call_get_core(call); + + sal_call_terminate(call->op); + + /* Stop ringing */ + linphone_core_stop_ringing(lc); + linphone_call_stop_media_streams(call); + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif // BUILD_UPNP + + linphone_core_notify_display_status(lc, _("Call aborted")); + linphone_call_set_state(call, LinphoneCallError, error); + return 0; +} + +int linphone_call_proceed_with_invite_if_ready(LinphoneCall *call, LinphoneProxyConfig *dest_proxy) { + bool_t ice_ready = FALSE; + bool_t upnp_ready = FALSE; + bool_t ping_ready = FALSE; + + if (call->ice_session != NULL) { + if (ice_session_candidates_gathered(call->ice_session)) ice_ready = TRUE; + } else { + ice_ready = TRUE; + } +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + if (linphone_upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE; + } else { + upnp_ready = TRUE; + } +#else + upnp_ready=TRUE; +#endif // BUILD_UPNP + if (call->ping_op != NULL) { + if (call->ping_replied == TRUE) ping_ready = TRUE; + } else { + ping_ready = TRUE; + } + + if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) { + return linphone_call_start_invite(call, NULL); + } + return 0; +} + +int linphone_call_start_invite(LinphoneCall *call, const LinphoneAddress *destination /* = NULL if to be taken from the call log */) { + int err; + char *real_url, *barmsg; + char *from; + LinphoneCore *lc = linphone_call_get_core(call); + + /* Try to be best-effort in giving real local or routable contact address */ + linphone_call_set_contact_op(call); + + linphone_core_stop_dtmf_stream(lc); + linphone_call_make_local_media_description(call); + + if (lc->ringstream == NULL) { + if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard) { + /* Give a chance a set card prefered sampling frequency */ + if (call->localdesc->streams[0].max_rate > 0) { + ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate); + } + if (!lc->use_files) { + audio_stream_prepare_sound(call->audiostream, lc->sound_conf.play_sndcard, lc->sound_conf.capt_sndcard); + } + } + } + real_url = linphone_address_as_string(destination ? destination : call->log->to); + from = linphone_address_as_string(call->log->from); + + if (!lc->sip_conf.sdp_200_ack) { + /* We are offering, set local media description before sending the call */ + sal_call_set_local_media_description(call->op, call->localdesc); + } + + barmsg = ms_strdup_printf("%s %s", _("Contacting"), real_url); + linphone_core_notify_display_status(lc, barmsg); + ms_free(barmsg); + + linphone_call_ref(call); /* Take a ref because sal_call() may destroy the call if no SIP transport is available */ + err = sal_call(call->op, from, real_url); + + if (err < 0) { + if ((call->state != LinphoneCallError) && (call->state != LinphoneCallReleased)) { + /* sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously, + in which case there is no need to perform a state change here. */ + linphone_core_notify_display_status(lc, _("Could not call")); + linphone_call_stop_media_streams(call); + linphone_call_set_state(call, LinphoneCallError, "Call failed"); + } + goto end; + } + if (lc->sip_conf.sdp_200_ack) { + /* We are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive. */ + sal_call_set_local_media_description(call->op, call->localdesc); + } + call->log->call_id = ms_strdup(sal_op_get_call_id(call->op)); /* Must be known at that time */ + linphone_call_set_state(call, LinphoneCallOutgoingProgress, "Outgoing call in progress"); + +end: + linphone_call_unref(call); /* Revert the ref taken before calling sal_call() */ + ms_free(real_url); + ms_free(from); + return err; +} + +int linphone_call_restart_invite(LinphoneCall *call) { + linphone_call_create_op(call); + linphone_call_stop_media_streams(call); + ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); + linphone_call_init_media_streams(call); + return linphone_call_start_invite(call, NULL); +} + void linphone_call_set_broken(LinphoneCall *call){ switch(call->state){ /*for all the early states, we prefer to drop the call*/ @@ -4954,7 +5682,7 @@ static void linphone_call_repair_by_invite_with_replaces(LinphoneCall *call) { sal_op_kill_dialog(call->op); linphone_call_create_op(call); sal_call_set_replaces(call->op, call_id, from_tag, to_tag); - linphone_core_start_invite(call->core, call, NULL); + linphone_call_start_invite(call, NULL); } void linphone_call_reinvite_to_recover_from_connection_loss(LinphoneCall *call) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 69249d7b7..da8cdfb50 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2806,7 +2806,7 @@ void linphone_core_iterate(LinphoneCore *lc){ elapsed = (int)(current_real_time - call->log->start_date_time); /* get immediately a reference to next one in case the one we are going to examine is destroy and removed during - linphone_core_start_invite() */ + linphone_call_start_invite() */ calls=calls->next; linphone_call_background_tasks(call,one_second_elapsed); if (call->state==LinphoneCallOutgoingInit && (elapsed>=lc->sip_conf.delayed_timeout)){ @@ -2823,7 +2823,7 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_delete_upnp_session(call); } #endif //BUILD_UPNP - linphone_core_start_invite(lc,call, NULL); + linphone_call_start_invite(call, NULL); } if (call->state==LinphoneCallIncomingReceived || call->state==LinphoneCallIncomingEarlyMedia){ if (one_second_elapsed) ms_message("incoming call ringing for %i seconds",elapsed); @@ -2920,7 +2920,7 @@ LinphoneCall * linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall * if (call->state!=LinphoneCallPaused){ ms_message("Automatically pausing current call to accept transfer."); - _linphone_core_pause_call(lc,call); + _linphone_call_pause(call); call->was_automatically_paused=TRUE; } @@ -3038,109 +3038,6 @@ const char *linphone_core_find_best_identity(LinphoneCore *lc, const LinphoneAdd return linphone_core_get_primary_contact(lc); } - -int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ - bool_t ice_ready = FALSE; - bool_t upnp_ready = FALSE; - bool_t ping_ready = FALSE; - - if (call->ice_session != NULL) { - if (ice_session_candidates_gathered(call->ice_session)) ice_ready = TRUE; - } else { - ice_ready = TRUE; - } -#ifdef BUILD_UPNP - if (call->upnp_session != NULL) { - if (linphone_upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE; - } else { - upnp_ready = TRUE; - } -#else - upnp_ready=TRUE; -#endif //BUILD_UPNP - if (call->ping_op != NULL) { - if (call->ping_replied == TRUE) ping_ready = TRUE; - } else { - ping_ready = TRUE; - } - - if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) { - return linphone_core_start_invite(lc, call, NULL); - } - return 0; -} - -int linphone_core_restart_invite(LinphoneCore *lc, LinphoneCall *call){ - linphone_call_create_op(call); - linphone_call_stop_media_streams(call); - ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); - ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); - ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); - linphone_call_init_media_streams(call); - return linphone_core_start_invite(lc,call, NULL); -} - -int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination /* = NULL if to be taken from the call log */){ - int err; - char *real_url,*barmsg; - char *from; - /*try to be best-effort in giving real local or routable contact address */ - linphone_call_set_contact_op(call); - - linphone_core_stop_dtmf_stream(lc); - linphone_call_make_local_media_description(call); - - if (lc->ringstream==NULL) { - if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){ - /*give a chance a set card prefered sampling frequency*/ - if (call->localdesc->streams[0].max_rate>0) { - ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate); - } - if (!lc->use_files) - audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); - } - } - real_url=linphone_address_as_string( destination ? destination : call->log->to); - from=linphone_address_as_string(call->log->from); - - if (!lc->sip_conf.sdp_200_ack){ - /*we are offering, set local media description before sending the call*/ - sal_call_set_local_media_description(call->op,call->localdesc); - } - - barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); - linphone_core_notify_display_status(lc,barmsg); - ms_free(barmsg); - - linphone_call_ref(call); /* Take a ref because sal_call() may destroy the call if no SIP transport is available */ - err=sal_call(call->op,from,real_url); - - if (err < 0){ - if (call->state != LinphoneCallError && - call->state != LinphoneCallReleased){ - /*sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously, - * in which case there is no need to perform a state change here.*/ - linphone_core_notify_display_status(lc,_("Could not call")); - linphone_call_stop_media_streams(call); - linphone_call_set_state(call,LinphoneCallError,"Call failed"); - } - goto end; - } - if (lc->sip_conf.sdp_200_ack){ - /*we are NOT offering, set local media description after sending the call so that we are ready to - process the remote offer when it will arrive*/ - sal_call_set_local_media_description(call->op,call->localdesc); - } - call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/ - linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress"); - -end: - linphone_call_unref(call); /* Revert the ref taken before calling sal_call() */ - ms_free(real_url); - ms_free(from); - return err; -} - LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url){ LinphoneCall *call; LinphoneCallParams *p=linphone_core_create_call_params(lc, NULL); @@ -3279,13 +3176,13 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const } else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { #ifdef BUILD_UPNP - if (linphone_core_update_upnp(lc,call)<0) { + if (linphone_call_update_upnp(call) < 0) { /* uPnP port mappings failed, proceed with the call anyway. */ linphone_call_delete_upnp_session(call); } else { defer = TRUE; } -#endif //BUILD_UPNP +#endif // BUILD_UPNP } if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ @@ -3305,7 +3202,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const } if (defer==FALSE) { - if (linphone_core_start_invite(lc,call,NULL) != 0){ + if (linphone_call_start_invite(call,NULL) != 0){ /*the call has already gone to error and released state, so do not return it*/ call = NULL; } @@ -3317,30 +3214,15 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const } int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *url) { - char *real_url=NULL; - LinphoneAddress *real_parsed_url=linphone_core_interpret_url(lc,url); - - if (!real_parsed_url){ - /* bad url */ - return -1; - } - if (call==NULL){ + if (call == NULL) { ms_warning("No established call to refer."); return -1; } - //lc->call=NULL; //Do not do that you will lose the call afterward . . . - real_url=linphone_address_as_string (real_parsed_url); - sal_call_refer(call->op,real_url); - ms_free(real_url); - linphone_address_unref(real_parsed_url); - linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); - return 0; + return linphone_call_transfer(call, url); } -int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){ - int result = sal_call_refer_with_replaces (call->op,dest->op); - linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); - return result; +int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest) { + return linphone_call_transfer_to_another(call, dest); } bool_t linphone_core_is_incoming_invite_pending(LinphoneCore*lc){ @@ -3413,509 +3295,62 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ ms_free(tmp); } -int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall* call, const LinphoneCallParams* params) { - if (call->state==LinphoneCallIncomingReceived){ - SalMediaDescription* md; - - /*try to be best-effort in giving real local or routable contact address for 100Rel case*/ - linphone_call_set_contact_op(call); - - // if parameters are passed, update the media description - if ( params ) { - linphone_call_set_new_params(call,params); - linphone_call_make_local_media_description (call); - sal_call_set_local_media_description ( call->op,call->localdesc ); - sal_op_set_sent_custom_header ( call->op,params->custom_headers ); - } - - sal_call_notify_ringing(call->op, TRUE); - - linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); - md=sal_call_get_final_media_description(call->op); - if (md) linphone_core_update_streams(lc, call, md, call->state); - return 0; - }else{ - ms_error("Bad state %s for linphone_core_accept_early_media_with_params()", linphone_call_state_to_string(call->state)); - } - - return -1; +int linphone_core_accept_early_media_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { + return linphone_call_accept_early_media_with_params(call, params); } -int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneCall* call){ - return linphone_core_accept_early_media_with_params(lc, call, NULL); +int linphone_core_accept_early_media(LinphoneCore *lc, LinphoneCall *call) { + return linphone_call_accept_early_media(call); } -int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ - const char *subject; - int err; - bool_t no_user_consent=call->params->no_user_consent; - - linphone_call_fill_media_multicast_addr(call); - - if (!no_user_consent) linphone_call_make_local_media_description(call); -#ifdef BUILD_UPNP - if(call->upnp_session != NULL) { - linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); - } -#endif //BUILD_UPNP - if (call->params->in_conference){ - subject="Conference"; - }else if (call->params->internal_call_update){ - subject="ICE processing concluded"; - }else if (no_user_consent){ - subject="Refreshing"; - }else{ - subject="Media change"; - } - linphone_core_notify_display_status(lc,_("Modifying call parameters...")); - if (!lc->sip_conf.sdp_200_ack){ - sal_call_set_local_media_description (call->op,call->localdesc); - } else { - sal_call_set_local_media_description (call->op,NULL); - } - if (call->dest_proxy && call->dest_proxy->op){ - /*give a chance to update the contact address if connectivity has changed*/ - sal_op_set_contact_address(call->op,sal_op_get_contact_address(call->dest_proxy->op)); - }else sal_op_set_contact_address(call->op,NULL); - err= sal_call_update(call->op,subject,no_user_consent); - if (lc->sip_conf.sdp_200_ack){ - /*we are NOT offering, set local media description after sending the call so that we are ready to - process the remote offer when it will arrive*/ - sal_call_set_local_media_description(call->op,call->localdesc); - } - return err; +int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { + return linphone_call_update(call, params); } -int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ - int err=0; - LinphoneCallState nextstate, initial_state; - const LinphoneCallParams *current_params; - -#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) - bool_t has_video = FALSE; -#endif - - switch(initial_state=call->state){ - case LinphoneCallIncomingReceived: - case LinphoneCallIncomingEarlyMedia: - case LinphoneCallOutgoingRinging: - case LinphoneCallOutgoingEarlyMedia: - nextstate=LinphoneCallEarlyUpdating; - break; - case LinphoneCallStreamsRunning: - case LinphoneCallPausedByRemote: - case LinphoneCallUpdatedByRemote: - nextstate=LinphoneCallUpdating; - break; - case LinphoneCallPaused: - nextstate=LinphoneCallPausing; - break; - case LinphoneCallOutgoingProgress: - case LinphoneCallPausing: - case LinphoneCallResuming: - case LinphoneCallUpdating: - nextstate=call->state; - break; - default: - ms_error("linphone_core_update_call() is not allowed in [%s] state",linphone_call_state_to_string(call->state)); - return -1; - } - - current_params = linphone_call_get_current_params(call); - if ((current_params != NULL) && (current_params == params)) { - ms_warning("linphone_core_update_call() is given the current params of the call, this probably not what you intend to do!"); - } - - linphone_call_check_ice_session(call, IR_Controlling, TRUE); - - if (params!=NULL){ - call->broken = FALSE; - linphone_call_set_state(call,nextstate,"Updating call"); -#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) - has_video = call->params->has_video; - - // Video removing - if((call->videostream != NULL) && !params->has_video) { - if(call->upnp_session != NULL) { - if (linphone_core_update_upnp(lc, call)<0) { - /* uPnP port mappings failed, proceed with the call anyway. */ - linphone_call_delete_upnp_session(call); - } - } - - } -#endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */ - linphone_call_set_new_params(call,params); - err=linphone_call_prepare_ice(call,FALSE); - if (err==1) { - ms_message("Defer call update to gather ICE candidates"); - return 0; - } - -#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) - // Video adding - if (!has_video && call->params->has_video) { - if(call->upnp_session != NULL) { - ms_message("Defer call update to add uPnP port mappings"); - video_stream_prepare_video(call->videostream); - if (linphone_core_update_upnp(lc, call)<0) { - /* uPnP port mappings failed, proceed with the call anyway. */ - linphone_call_delete_upnp_session(call); - } else { - return err; - } - } - } -#endif //defined(VIDEO_ENABLED) && defined(BUILD_UPNP) - if ((err = linphone_core_start_update_call(lc, call)) && call->state!=initial_state) { - /*Restore initial state*/ - linphone_call_set_state(call,initial_state,"Restore initial state"); - } - - }else{ -#ifdef VIDEO_ENABLED - if ((call->videostream != NULL) && (call->state == LinphoneCallStreamsRunning)) { - video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc)); - video_stream_set_fps(call->videostream, linphone_core_get_preferred_framerate(lc)); - if (call->camera_enabled && call->videostream->cam!=lc->video_conf.device){ - video_stream_change_camera(call->videostream, lc->video_conf.device); - }else video_stream_update_video_params(call->videostream); - } -#endif - } - - return err; +int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call) { + return linphone_call_defer_update(call); } -int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ - if (call->state==LinphoneCallUpdatedByRemote){ - if (call->expect_media_in_ack){ - ms_error("linphone_core_defer_call_update() is not possible during a late offer incoming reINVITE (INVITE without SDP)"); - return -1; - } - call->defer_update=TRUE; - return 0; - }else{ - ms_error("linphone_core_defer_call_update() not done in state LinphoneCallUpdatedByRemote"); - } - return -1; +int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { + return linphone_call_accept_update(call, params); } -int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState next_state, const char *state_info){ - SalMediaDescription *md; - if (call->ice_session != NULL) { - if (ice_session_nb_losing_pairs(call->ice_session) > 0) { - /* Defer the sending of the answer until there are no losing pairs left. */ - return 0; - } +static LinphoneCall * get_unique_call(LinphoneCore *lc) { + LinphoneCall *call = linphone_core_get_current_call(lc); + if ((call == NULL) && (linphone_core_get_calls_nb(lc) == 1)) { + call = (LinphoneCall *)bctbx_list_get_data(linphone_core_get_calls(lc)); } - linphone_call_make_local_media_description(call); - - linphone_call_update_remote_session_id_and_ver(call); - sal_call_set_local_media_description(call->op,call->localdesc); - sal_call_accept(call->op); - md=sal_call_get_final_media_description(call->op); - linphone_call_stop_ice_for_inactive_streams(call, md); - if (md && !sal_media_description_empty(md)){ - linphone_core_update_streams(lc, call, md, next_state); - } - linphone_call_set_state(call,next_state,state_info); - return 0; + return call; } -int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ - if (call->state != LinphoneCallUpdatedByRemote){ - ms_error("linphone_core_accept_update(): invalid state %s to call this function.", - linphone_call_state_to_string(call->state)); - return -1; - } - if (call->expect_media_in_ack){ - ms_error("linphone_core_accept_call_update() is not possible during a late offer incoming reINVITE (INVITE without SDP)"); - return -1; - } - return _linphone_core_accept_call_update(lc, call, params, call->prevstate, linphone_call_state_to_string(call->prevstate)); +int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) { + return linphone_core_accept_call_with_params(lc, call, NULL); } -int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info){ - SalMediaDescription *remote_desc; - bool_t keep_sdp_version; -#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) - bool_t old_has_video = call->params->has_video; -#endif - - remote_desc = sal_call_get_remote_media_description(call->op); - keep_sdp_version = lp_config_get_int(lc->config, "sip", "keep_sdp_version", 0); - if (keep_sdp_version &&(remote_desc->session_id == call->remote_session_id) && (remote_desc->session_ver == call->remote_session_ver)) { - /* Remote has sent an INVITE with the same SDP as before, so send a 200 OK with the same SDP as before. */ - ms_warning("SDP version has not changed, send same SDP as before."); - sal_call_accept(call->op); - linphone_call_set_state(call,next_state,state_info); - return 0; - } - if (params==NULL){ - if (!sal_call_is_offerer(call->op)) { - /*reset call param for multicast because this param is only relevant when offering*/ - linphone_call_params_enable_audio_multicast(call->params,FALSE); - linphone_call_params_enable_video_multicast(call->params,FALSE); - } - }else - linphone_call_set_new_params(call,params); - - if (call->params->has_video && !linphone_core_video_enabled(lc)){ - ms_warning("linphone_core_accept_call_update(): requested video but video support is globally disabled. Refusing video."); - call->params->has_video=FALSE; - } - if (call->current_params->in_conference) { - ms_warning("Video isn't supported in conference"); - call->params->has_video = FALSE; - } - /*update multicast params according to call params*/ - linphone_call_fill_media_multicast_addr(call); - - linphone_call_check_ice_session(call, IR_Controlled, TRUE); - linphone_call_init_media_streams(call); /*so that video stream is initialized if necessary*/ - if (linphone_call_prepare_ice(call,TRUE)==1) - return 0;/*deferred to completion of ICE gathering*/ - -#ifdef BUILD_UPNP - if(call->upnp_session != NULL) { - linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op)); -#ifdef VIDEO_ENABLED - if ((call->params->has_video) && (call->params->has_video != old_has_video)) { - video_stream_prepare_video(call->videostream); - if (linphone_core_update_upnp(lc, call)<0) { - /* uPnP update failed, proceed with the call anyway. */ - linphone_call_delete_upnp_session(call); - } else return 0; - } -#endif //VIDEO_ENABLED - } -#endif //BUILD_UPNP - - linphone_core_start_accept_call_update(lc, call, next_state, state_info); - return 0; -} - -int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){ - return linphone_core_accept_call_with_params(lc,call,NULL); -} - -int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ - SalOp *replaced; - SalMediaDescription *new_md; - bool_t was_ringing=FALSE; - bctbx_list_t * iterator, *copy; - - if (call==NULL){ - //if just one call is present answer the only one ... - if(linphone_core_get_calls_nb (lc) != 1) - return -1; - else - call = (LinphoneCall*)linphone_core_get_calls(lc)->data; - } - - switch(call->state){ - case LinphoneCallIncomingReceived: - case LinphoneCallIncomingEarlyMedia: - break; - default: - ms_error("linphone_core_accept_call_with_params() call [%p] is in state [%s], operation not permitted.", - call, linphone_call_state_to_string(call->state)); - return -1; - break; - } - - - for (iterator=copy=bctbx_list_copy(linphone_core_get_calls(lc));iterator!=NULL;iterator=iterator->next) { - LinphoneCall *a_call=(LinphoneCall*)iterator->data; - if (a_call==call) continue; - switch(a_call->state){ - case LinphoneCallOutgoingInit: - case LinphoneCallOutgoingProgress: - case LinphoneCallOutgoingRinging: - case LinphoneCallOutgoingEarlyMedia: - ms_message("Already existing call [%p] in state [%s], canceling it before accepting new call [%p]",a_call - ,linphone_call_state_to_string(a_call->state) - ,call); - linphone_core_terminate_call(lc,a_call); - break; - default: - break; /*nothing to do*/ - } - } - bctbx_list_free(copy); - - /* check if this call is supposed to replace an already running one*/ - replaced=sal_call_get_replaces(call->op); - if (replaced){ - LinphoneCall *rc=(LinphoneCall*)sal_op_get_user_pointer (replaced); - if (rc){ - ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.", - call,rc); - linphone_core_terminate_call(lc,rc); - } - } - - if (lc->current_call!=call){ - linphone_core_preempt_sound_resources(lc); - } - - /*stop ringing */ - if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) { - ms_message("stop ringing"); - linphone_core_stop_ringing(lc); - was_ringing=TRUE; - } - if (call->ringing_beep){ - linphone_core_stop_dtmf(lc); - call->ringing_beep=FALSE; - } - - /*try to be best-effort in giving real local or routable contact address */ - linphone_call_set_contact_op(call); - if (params){ - linphone_call_set_new_params(call,params); - linphone_call_prepare_ice(call,TRUE); - linphone_call_make_local_media_description(call); - sal_call_set_local_media_description(call->op,call->localdesc); - sal_op_set_sent_custom_header(call->op,params->custom_headers); - } - - /*give a chance a set card prefered sampling frequency*/ - if (call->localdesc->streams[0].max_rate>0) { - ms_message ("configuring prefered card sampling rate to [%i]",call->localdesc->streams[0].max_rate); - if (lc->sound_conf.play_sndcard) - ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate); - if (lc->sound_conf.capt_sndcard) - ms_snd_card_set_preferred_sample_rate(lc->sound_conf.capt_sndcard, call->localdesc->streams[0].max_rate); - } - - if (!was_ringing && call->audiostream->ms.state==MSStreamInitialized && !lc->use_files){ - audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); - } - - linphone_call_update_remote_session_id_and_ver(call); - - sal_call_accept(call->op); - linphone_core_notify_display_status(lc,_("Connected.")); - lc->current_call=call; - linphone_call_set_state(call,LinphoneCallConnected,"Connected"); - new_md=sal_call_get_final_media_description(call->op); - linphone_call_stop_ice_for_inactive_streams(call, new_md); - if (new_md){ - linphone_core_update_streams(lc, call, new_md, LinphoneCallStreamsRunning); - linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); - }else call->expect_media_in_ack=TRUE; - - ms_message("call answered."); - return 0; -} - -int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error){ - sal_call_terminate(call->op); - - /*stop ringing*/ - linphone_core_stop_ringing(lc); - linphone_call_stop_media_streams(call); - -#ifdef BUILD_UPNP - linphone_call_delete_upnp_session(call); -#endif //BUILD_UPNP - - linphone_core_notify_display_status(lc,_("Call aborted") ); - linphone_call_set_state(call,LinphoneCallError,error); - return 0; -} - -static void terminate_call(LinphoneCore *lc, LinphoneCall *call){ - if (call->state==LinphoneCallIncomingReceived){ - if (call->non_op_error.reason!=SalReasonRequestTimeout) - call->non_op_error.reason=SalReasonDeclined; - } - /*stop ringing*/ - linphone_core_stop_ringing(lc); - - linphone_call_stop_media_streams(call); - -#ifdef BUILD_UPNP - linphone_call_delete_upnp_session(call); -#endif //BUILD_UPNP - - linphone_core_notify_display_status(lc,_("Call ended") ); - linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); -} - -int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri){ - - if (call->state==LinphoneCallIncomingReceived){ - char *real_url=NULL; - LinphoneAddress *real_parsed_url=linphone_core_interpret_url(lc,redirect_uri); - if (!real_parsed_url){ - /* bad url */ - ms_error("Bad redirect URI: %s", redirect_uri?redirect_uri:"NULL"); - return -1; - } - real_url=linphone_address_as_string (real_parsed_url); - sal_call_decline(call->op,SalReasonRedirect,real_url); - ms_free(real_url); - sal_error_info_set(&call->non_op_error,SalReasonRedirect,603,"Call redirected",NULL); - terminate_call(lc,call); - linphone_address_unref(real_parsed_url); - }else{ - ms_error("Bad state for call redirection."); - return -1; - } - return 0; -} - -int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) { - LinphoneCall *call; - if (the_call == NULL){ - call = linphone_core_get_current_call(lc); - if (bctbx_list_size(lc->calls)==1){ - call=(LinphoneCall*)lc->calls->data; - }else{ - ms_warning("No unique call to terminate !"); +int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { + if (call == NULL) { + call = get_unique_call(lc); + if (call == NULL) { + ms_warning("No unique call to accept!"); return -1; } } - else - { - call = the_call; - } - ms_message("Terminate call [%p] which is currently in state %s", call, linphone_call_state_to_string(call->state)); - switch (call->state) { - case LinphoneCallReleased: - case LinphoneCallEnd: - case LinphoneCallError: - ms_warning("No need to terminate a call [%p] in state [%s]",call,linphone_call_state_to_string(call->state)); - return -1; - case LinphoneCallIncomingReceived: - case LinphoneCallIncomingEarlyMedia: - return linphone_core_decline_call(lc,call,LinphoneReasonDeclined); - case LinphoneCallOutgoingInit: { - /* In state OutgoingInit, op has to be destroyed */ - sal_op_release(call->op); - call->op = NULL; - break; - } - default: - sal_call_terminate(call->op); - break; - } - terminate_call(lc,call); - return 0; + return linphone_call_accept_with_params(call, params); } -int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall * call, LinphoneReason reason){ - if (call->state!=LinphoneCallIncomingReceived && call->state!=LinphoneCallIncomingEarlyMedia){ - ms_error("linphone_core_decline_call(): Cannot decline a call that is in state %s",linphone_call_state_to_string(call->state)); - return -1; - } +int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri) { + return linphone_call_redirect(call, redirect_uri); +} - sal_call_decline(call->op,linphone_reason_to_sal(reason),NULL); - terminate_call(lc,call); - return 0; +int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *call) { + if (call == NULL) { + call = get_unique_call(lc); + if (call == NULL) { + ms_warning("No unique call to terminate!"); + return -1; + } + } + return linphone_call_terminate(call); } int linphone_core_terminate_all_calls(LinphoneCore *lc){ @@ -3928,6 +3363,10 @@ int linphone_core_terminate_all_calls(LinphoneCore *lc){ return 0; } +int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall *call, LinphoneReason reason) { + return linphone_call_decline(call, reason); +} + const bctbx_list_t *linphone_core_get_calls(LinphoneCore *lc) { return lc->calls; } @@ -3940,46 +3379,8 @@ LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc){ return lc->current_call; } -int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call){ - int err=_linphone_core_pause_call(lc,call); - if (err==0) call->paused_by_app=TRUE; - return err; -} - -/* Internal version that does not play tone indication*/ -int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call){ - const char *subject=NULL; - - if (call->state!=LinphoneCallStreamsRunning && call->state!=LinphoneCallPausedByRemote){ - ms_warning("Cannot pause this call, it is not active."); - return -1; - } - if (sal_media_description_has_dir(call->resultdesc, SalStreamSendRecv)) { - subject = "Call on hold"; - } else if (sal_media_description_has_dir(call->resultdesc, SalStreamRecvOnly)) { - subject = "Call on hold for me too"; - } else { - ms_error("No reason to pause this call, it is already paused or inactive."); - return -1; - } - call->broken = FALSE; - linphone_call_set_state(call, LinphoneCallPausing, "Pausing call"); - linphone_call_make_local_media_description(call); -#ifdef BUILD_UPNP - if(call->upnp_session != NULL) { - linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); - } -#endif //BUILD_UPNP - sal_call_set_local_media_description(call->op,call->localdesc); - if (sal_call_update(call->op,subject,FALSE) != 0){ - linphone_core_notify_display_warning(lc,_("Could not pause the call")); - } - lc->current_call=NULL; - linphone_core_notify_display_status(lc,_("Pausing the current call...")); - if (call->audiostream || call->videostream || call->textstream) - linphone_call_stop_media_streams (call); - call->paused_by_app=FALSE; - return 0; +int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) { + return linphone_call_pause(call); } int linphone_core_pause_all_calls(LinphoneCore *lc){ @@ -3988,7 +3389,7 @@ int linphone_core_pause_all_calls(LinphoneCore *lc){ LinphoneCall *call=(LinphoneCall *)elem->data; LinphoneCallState cs=linphone_call_get_state(call); if (cs==LinphoneCallStreamsRunning || cs==LinphoneCallPausedByRemote){ - _linphone_core_pause_call(lc,call); + _linphone_call_pause(call); } } return 0; @@ -4006,7 +3407,7 @@ int linphone_core_preempt_sound_resources(LinphoneCore *lc){ current_call=linphone_core_get_current_call(lc); if(current_call != NULL){ ms_message("Pausing automatically the current call."); - err = _linphone_core_pause_call(lc,current_call); + err = _linphone_call_pause(current_call); } if (lc->ringstream){ linphone_core_stop_ringing(lc); @@ -4014,60 +3415,8 @@ int linphone_core_preempt_sound_resources(LinphoneCore *lc){ return err; } -int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ - char temp[255]={0}; - const char *subject="Call resuming"; - char *tmp; - - if(call->state!=LinphoneCallPaused ){ - ms_warning("we cannot resume a call that has not been established and paused before"); - return -1; - } - if (call->params->in_conference==FALSE){ - if (linphone_core_sound_resources_locked(lc)){ - ms_warning("Cannot resume call %p because another call is locking the sound resources.",call); - return -1; - } - linphone_core_preempt_sound_resources(lc); - ms_message("Resuming call %p",call); - } - - call->was_automatically_paused=FALSE; - call->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 (call->audiostream) audio_stream_play(call->audiostream, NULL); - - linphone_call_make_local_media_description(call); -#ifdef BUILD_UPNP - if(call->upnp_session != NULL) { - linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); - } -#endif //BUILD_UPNP - if (!lc->sip_conf.sdp_200_ack){ - sal_call_set_local_media_description(call->op,call->localdesc); - } else { - sal_call_set_local_media_description(call->op,NULL); - } - sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); - if (call->params->in_conference && !call->current_params->in_conference) subject="Conference"; - if ( sal_call_update(call->op,subject,FALSE) != 0){ - return -1; - } - linphone_call_set_state(call,LinphoneCallResuming,"Resuming"); - if (call->params->in_conference==FALSE) - lc->current_call=call; - snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",(tmp = linphone_call_get_remote_address_as_string(call))); - ms_free(tmp); - linphone_core_notify_display_status(lc,temp); - - if (lc->sip_conf.sdp_200_ack){ - /*we are NOT offering, set local media description after sending the call so that we are ready to - process the remote offer when it will arrive*/ - sal_call_set_local_media_description(call->op,call->localdesc); - } - return 0; +int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call) { + return linphone_call_resume(call); } static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *raddr){ diff --git a/coreapi/misc.c b/coreapi/misc.c index 1d36282c7..f75fdc89b 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -69,8 +69,12 @@ bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloa return FALSE; } -bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt){ - if (pt->type==PAYLOAD_VIDEO) return TRUE; +bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt) { + return linphone_payload_type_is_vbr(pt); +} + +bool_t linphone_payload_type_is_vbr(const LinphonePayloadType *pt) { + if (pt->type == PAYLOAD_VIDEO) return TRUE; return !!(pt->flags & PAYLOAD_TYPE_IS_VBR); } @@ -85,12 +89,20 @@ int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, return -1; } -int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){ +int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt) { + return linphone_payload_type_get_number(pt); +} + +int linphone_payload_type_get_number(const PayloadType *pt) { return payload_type_get_number(pt); } -void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number){ - payload_type_set_number(pt,number); +void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number) { + linphone_payload_type_set_number(pt, number); +} + +void linphone_payload_type_set_number(PayloadType *pt, int number) { + payload_type_set_number(pt, number); } const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){ @@ -772,8 +784,7 @@ const char *linphone_ice_state_to_string(LinphoneIceState state){ return "invalid"; } -void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call) -{ +void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) { IceCheckList *audio_check_list; IceCheckList *video_check_list; IceCheckList *text_check_list; @@ -891,7 +902,7 @@ void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDes } } - linphone_core_update_ice_state_in_call_stats(call); + linphone_call_update_ice_state_in_call_stats(call); } void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) { diff --git a/coreapi/private.h b/coreapi/private.h index e0648022f..66407ad0f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -414,7 +414,7 @@ const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc); const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, bool_t ignore_realm); void linphone_core_update_proxy_register(LinphoneCore *lc); -int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); +int linphone_call_abort(LinphoneCall *call, const char *error); const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphonePresenceModel *presence); @@ -532,7 +532,7 @@ void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, Linphone int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); LINPHONE_PUBLIC void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable); LINPHONE_PUBLIC void linphone_core_enable_short_turn_refresh(LinphoneCore *lc, bool_t enable); -void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call); +void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call); LINPHONE_PUBLIC void linphone_call_stats_fill(LinphoneCallStats *stats, MediaStream *ms, OrtpEvent *ev); void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDescription *result); void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy); @@ -579,14 +579,14 @@ void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); void linphone_core_stop_waiting(LinphoneCore *lc); -int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); -int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination/* = NULL if to be taken from the call log */); -int linphone_core_restart_invite(LinphoneCore *lc, LinphoneCall *call); +int linphone_call_proceed_with_invite_if_ready(LinphoneCall *call, LinphoneProxyConfig *dest_proxy); +int linphone_call_start_invite(LinphoneCall *call, const LinphoneAddress *destination/* = NULL if to be taken from the call log */); +int linphone_call_restart_invite(LinphoneCall *call); /* * param automatic_offering aims is to take into account previous answer for video in case of automatic re-invite. * Purpose is to avoid to re-ask video previously declined */ -int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call); -int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState next_state, const char *state_info); +int linphone_call_start_update(LinphoneCall *call); +int linphone_call_start_accept_update(LinphoneCall *call, LinphoneCallState next_state, const char *state_info); void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call); bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md); extern SalCallbacks linphone_sal_callbacks; @@ -946,7 +946,7 @@ LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *l void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason); void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile); LINPHONE_PUBLIC const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id); -int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info); +int _linphone_call_accept_update(LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info); typedef struct _LinphoneTaskList{ MSList *hooks; @@ -1148,7 +1148,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call); void linphone_call_make_local_media_description_with_params(LinphoneCore *lc, LinphoneCall *call, LinphoneCallParams *params); void linphone_call_increment_local_media_description(LinphoneCall *call); void linphone_call_fill_media_multicast_addr(LinphoneCall *call); -void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state); +void linphone_call_update_streams(LinphoneCall *call, SalMediaDescription *new_md, LinphoneCallState target_state); bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, const PayloadType *pt, int bandwidth_limit); @@ -1199,7 +1199,7 @@ 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_core_pause_call(LinphoneCore *lc, LinphoneCall *call); +int _linphone_call_pause(LinphoneCall *call); /*conferencing subsystem*/ void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index de92d2263..c1681918a 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -679,7 +679,7 @@ int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortB * uPnP Core interfaces */ -int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) { +int linphone_call_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; @@ -725,7 +725,7 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool -int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { +int linphone_call_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { bool_t audio = FALSE; bool_t video = FALSE; int i; @@ -741,14 +741,14 @@ int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, } } - return linphone_core_update_upnp_audio_video(call, audio, video); + return linphone_call_update_upnp_audio_video(call, audio, video); } -int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { - return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); +int linphone_call_update_upnp(LinphoneCall *call) { + return linphone_call_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); } -void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) { +void linphone_call_update_upnp_state_in_call_stats(LinphoneCall *call) { call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state; call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state; } @@ -799,7 +799,7 @@ int linphone_upnp_call_process(LinphoneCall *call) { /* * Update stat */ - linphone_core_update_upnp_state_in_call_stats(call); + linphone_call_update_upnp_state_in_call_stats(call); /* * Update session state @@ -832,13 +832,13 @@ int linphone_upnp_call_process(LinphoneCall *call) { switch (call->state) { case LinphoneCallUpdating: - linphone_core_start_update_call(lc, call); + linphone_call_start_update(call); break; case LinphoneCallUpdatedByRemote: - linphone_core_start_accept_call_update(lc, call,call->prevstate,linphone_call_state_to_string(call->prevstate)); + linphone_call_start_accept_update(call, call->prevstate, linphone_call_state_to_string(call->prevstate)); break; case LinphoneCallOutgoingInit: - linphone_core_proceed_with_invite_if_ready(lc, call, NULL); + linphone_call_proceed_with_invite_if_ready(call, NULL); break; case LinphoneCallIdle: linphone_call_update_local_media_description_from_ice_or_upnp(call); @@ -1056,7 +1056,7 @@ bool_t linphone_core_upnp_hook(void *data) { return TRUE; } -int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { +int linphone_call_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { int i; SalStreamDescription *stream; UpnpStream *upnpStream; diff --git a/coreapi/upnp.h b/coreapi/upnp.h index e666743a8..a8abeb16b 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -27,9 +27,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. typedef struct _UpnpSession UpnpSession; typedef struct _UpnpContext UpnpContext; -int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); -int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); -int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); +int linphone_call_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); +int linphone_call_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); +int linphone_call_update_upnp(LinphoneCall *call); int linphone_upnp_call_process(LinphoneCall *call); UpnpSession* linphone_upnp_session_new(LinphoneCall *call); @@ -43,6 +43,6 @@ LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx); const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx); int linphone_upnp_context_get_external_port(UpnpContext *ctx); bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *ctx); -void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call); +void linphone_call_update_upnp_state_in_call_stats(LinphoneCall *call); #endif //LINPHONE_UPNP_H diff --git a/include/linphone/call.h b/include/linphone/call.h index 2bd4b347f..61d2260c6 100644 --- a/include/linphone/call.h +++ b/include/linphone/call.h @@ -352,6 +352,176 @@ LINPHONE_PUBLIC RtpTransport * linphone_call_get_meta_rtp_transport(LinphoneCall **/ LINPHONE_PUBLIC RtpTransport * linphone_call_get_meta_rtcp_transport(LinphoneCall *call, int stream_index); +/** + * Pauses the call. If a music file has been setup using linphone_core_set_play_file(), + * this file will be played to the remote user. + * The only way to resume a paused call is to call linphone_call_resume(). + * @param[in] call LinphoneCall object + * @return 0 on success, -1 on failure + * @see linphone_call_resume() +**/ +LINPHONE_PUBLIC int linphone_call_pause(LinphoneCall *call); + +/** + * Resumes a call. + * The call needs to have been paused previously with linphone_call_pause(). + * @param[in] call LinphoneCall object + * @return 0 on success, -1 on failure + * @see linphone_call_pause() +**/ +LINPHONE_PUBLIC int linphone_call_resume(LinphoneCall *call); + +/** + * Terminates a call. + * @param[in] call LinphoneCall object + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_terminate(LinphoneCall *call); + +/** + * Redirect the specified call to the given redirect URI. + * @param[in] call A LinphoneCall object + * @param[in] redirect_uri The URI to redirect the call to + * @return 0 if successful, -1 on error. + */ +LINPHONE_PUBLIC int linphone_call_redirect(LinphoneCall *call, const char *redirect_uri); + +/** + * Decline a pending incoming call, with a reason. + * @param[in] call A LinphoneCall object that must be in the IncomingReceived state + * @param[in] reason The reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_decline(LinphoneCall * call, LinphoneReason reason); + +/** + * Accept an incoming call. + * + * Basically the application is notified of incoming calls within the + * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive + * a LinphoneCallIncoming event with the associated LinphoneCall object. + * The application can later accept the call using this method. + * @param[in] call A LinphoneCall object + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_accept(LinphoneCall *call); + +/** + * Accept an incoming call, with parameters. + * + * Basically the application is notified of incoming calls within the + * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive + * a LinphoneCallIncoming event with the associated LinphoneCall object. + * The application can later accept the call using this method. + * @param[in] call A LinphoneCall object + * @param[in] params The specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_accept_with_params(LinphoneCall *call, const LinphoneCallParams *params); + +/** + * Accept an early media session for an incoming call. + * This is identical as calling linphone_call_accept_early_media_with_params() with NULL parameters. + * @param[in] call A LinphoneCall object + * @return 0 if successful, -1 otherwise + * @see linphone_call_accept_early_media_with_params() +**/ +LINPHONE_PUBLIC int linphone_call_accept_early_media(LinphoneCall *call); + +/** + * When receiving an incoming, accept to start a media session as early-media. + * This means the call is not accepted but audio & video streams can be established if the remote party supports early media. + * However, unlike after call acceptance, mic and camera input are not sent during early-media, though received audio & video are played normally. + * The call can then later be fully accepted using linphone_call_accept() or linphone_call_accept_with_params(). + * @param[in] call A LinphoneCall object + * @param[in] params The call parameters to use (can be NULL) + * @return 0 if successful, -1 otherwise +**/ +LINPHONE_PUBLIC int linphone_call_accept_early_media_with_params(LinphoneCall *call, const LinphoneCallParams *params); + +/** + * Updates a running call according to supplied call parameters or parameters changed in the LinphoneCore. + * In this version this is limited to the following use cases: + * - setting up/down the video stream according to the video parameter of the LinphoneCallParams (see linphone_call_params_enable_video() ). + * - changing the size of the transmitted video after calling linphone_core_set_preferred_video_size() + * In case no changes are requested through the LinphoneCallParams argument, then this argument can be omitted and set to NULL. + * WARNING: Updating a call in the LinphoneCallPaused state will still result in a paused call even if the media directions set in the + * params are sendrecv. To resume a paused call, you need to call linphone_call_resume(). + * @param[in] call A LinphoneCall update + * @param[in] params The new call parameters to use (may be NULL) + * @return 0 if successful, -1 otherwise. +**/ +LINPHONE_PUBLIC int linphone_call_update(LinphoneCall *call, const LinphoneCallParams *params); + +/** + * When receiving a #LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer. + * + * When receiving a #LinphoneCallUpdatedByRemote state notification (ie an incoming reINVITE), the default behaviour of + * LinphoneCore is defined by the "defer_update_default" option of the "sip" section of the config. If this option is 0 (the default) + * then the LinphoneCore automatically answers the reINIVTE with call parameters unchanged. + * However when for example when the remote party updated the call to propose a video stream, it can be useful + * to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during + * the call state notification, to deactivate the automatic answer that would just confirm the audio but reject the video. + * Then, when the user responds to dialog prompt, it becomes possible to call linphone_call_accept_update() to answer + * the reINVITE, with eventually video enabled in the LinphoneCallParams argument. + * + * The #LinphoneCallUpdatedByRemote notification can also arrive when receiving an INVITE without SDP. In such case, an unchanged offer is made + * in the 200Ok, and when the ACK containing the SDP answer is received, #LinphoneCallUpdatedByRemote is triggered to notify the application of possible + * changes in the media session. However in such case defering the update has no meaning since we just generating an offer. + * + * @param[in] call A LinphoneCall object + * @return 0 if successful, -1 if the linphone_call_defer_update() was done outside a valid #LinphoneCallUpdatedByRemote notification +**/ +LINPHONE_PUBLIC int linphone_call_defer_update(LinphoneCall *call); + +/** + * Accept call modifications initiated by other end. + * + * This call may be performed in response to a #LinphoneCallUpdatedByRemote state notification. + * When such notification arrives, the application can decide to call linphone_call_defer_update() so that it can + * have the time to prompt the user. linphone_call_get_remote_params() can be used to get information about the call parameters + * requested by the other party, such as whether a video stream is requested. + * + * When the user accepts or refuse the change, linphone_call_accept_update() can be done to answer to the other party. + * If params is NULL, then the same call parameters established before the update request will continue to be used (no change). + * If params is not NULL, then the update will be accepted according to the parameters passed. + * Typical example is when a user accepts to start video, then params should indicate that video stream should be used + * (see linphone_call_params_enable_video()). + * @param[in] call A LinphoneCall object + * @param[in] params A LinphoneCallParams object describing the call parameters to accept + * @return 0 if successful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state) +**/ +LINPHONE_PUBLIC int linphone_call_accept_update(LinphoneCall *call, const LinphoneCallParams *params); + +/** + * Performs a simple call transfer to the specified destination. + * The remote endpoint is expected to issue a new call to the specified destination. + * The current call remains active and thus can be later paused or terminated. + * It is possible to follow the progress of the transfer provided that transferee sends notification about it. + * In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party. + * The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallConnected. + * @param[in] call The call to be transfered + * @param[in] refer_to The destination the call is to be refered to + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_transfer(LinphoneCall *call, const char *refer_to); + +/** + * Transfers a call to destination of another running call. This is used for "attended transfer" scenarios. + * The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately. + * The destination call is a call previously established to introduce the transfered person. + * This method will send a transfer request to the transfered person. The phone of the transfered is then + * expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically + * close the call with us (the 'dest' call). + * It is possible to follow the progress of the transfer provided that transferee sends notification about it. + * In this case, the transfer_state_changed callback of the #LinphoneCoreVTable is invoked to notify of the state of the new call at the other party. + * The notified states are #LinphoneCallOutgoingInit , #LinphoneCallOutgoingProgress, #LinphoneCallOutgoingRinging and #LinphoneCallConnected. + * @param[in] call A running call you want to transfer + * @param[in] dest A running call whose remote person will receive the transfer + * @return 0 on success, -1 on failure +**/ +LINPHONE_PUBLIC int linphone_call_transfer_to_another(LinphoneCall *call, LinphoneCall *dest); + /** * @} */ diff --git a/include/linphone/core.h b/include/linphone/core.h index 752975ff1..b31a99688 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -1146,8 +1146,9 @@ LINPHONE_PUBLIC LinphoneCall * linphone_core_invite_address_with_params(Linphone * @param[in] refer_to The destination the call is to be refered to * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_transfer() instead **/ -LINPHONE_PUBLIC int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *refer_to); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *refer_to); /** * Transfers a call to destination of another running call. This is used for "attended transfer" scenarios. @@ -1164,8 +1165,9 @@ LINPHONE_PUBLIC int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall * * @param[in] dest A running call whose remote person will receive the transfer * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_transfer_to_another() instead **/ -LINPHONE_PUBLIC int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest); /** * Start a new call as a consequence of a transfer request received from a call. @@ -1217,8 +1219,9 @@ LINPHONE_PUBLIC LinphoneCall *linphone_core_get_current_call(const LinphoneCore * @param[in] call The LinphoneCall object representing the call to be answered * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_accept() instead **/ -LINPHONE_PUBLIC int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call); /** * Accept an incoming call, with parameters. @@ -1233,8 +1236,9 @@ LINPHONE_PUBLIC int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *ca * @param[in] params The specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_accept_with_params() instead **/ -LINPHONE_PUBLIC int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); /** * When receiving an incoming, accept to start a media session as early-media. @@ -1246,8 +1250,9 @@ LINPHONE_PUBLIC int linphone_core_accept_call_with_params(LinphoneCore *lc, Linp * @param[in] params The call parameters to use (can be NULL) * @return 0 if successful, -1 otherwise * @ingroup call_control + * @deprecated Use linphone_call_accept_early_media_with_params() instead **/ -LINPHONE_PUBLIC int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall* call, const LinphoneCallParams* params); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_accept_early_media_with_params(LinphoneCore* lc, LinphoneCall* call, const LinphoneCallParams* params); /** * Accept an early media session for an incoming call. @@ -1257,8 +1262,9 @@ LINPHONE_PUBLIC int linphone_core_accept_early_media_with_params(LinphoneCore* l * @return 0 if successful, -1 otherwise * @ingroup call_control * @see linphone_core_accept_early_media_with_params() + * @deprecated Use linphone_call_accept_early_media() instead **/ -LINPHONE_PUBLIC int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneCall* call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneCall* call); /** * Terminates a call. @@ -1266,8 +1272,9 @@ LINPHONE_PUBLIC int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneC * @param[in] call The LinphoneCall object representing the call to be terminated * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_terminate() instead **/ -LINPHONE_PUBLIC int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *call); /** * Redirect the specified call to the given redirect URI. @@ -1276,8 +1283,9 @@ LINPHONE_PUBLIC int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall * @param[in] redirect_uri The URI to redirect the call to * @return 0 if successful, -1 on error. * @ingroup call_control + * @deprecated Use linphone_call_redirect() instead */ -LINPHONE_PUBLIC int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri); /** * Decline a pending incoming call, with a reason. @@ -1286,8 +1294,9 @@ LINPHONE_PUBLIC int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall * * @param[in] reason The reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy * @return 0 on success, -1 on failure * @ingroup call_control + * @deprecated Use linphone_call_decline() instead **/ -LINPHONE_PUBLIC int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall * call, LinphoneReason reason); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall * call, LinphoneReason reason); /** * Terminates all the calls. @@ -1306,8 +1315,9 @@ LINPHONE_PUBLIC int linphone_core_terminate_all_calls(LinphoneCore *lc); * @return 0 on success, -1 on failure * @ingroup call_control * @see linphone_core_resume_call() + * @deprecated Use linphone_call_pause() instead **/ -LINPHONE_PUBLIC int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call); /** * Pause all currently running calls. @@ -1325,8 +1335,9 @@ LINPHONE_PUBLIC int linphone_core_pause_all_calls(LinphoneCore *lc); * @return 0 on success, -1 on failure * @ingroup call_control * @see linphone_core_pause_call() + * @deprecated Use linphone_call_resume() instead **/ -LINPHONE_PUBLIC int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call); /** * Updates a running call according to supplied call parameters or parameters changed in the LinphoneCore. @@ -1341,8 +1352,9 @@ LINPHONE_PUBLIC int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *ca * @param[in] params The new call parameters to use (may be NULL) * @return 0 if successful, -1 otherwise. * @ingroup call_control + * @deprecated Use linphone_call_update() instead **/ -LINPHONE_PUBLIC int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); /** * When receiving a #LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer. @@ -1364,8 +1376,9 @@ LINPHONE_PUBLIC int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *ca * @param[in] call The call for which to defer the update * @return 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a valid #LinphoneCallUpdatedByRemote notification * @ingroup call_control + * @deprecated Use linphone_call_defer_update() instead **/ -LINPHONE_PUBLIC int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call); /** * Accept call modifications initiated by other end. @@ -1385,8 +1398,9 @@ LINPHONE_PUBLIC int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCa * @param[in] params A LinphoneCallParams object describing the call parameters to accept * @return 0 if successful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state) * @ingroup call_control + * @deprecated Use linphone_call_accept_update() instead **/ -LINPHONE_PUBLIC int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params); /** * Create a LinphoneCallParams suitable for linphone_core_invite_with_params(), linphone_core_accept_call_with_params(), linphone_core_accept_early_media_with_params(), @@ -1781,8 +1795,9 @@ LINPHONE_PUBLIC bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, cons * @param[in] pt The #LinphonePayloadType we want to know * @return TRUE if the payload type represents a VBR codec, FALSE if disabled. * @ingroup media_parameters + * @deprecated Use linphone_payload_type_is_vbr() instead */ -LINPHONE_PUBLIC bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt); +LINPHONE_PUBLIC LINPHONE_DEPRECATED bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt); /** * Set an explicit bitrate (IP bitrate, not codec bitrate) for a given codec, in kbit/s. @@ -1839,15 +1854,17 @@ LINPHONE_PUBLIC LinphonePayloadType* linphone_core_find_payload_type(LinphoneCor /** * Returns the payload type number assigned for this codec. * @ingroup media_parameters + * @deprecated Use linphone_payload_type_get_number() instead **/ -LINPHONE_PUBLIC int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt); +LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt); /** * Force a number for a payload type. The LinphoneCore does payload type number assignment automatically. THis function is to be used mainly for tests, in order * to override the automatic assignment mechanism. * @ingroup media_parameters + * @deprecated Use linphone_payload_type_set_number() instead **/ -LINPHONE_PUBLIC void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number); +LINPHONE_PUBLIC LINPHONE_DEPRECATED void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number); LINPHONE_PUBLIC const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt); diff --git a/include/linphone/payload_type.h b/include/linphone/payload_type.h index 746f61f90..e4562de8e 100644 --- a/include/linphone/payload_type.h +++ b/include/linphone/payload_type.h @@ -62,6 +62,28 @@ LINPHONE_PUBLIC const char * linphone_payload_type_get_mime_type(const LinphoneP */ LINPHONE_PUBLIC int linphone_payload_type_get_channels(const LinphonePayloadType *pt); +/** + * Returns the payload type number assigned for this codec. + * @param[in] pt LinphonePayloadType object + * @return The number of the payload type +**/ +LINPHONE_PUBLIC int linphone_payload_type_get_number(const PayloadType *pt); + +/** + * Force a number for a payload type. The LinphoneCore does payload type number assignment automatically. THis function is to be used mainly for tests, in order + * to override the automatic assignment mechanism. + * @param[in] pt LinphonePayloadType object + * @param[in] number The number to assign to the payload type +**/ +LINPHONE_PUBLIC void linphone_payload_type_set_number(PayloadType *pt, int number); + +/** + * Tells whether the specified payload type represents a variable bitrate codec. + * @param[in] pt LinphonePayloadType object + * @return TRUE if the payload type represents a VBR codec, FALSE if disabled. + */ +LINPHONE_PUBLIC bool_t linphone_payload_type_is_vbr(const LinphonePayloadType *pt); + /** * @} **/