diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 59473474f..f8d35e15c 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -152,11 +152,12 @@ static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminat static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) { belle_sdp_session_description_t* sdp; SalReason reason; + if (op->base.remote_media){ + sal_media_description_unref(op->base.remote_media); + op->base.remote_media=NULL; + } if (extract_sdp(BELLE_SIP_MESSAGE(response),&sdp,&reason)==0) { if (sdp){ - if (op->base.remote_media){ - sal_media_description_unref(op->base.remote_media); - } op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); if (op->base.local_media) sdp_process(op); @@ -177,6 +178,7 @@ static int vfu_retry (void *user_data, unsigned int events) { sal_op_unref(op); return BELLE_SIP_STOP; } + static void call_process_response(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; belle_sip_request_t* ack; @@ -186,17 +188,17 @@ static void call_process_response(void *op_base, const belle_sip_response_event_ belle_sip_response_t* response=belle_sip_response_event_get_response(event); int code = belle_sip_response_get_status_code(response); belle_sip_header_content_type_t *header_content_type=NULL; - + belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event); if (!client_transaction) { ms_warning("Discarding stateless response [%i] on op [%p]",code,op); return; } req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); - set_or_update_dialog(op,belle_sip_response_event_get_dialog(event)); - dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; + set_or_update_dialog(op,dialog); + dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL; - ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,op->dialog,belle_sip_dialog_state_to_string(dialog_state)); + ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state)); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: @@ -218,14 +220,18 @@ static void call_process_response(void *op_base, const belle_sip_response_event_ if (op->dialog==NULL) call_set_released(op); } } - } else if (code >= 180 && code<300) { - handle_sdp_from_response(op,response); - op->base.root->callbacks.call_ringing(op); + } else if (code >= 180 && code<200) { + belle_sip_response_t *prev_response=belle_sip_object_data_get(BELLE_SIP_OBJECT(dialog),"early_response"); + if (!prev_response || code>belle_sip_response_get_status_code(prev_response)){ + handle_sdp_from_response(op,response); + op->base.root->callbacks.call_ringing(op); + } + belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref); } else if (code>=300){ call_set_error(op,response); if (op->dialog==NULL) call_set_released(op); } - } else if ( code >=200 + } else if (code >=200 && code<300 && strcmp("UPDATE",belle_sip_request_get_method(req))==0) { handle_sdp_from_response(op,response); diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index a364ce324..e2678dde0 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -560,20 +560,28 @@ const SalErrorInfo *sal_op_get_error_info(const SalOp *op){ return &op->error_info; } +static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){ + belle_sip_dialog_set_application_data(dialog,NULL); + sal_op_unref(op); + belle_sip_object_unref(dialog); +} + +static belle_sip_dialog_t *link_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){ + belle_sip_dialog_set_application_data(dialog,sal_op_ref(op)); + belle_sip_object_ref(dialog); + return dialog; +} + void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) { - /*check if dialog has changed*/ - if (dialog && dialog != op->dialog) { - ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op); - /*fixme, shouldn't we cancel previous dialog*/ - if (op->dialog) { - belle_sip_dialog_set_application_data(op->dialog,NULL); - belle_sip_object_unref(op->dialog); - sal_op_unref(op); + if (dialog==NULL) return; + ms_message("op [%p] : set_or_update_dialog() current=[%p] new=[%p]",op,op->dialog,dialog); + if (dialog && op->dialog!=dialog){ + if (op->dialog){ + /*FIXME: shouldn't we delete unconfirmed dialogs ?*/ + unlink_op_with_dialog(op,op->dialog); + op->dialog=NULL; } - op->dialog=dialog; - belle_sip_dialog_set_application_data(op->dialog,op); - sal_op_ref(op); - belle_sip_object_ref(op->dialog); + op->dialog=link_op_with_dialog(op,dialog); } } /*return reffed op*/ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 64f17f411..9b427506b 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -72,6 +72,32 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c #endif } +static void _clear_early_media_destinations(LinphoneCall *call, MediaStream *ms){ + RtpSession *session=ms->sessions.rtp_session; + rtp_session_clear_aux_remote_addr(session); + if (!call->ice_session) rtp_session_set_symmetric_rtp(session,TRUE);/*restore symmetric rtp if ICE is not used*/ +} + +static void clear_early_media_destinations(LinphoneCall *call){ + if (call->audiostream){ + _clear_early_media_destinations(call,(MediaStream*)call->audiostream); + } + if (call->videostream){ + _clear_early_media_destinations(call,(MediaStream*)call->videostream); + } +} + +static void prepare_early_media_forking(LinphoneCall *call){ + /*we need to disable symmetric rtp otherwise our outgoing streams will be switching permanently between the multiple destinations*/ + if (call->audiostream){ + rtp_session_set_symmetric_rtp(call->audiostream->ms.sessions.rtp_session,FALSE); + } + if (call->videostream){ + rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE); + } + +} + void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ SalMediaDescription *oldmd=call->resultdesc; bool_t all_muted=FALSE; @@ -98,6 +124,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia call->expect_media_in_ack=FALSE; 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){ int md_changed = media_parameters_changed(call, oldmd, new_md); @@ -146,6 +173,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia if ((call->state==LinphoneCallIncomingEarlyMedia || call->state==LinphoneCallOutgoingEarlyMedia) && !call->params.real_early_media){ all_muted=TRUE; } + if (call->params.real_early_media && call->state==LinphoneCallOutgoingEarlyMedia){ + prepare_early_media_forking(call); + } 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); @@ -276,6 +306,38 @@ static void call_received(SalOp *h){ linphone_core_notify_incoming_call(lc,call); } +static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md){ + SalMediaDescription *cur_md=call->resultdesc; + int i; + SalStreamDescription *ref_stream,*new_stream; + ms_message("Early media response received from another branch, checking if media can be forked to this new destination."); + + for (i=0;in_active_streams;++i){ + ref_stream=&cur_md->streams[i]; + new_stream=&md->streams[i]; + if (ref_stream->type==new_stream->type && ref_stream->payloads && new_stream->payloads){ + PayloadType *refpt, *newpt; + refpt=(PayloadType*)ref_stream->payloads->data; + newpt=(PayloadType*)new_stream->payloads->data; + if (strcmp(refpt->mime_type,newpt->mime_type)==0 && refpt->clock_rate==newpt->clock_rate + && payload_type_get_number(refpt)==payload_type_get_number(newpt)){ + MediaStream *ms=NULL; + if (ref_stream->type==SalAudio){ + ms=(MediaStream*)call->audiostream; + }else if (ref_stream->type==SalVideo){ + ms=(MediaStream*)call->videostream; + } + if (ms){ + RtpSession *session=ms->sessions.rtp_session; + const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr; + const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr; + rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); + } + } + } + } +} + static void call_ringing(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h); @@ -309,7 +371,7 @@ static void call_ringing(SalOp *h){ /*accept early media */ if (call->audiostream && audio_stream_started(call->audiostream)){ /*streams already started */ - ms_message("Early media already started."); + try_early_media_forking(call,md); return; } if (lc->vtable.show) lc->vtable.show(lc); diff --git a/mediastreamer2 b/mediastreamer2 index 916b6465a..e0ff32eeb 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 916b6465a2f46883cb2ad17ccc7b8fc210f1a942 +Subproject commit e0ff32eebb29671e855cb3cfe9c0ea46929419b4 diff --git a/oRTP b/oRTP index ae0fec375..a9ffd72d7 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit ae0fec37506f4cc5de7dfc8ca84321b7ae311bbe +Subproject commit a9ffd72d73d459e531fad094d18eb18f67870aba diff --git a/tester/call_tester.c b/tester/call_tester.c index 852bc4d68..a59313626 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -1200,7 +1200,8 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) { if (pauline_called_by_laure && enable_caller_privacy ) CU_ASSERT_EQUAL(linphone_call_params_get_privacy(linphone_call_get_current_params(pauline_called_by_laure)),LinphonePrivacyId); - + /*wait a bit for ACK to be sent*/ + wait_for_list(lcs,NULL,0,1000); linphone_core_terminate_all_calls(pauline->lc); CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,2000)); @@ -2162,6 +2163,86 @@ static void statistics_sent_at_call_termination() { } #ifdef VIDEO_ENABLED +/*this is call forking with early media managed at client side (not by flexisip server)*/ +static void multiple_early_media(void) { + LinphoneCoreManager* marie1 = linphone_core_manager_new("marie_early_rc"); + LinphoneCoreManager* marie2 = linphone_core_manager_new("marie_early_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); + MSList *lcs=NULL; + LinphoneCallParams *params=linphone_core_create_default_call_parameters(pauline->lc); + LinphoneVideoPolicy pol; + LinphoneCall *marie1_call; + LinphoneCall *marie2_call; + LinphoneCall *pauline_call; + int dummy=0; + char ringbackpath[256]; + snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix); + + pol.automatically_accept=1; + pol.automatically_initiate=1; + + linphone_core_enable_video(pauline->lc,TRUE,TRUE); + + linphone_core_enable_video(marie1->lc,TRUE,TRUE); + linphone_core_set_video_policy(marie1->lc,&pol); + /*use playfile for marie1 to avoid locking on capture card*/ + linphone_core_use_files(marie1->lc,TRUE); + linphone_core_set_play_file(marie1->lc,ringbackpath); + + linphone_core_enable_video(marie2->lc,TRUE,TRUE); + linphone_core_set_video_policy(marie2->lc,&pol); + linphone_core_set_audio_port_range(marie2->lc,40200,40300); + linphone_core_set_video_port_range(marie2->lc,40400,40500); + /*use playfile for marie2 to avoid locking on capture card*/ + linphone_core_use_files(marie2->lc,TRUE); + linphone_core_set_play_file(marie2->lc,ringbackpath); + + + lcs=ms_list_append(lcs,marie1->lc); + lcs=ms_list_append(lcs,marie2->lc); + lcs=ms_list_append(lcs,pauline->lc); + + linphone_call_params_enable_early_media_sending(params,TRUE); + linphone_call_params_enable_video(params,TRUE); + + linphone_core_invite_address_with_params(pauline->lc,marie1->identity,params); + linphone_call_params_destroy(params); + + CU_ASSERT_TRUE(wait_for_list(lcs, &marie1->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingEarlyMedia,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,3000)); + + pauline_call=linphone_core_get_current_call(pauline->lc); + marie1_call=linphone_core_get_current_call(marie1->lc); + marie2_call=linphone_core_get_current_call(marie2->lc); + + /*wait a bit that streams are established*/ + wait_for_list(lcs,&dummy,1,6000); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>70); + + linphone_core_accept_call(marie1->lc,linphone_core_get_current_call(marie1->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallStreamsRunning,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,1,3000)); + + /*marie2 should get her call terminated*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallEnd,1,1000)); + + /*wait a bit that streams are established*/ + wait_for_list(lcs,&dummy,1,1000); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>71); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>71); + + linphone_core_terminate_all_calls(pauline->lc); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallEnd,1,1000)); + + ms_list_free(lcs); + linphone_core_manager_destroy(marie1); + linphone_core_manager_destroy(marie2); + linphone_core_manager_destroy(pauline); +} #endif test_t call_tests[] = { @@ -2196,6 +2277,7 @@ test_t call_tests[] = { { "Call with video added", call_with_video_added }, { "Call with video added (random ports)", call_with_video_added_random_ports }, { "Call with video declined",call_with_declined_video}, + { "Call with multiple early media", multiple_early_media }, #endif { "SRTP ice call", srtp_ice_call }, { "ZRTP ice call", zrtp_ice_call }, diff --git a/tester/rcfiles/marie_early_rc b/tester/rcfiles/marie_early_rc index 844959eae..079a81879 100644 --- a/tester/rcfiles/marie_early_rc +++ b/tester/rcfiles/marie_early_rc @@ -30,8 +30,8 @@ subscribe=0 [rtp] -audio_rtp_port=8070 -video_rtp_port=8072 +audio_rtp_port=18070 +video_rtp_port=19072 [video] display=0