/** * Merge a call into a conference. * @param lc the linphone core * @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state. * * If this is the first call that enters the conference, the virtual conference will be created automatically. * If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference. * If the call was in paused state, then it is automatically resumed when entering into the conference. * * @returns 0 if successful, -1 otherwise. **/ int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){ LinphoneConference *conf=&lc->conf_ctx; if (call->current_params.in_conference){ ms_error("Already in conference"); return -1; } conference_check_init(&lc->conf_ctx, lp_config_get_int(lc->config, "sound","conference_rate",16000)); if (call->state==LinphoneCallPaused){ call->params.in_conference=TRUE; call->params.has_video=FALSE; linphone_core_resume_call(lc,call); }else if (call->state==LinphoneCallStreamsRunning){ LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call)); params->in_conference=TRUE; params->has_video=FALSE; if (call->audiostream || call->videostream){ linphone_call_stop_media_streams (call); /*free the audio & video local resources*/ } if (call==lc->current_call){ lc->current_call=NULL; } /*this will trigger a reINVITE that will later redraw the streams */ linphone_core_update_call(lc,call,params); linphone_call_params_destroy(params); add_local_endpoint(conf,lc); }else{ ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state)); return -1; } return 0; }
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ SalMediaDescription *oldmd=call->resultdesc; if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } if (new_md!=NULL){ sal_media_description_ref(new_md); call->media_pending=FALSE; }else{ call->media_pending=TRUE; } call->resultdesc=new_md; if (call->audiostream && call->audiostream->ticker){ /* we already started media: check if we really need to restart it*/ if (oldmd){ if (!media_parameters_changed(call,oldmd,new_md) && !call->playing_ringbacktone){ sal_media_description_unref(oldmd); if (call->all_muted){ ms_message("Early media finished, unmuting inputs..."); /*we were in early media, now we want to enable real media */ linphone_call_enable_camera (call,linphone_call_camera_enabled (call)); if (call->audiostream) linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc)); #ifdef VIDEO_ENABLED if (call->videostream && call->camera_active) video_stream_change_camera(call->videostream,lc->video_conf.device ); #endif } ms_message("No need to restart streams, SDP is unchanged."); return; }else{ ms_message("Media descriptions are different, need to restart the streams."); } } linphone_call_stop_media_streams (call); linphone_call_init_media_streams (call); } if (oldmd) sal_media_description_unref(oldmd); if (new_md) { bool_t all_muted=FALSE; bool_t send_ringbacktone=FALSE; if (call->audiostream==NULL){ /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/ linphone_call_init_media_streams (call); } if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){ send_ringbacktone=TRUE; } if (call->state==LinphoneCallIncomingEarlyMedia || (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){ all_muted=TRUE; } linphone_call_start_media_streams(call,all_muted,send_ringbacktone); } }
static void call_terminated(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL) return; switch(linphone_call_get_state(call)){ case LinphoneCallEnd: case LinphoneCallError: ms_warning("call_terminated: ignoring."); return; break; case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: call->reason=LinphoneReasonNotAnswered; break; default: break; } ms_message("Current call terminated..."); if (call->refer_pending){ linphone_core_start_refered_call(lc,call,NULL); } //we stop the call only if we have this current call or if we are in call if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) { linphone_core_stop_ringing(lc); } linphone_call_stop_media_streams(call); if (lc->vtable.show!=NULL) lc->vtable.show(lc); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call terminated.")); #ifdef BUILD_UPNP linphone_call_delete_upnp_session(call); #endif //BUILD_UPNP linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); }
static void call_terminated(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL) return; if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){ ms_warning("call_terminated: ignoring."); return; } ms_message("Current call terminated..."); //we stop the call only if we have this current call or if we are in call if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) { ring_stop(lc->ringstream); lc->ringstream=NULL; } linphone_call_stop_media_streams(call); if (lc->vtable.show!=NULL) lc->vtable.show(lc); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call terminated.")); linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); }
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ SalMediaDescription *oldmd=call->resultdesc; bool_t all_muted=FALSE; bool_t send_ringbacktone=FALSE; linphone_core_stop_ringing(lc); if (!new_md) { ms_error("linphone_core_update_streams() called with null media description"); return; } if (call->biggestdesc==NULL || new_md->n_total_streams>call->biggestdesc->n_total_streams){ /*we have been offered and now are ready to proceed, or we added a new stream*/ /*store the media description to remember the mapping of calls*/ if (call->biggestdesc){ sal_media_description_unref(call->biggestdesc); call->biggestdesc=NULL; } if (sal_call_is_offerer(call->op)) call->biggestdesc=sal_media_description_ref(call->localdesc); else call->biggestdesc=sal_media_description_ref(sal_call_get_remote_media_description(call->op)); } sal_media_description_ref(new_md); call->expect_media_in_ack=FALSE; call->resultdesc=new_md; if ((call->audiostream && call->audiostream->ms.ticker) || (call->videostream && call->videostream->ms.ticker)){ /* we already started media: check if we really need to restart it*/ if (oldmd){ int md_changed = media_parameters_changed(call, oldmd, new_md); if ((md_changed & SAL_MEDIA_DESCRIPTION_CODEC_CHANGED) || call->playing_ringbacktone) { ms_message("Media descriptions are different, need to restart the streams."); } else { if (md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) { if (call->all_muted){ ms_message("Early media finished, unmuting inputs..."); /*we were in early media, now we want to enable real media */ linphone_call_enable_camera (call,linphone_call_camera_enabled (call)); if (call->audiostream) linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc)); #ifdef VIDEO_ENABLED if (call->videostream && call->camera_enabled) video_stream_change_camera(call->videostream,lc->video_conf.device ); #endif } ms_message("No need to restart streams, SDP is unchanged."); goto end; }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); } if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED) { ms_message("Crypto parameters have changed, update them."); linphone_call_update_crypto_parameters(call, oldmd, new_md); } goto end; } } } linphone_call_stop_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->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){ send_ringbacktone=TRUE; } if (call->state==LinphoneCallIncomingEarlyMedia || (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){ all_muted=TRUE; } linphone_call_start_media_streams(call,all_muted,send_ringbacktone); if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){ linphone_core_play_named_tone(lc,LinphoneToneCallOnHold); } end: if (oldmd) sal_media_description_unref(oldmd); }
static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); char *msg486=_("User is busy."); char *msg480=_("User is temporarily unavailable."); /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ char *msg600=_("User does not want to be disturbed."); char *msg603=_("Call declined."); const char *msg=details; LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); LinphoneCall *referer=call->referer; if (call==NULL){ ms_warning("Call faillure reported on already terminated call."); return ; } if (lc->vtable.show) lc->vtable.show(lc); if (error==SalErrorNoResponse){ msg=_("No response."); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); }else if (error==SalErrorProtocol){ msg=details ? details : _("Protocol error."); if (lc->vtable.display_status) lc->vtable.display_status(lc, msg); }else if (error==SalErrorFailure){ switch(sr){ case SalReasonDeclined: msg=msg603; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg603); break; case SalReasonBusy: msg=msg486; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg486); break; case SalReasonRedirect: { ms_error("case SalReasonRedirect"); linphone_call_stop_media_streams(call); if ( call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress || call->state==LinphoneCallOutgoingRinging /*push case*/ || call->state==LinphoneCallOutgoingEarlyMedia){ LinphoneAddress* redirection_to = (LinphoneAddress*)sal_op_get_remote_contact_address(call->op); if( redirection_to ){ char* url = linphone_address_as_string(redirection_to); ms_error("Redirecting call [%p] to %s",call, url); ms_free(url); linphone_call_create_op(call); linphone_core_start_invite(lc, call, redirection_to); return; } } msg=_("Redirected"); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); } break; case SalReasonTemporarilyUnavailable: msg=msg480; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg480); break; case SalReasonNotFound: if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; case SalReasonDoNotDisturb: msg=msg600; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg600); break; case SalReasonUnsupportedContent: /*<this is for compatibility: linphone sent 415 because of SDP offer answer failure*/ case SalReasonNotAcceptable: //media_encryption_mandatory if (call->params.media_encryption == LinphoneMediaEncryptionSRTP && !linphone_core_is_media_encryption_mandatory(lc)) { int i; ms_message("Outgoing call [%p] failed with SRTP (SAVP) enabled",call); linphone_call_stop_media_streams(call); if ( call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress || call->state==LinphoneCallOutgoingRinging /*push case*/ || call->state==LinphoneCallOutgoingEarlyMedia){ ms_message("Retrying call [%p] with AVP",call); /* clear SRTP local params */ call->params.media_encryption = LinphoneMediaEncryptionNone; for(i=0; i<call->localdesc->n_active_streams; i++) { call->localdesc->streams[i].proto = SalProtoRtpAvp; memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); } linphone_core_restart_invite(lc, call); return; } } msg=_("Incompatible media parameters."); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; case SalReasonRequestPending: /*restore previous state, the application will decide to resubmit the action if relevant*/ call->reason=linphone_reason_from_sal(sr); linphone_call_set_state(call,call->prevstate,msg); return; break; default: if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Call failed.")); } } /*some call error are not fatal*/ switch (call->state) { case LinphoneCallUpdating: case LinphoneCallPausing: case LinphoneCallResuming: ms_message("Call error on state [%s], restoring previous state",linphone_call_state_to_string(call->prevstate)); call->reason=linphone_reason_from_sal(sr); linphone_call_set_state(call, call->prevstate,details); return; default: break; /*nothing to do*/ } linphone_core_stop_ringing(lc); linphone_call_stop_media_streams(call); #ifdef BUILD_UPNP linphone_call_delete_upnp_session(call); #endif //BUILD_UPNP call->reason=linphone_reason_from_sal(sr); if (sr==SalReasonDeclined){ linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); }else{ linphone_call_set_state(call,LinphoneCallError,details); if (sr==SalReasonBusy) linphone_core_play_named_tone(lc,LinphoneToneBusy); } if (referer){ /*notify referer of the failure*/ linphone_core_notify_refer_state(lc,referer,call); /*schedule automatic resume of the call. This must be done only after the notifications are completed due to dialog serialization of requests.*/ linphone_core_queue_task(lc,(belle_sip_source_func_t)resume_call_after_failed_transfer,linphone_call_ref(referer),"Automatic call resuming after failed transfer"); } }
static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); char *msg486=_("User is busy."); char *msg480=_("User is temporarily unavailable."); /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ char *msg600=_("User does not want to be disturbed."); char *msg603=_("Call declined."); const char *msg=details; LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL){ ms_warning("Call faillure reported on already cleaned call ?"); return ; } if (lc->vtable.show) lc->vtable.show(lc); if (error==SalErrorNoResponse){ msg=_("No response."); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); }else if (error==SalErrorProtocol){ msg=details ? details : _("Protocol error."); if (lc->vtable.display_status) lc->vtable.display_status(lc, msg); }else if (error==SalErrorFailure){ switch(sr){ case SalReasonDeclined: msg=msg603; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg603); break; case SalReasonBusy: msg=msg486; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg486); break; case SalReasonRedirect: msg=_("Redirected"); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; case SalReasonTemporarilyUnavailable: msg=msg480; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg480); break; case SalReasonNotFound: msg=_("Not found"); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; case SalReasonDoNotDisturb: msg=msg600; if (lc->vtable.display_status) lc->vtable.display_status(lc,msg600); break; case SalReasonMedia: msg=_("No common codecs"); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; default: if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Call failed.")); } } if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } linphone_call_stop_media_streams (call); if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg); else{ call->reason=LinphoneReasonDeclined; linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); } }