diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h index b75be9156..bd942c9aa 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -109,6 +109,7 @@ struct SalOp{ bool_t manual_refresher; bool_t has_auth_pending; bool_t supports_session_timers; + bool_t op_released; }; diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index dff9f0713..5a55d2900 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -38,6 +38,7 @@ void sal_op_release(SalOp *op){ if (op->refresher) { belle_sip_refresher_stop(op->refresher); } + op->op_released = TRUE; sal_op_unref(op); } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index c0fcc25b0..f5ec79ad8 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -55,7 +55,9 @@ static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog if (op->dialog) { if (belle_sip_dialog_is_server(op->dialog)){ ms_message("Incoming subscribtion from [%s] terminated",sal_op_get_from(op)); - op->base.root->callbacks.subscribe_presence_closed(op, sal_op_get_from(op)); + if (!op->op_released){ + op->base.root->callbacks.subscribe_presence_closed(op, sal_op_get_from(op)); + } } set_or_update_dialog(op, NULL); } @@ -98,7 +100,9 @@ static void presence_response_event(void *op_base, const belle_sip_response_even if (code>=300) { if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){ ms_message("subscription to [%s] rejected",sal_op_get_to(op)); - op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ + if (!op->op_released){ + op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ + } return; } } @@ -156,7 +160,9 @@ static void presence_process_timeout(void *user_ctx, const belle_sip_timeout_eve if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){ ms_message("subscription to [%s] timeout",sal_op_get_to(op)); - op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ + if (!op->op_released){ + op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/ + } } } @@ -176,12 +182,13 @@ static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_req return NULL; if (body==NULL) return NULL; - - op->base.root->callbacks.parse_presence_requested(op, + if (!op->op_released){ + op->base.root->callbacks.parse_presence_requested(op, belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type), body, &result); + } return result; } @@ -210,7 +217,9 @@ static void handle_notify(SalOp *op, belle_sip_request_t *req, belle_sip_dialog_ /* Presence notification body parsed successfully. */ resp = sal_op_create_response_from_request(op, req, 200); /*create first because the op may be destroyed by notify_presence */ - op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL); + if (!op->op_released){ + op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL); + } } else if (body){ /* Formatting error in presence notification body. */ ms_warning("Wrongly formatted presence document."); diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index f473b5956..97f9d9ac2 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -304,6 +304,10 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session } if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) ); + if (stream->rtcp_mux){ + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create ("rtcp-mux",NULL ) ); + } + if (rtp_port != 0) { different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { @@ -781,6 +785,8 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, stream->dir=md->dir; /*takes default value if not present*/ } + stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp_mux") != NULL; + /* Get media payload types */ sdp_parse_payload_types(media_desc, stream); diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index f68c7de9d..a0035d2ed 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -1058,6 +1058,7 @@ static void vfu_request(SalOp *op){ static void dtmf_received(SalOp *op, char dtmf){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + if (!call) return; linphone_core_notify_dtmf_received(lc, call, dtmf); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 19e491e6b..d5283c026 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -659,6 +659,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { CodecConstraints codec_hints={0}; LinphoneCallParams *params = call->params; LinphoneCore *lc = call->core; + bool_t rtcp_mux = lp_config_get_int(lc->config, "rtp", "rtcp_mux", 0); /*multicast is only set in case of outgoing call*/ if (call->dir == LinphoneCallOutgoing && linphone_call_params_audio_multicast_enabled(params)) { @@ -709,6 +710,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { md->streams[call->main_audio_stream_index].proto=get_proto_from_call_params(params); md->streams[call->main_audio_stream_index].dir=get_audio_dir_from_call_params(params); md->streams[call->main_audio_stream_index].type=SalAudio; + md->streams[call->main_audio_stream_index].rtcp_mux = rtcp_mux; if (params->down_ptime) md->streams[call->main_audio_stream_index].ptime=params->down_ptime; else @@ -739,6 +741,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { md->streams[call->main_video_stream_index].proto=md->streams[call->main_audio_stream_index].proto; md->streams[call->main_video_stream_index].dir=get_video_dir_from_call_params(params); md->streams[call->main_video_stream_index].type=SalVideo; + md->streams[call->main_video_stream_index].rtcp_mux = rtcp_mux; strncpy(md->streams[call->main_video_stream_index].name,"Video",sizeof(md->streams[call->main_video_stream_index].name)-1); if (params->has_video){ @@ -771,6 +774,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { md->streams[call->main_text_stream_index].proto=md->streams[call->main_audio_stream_index].proto; md->streams[call->main_text_stream_index].dir=SalStreamSendRecv; md->streams[call->main_text_stream_index].type=SalText; + md->streams[call->main_text_stream_index].rtcp_mux = rtcp_mux; strncpy(md->streams[call->main_text_stream_index].name,"Text",sizeof(md->streams[call->main_text_stream_index].name)-1); if (params->realtimetext_enabled) { strncpy(md->streams[call->main_text_stream_index].rtp_addr,linphone_call_get_public_ip_for_stream(call,call->main_text_stream_index),sizeof(md->streams[call->main_text_stream_index].rtp_addr)); @@ -3002,6 +3006,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, LinphoneCallSta ms_qos_analyzer_algorithm_from_string(linphone_core_get_adaptive_rate_algorithm(lc))); audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc)); rtp_session_set_jitter_compensation(call->audiostream->ms.sessions.rtp_session,linphone_core_get_audio_jittcomp(lc)); + rtp_session_enable_rtcp_mux(call->audiostream->ms.sessions.rtp_session, stream->rtcp_mux); if (!call->params->in_conference && call->params->record_file){ audio_stream_mixed_record_open(call->audiostream,call->params->record_file); call->current_params->record_file=ms_strdup(call->params->record_file); @@ -3149,6 +3154,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, LinphoneCallSta ms_qos_analyzer_algorithm_from_string(linphone_core_get_adaptive_rate_algorithm(lc))); video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc)); rtp_session_set_jitter_compensation(call->videostream->ms.sessions.rtp_session, linphone_core_get_video_jittcomp(lc)); + rtp_session_enable_rtcp_mux(call->videostream->ms.sessions.rtp_session, vstream->rtcp_mux); if (lc->video_conf.preview_vsize.width!=0) video_stream_set_preview_size(call->videostream,lc->video_conf.preview_vsize); video_stream_set_fps(call->videostream,linphone_core_get_preferred_framerate(lc)); @@ -3275,8 +3281,10 @@ static void linphone_call_start_text_stream(LinphoneCall *call) { ms_media_stream_sessions_set_srtp_send_key_b64(&call->textstream->ms.sessions, tstream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key); } } + configure_rtp_session_for_rtcp_fb(call, tstream); configure_rtp_session_for_rtcp_xr(lc, call, SalText); + rtp_session_enable_rtcp_mux(call->textstream->ms.sessions.rtp_session, tstream->rtcp_mux); if (is_multicast) rtp_session_set_multicast_ttl(call->textstream->ms.sessions.rtp_session,tstream->ttl); diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 684e0cb44..2f56f6bbf 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -310,67 +310,66 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, result->type=local_offer->type; if (local_offer->rtp_addr[0]!='\0' && ms_is_multicast(local_offer->rtp_addr)) { - /*6.2 Multicast Streams - ... - If a multicast stream is accepted, the address and port information - in the answer MUST match that of the offer. Similarly, the - directionality information in the answer (sendonly, recvonly, or - sendrecv) MUST equal that of the offer. This is because all - participants in a multicast session need to have equivalent views of - the parameters of the session, an underlying assumption of the - multicast bias of RFC 2327.*/ - if (strcmp(local_offer->rtp_addr,remote_answer->rtp_addr) !=0 ) { - ms_message("Remote answered IP [%s] does not match offered [%s] for local stream description [%p]" - ,remote_answer->rtp_addr - ,local_offer->rtp_addr - ,local_offer); - result->rtp_port=0; - return; - } - if (local_offer->rtp_port!=remote_answer->rtp_port) { - ms_message("Remote answered rtp port [%i] does not match offered [%i] for local stream description [%p]" - ,remote_answer->rtp_port - ,local_offer->rtp_port - ,local_offer); - result->rtp_port=0; - return; - } - if (local_offer->dir!=remote_answer->dir) { - ms_message("Remote answered dir [%s] does not match offered [%s] for local stream description [%p]" - ,sal_stream_dir_to_string(remote_answer->dir) - ,sal_stream_dir_to_string(local_offer->dir) - ,local_offer); - result->rtp_port=0; - return; - } - if (local_offer->bandwidth!=remote_answer->bandwidth) { - ms_message("Remote answered bandwidth [%i] does not match offered [%i] for local stream description [%p]" - ,remote_answer->bandwidth - ,local_offer->bandwidth - ,local_offer); - result->rtp_port=0; - return; - } - if (local_offer->ptime > 0 && local_offer->ptime!=remote_answer->ptime) { - ms_message("Remote answered ptime [%i] does not match offered [%i] for local stream description [%p]" - ,remote_answer->ptime - ,local_offer->ptime - ,local_offer); - result->rtp_port=0; - return; - } - if (local_offer->ttl > 0 && local_offer->ttl!=remote_answer->ttl) { - ms_message("Remote answered ttl [%i] does not match offered [%i] for local stream description [%p]" - ,remote_answer->ttl - ,local_offer->ttl - ,local_offer); - result->rtp_port=0; - return; - } - result->ttl=local_offer->ttl; - result->dir=local_offer->dir; - result->multicast_role = SalMulticastSender; - + /*6.2 Multicast Streams + ... + If a multicast stream is accepted, the address and port information + in the answer MUST match that of the offer. Similarly, the + directionality information in the answer (sendonly, recvonly, or + sendrecv) MUST equal that of the offer. This is because all + participants in a multicast session need to have equivalent views of + the parameters of the session, an underlying assumption of the + multicast bias of RFC 2327.*/ + if (strcmp(local_offer->rtp_addr,remote_answer->rtp_addr) !=0 ) { + ms_message("Remote answered IP [%s] does not match offered [%s] for local stream description [%p]" + ,remote_answer->rtp_addr + ,local_offer->rtp_addr + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->rtp_port!=remote_answer->rtp_port) { + ms_message("Remote answered rtp port [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->rtp_port + ,local_offer->rtp_port + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->dir!=remote_answer->dir) { + ms_message("Remote answered dir [%s] does not match offered [%s] for local stream description [%p]" + ,sal_stream_dir_to_string(remote_answer->dir) + ,sal_stream_dir_to_string(local_offer->dir) + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->bandwidth!=remote_answer->bandwidth) { + ms_message("Remote answered bandwidth [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->bandwidth + ,local_offer->bandwidth + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ptime > 0 && local_offer->ptime!=remote_answer->ptime) { + ms_message("Remote answered ptime [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ptime + ,local_offer->ptime + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ttl > 0 && local_offer->ttl!=remote_answer->ttl) { + ms_message("Remote answered ttl [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ttl + ,local_offer->ttl + ,local_offer); + result->rtp_port=0; + return; + } + result->ttl=local_offer->ttl; + result->dir=local_offer->dir; + result->multicast_role = SalMulticastSender; } else { result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); } @@ -409,8 +408,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, result->dtls_fingerprint[0] = '\0'; result->dtls_role = SalDtlsRoleInvalid; } - - + result->rtcp_mux = remote_answer->rtcp_mux && local_offer->rtcp_mux; } @@ -478,8 +476,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->dtls_fingerprint[0] = '\0'; result->dtls_role = SalDtlsRoleInvalid; } - - + result->rtcp_mux = remote_offer->rtcp_mux && local_cap->rtcp_mux; } diff --git a/coreapi/presence.c b/coreapi/presence.c index b10f1e35f..44d3c931a 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1888,10 +1888,15 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, Sa return ; } if (ss==SalSubscribeTerminated){ - sal_op_release(op); if (lf){ + sal_op_release(lf->outsub); lf->outsub=NULL; lf->subscribe_active=FALSE; + if (lf->outsub != op){ + sal_op_release(op); + } + }else{ + sal_op_release(op); } } } diff --git a/include/sal/sal.h b/include/sal/sal.h index 3da8e52c5..c5c8a1e21 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -245,7 +245,8 @@ typedef struct SalStreamDescription{ char ice_pwd[SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN]; bool_t ice_mismatch; bool_t ice_completed; - bool_t pad[2]; + bool_t rtcp_mux; + bool_t pad[1]; char dtls_fingerprint[256]; SalDtlsRole dtls_role; int ttl; /*for multicast -1 to disable*/ diff --git a/oRTP b/oRTP index 055c29e5b..cb4f9f5ce 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 055c29e5b3be15ada48c7c6b195e6f6ee732c0c0 +Subproject commit cb4f9f5ce7d984b81d6a71416abc77bda2f82bdb diff --git a/tester/call_tester.c b/tester/call_tester.c index 8424bdff2..818fe8ee3 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -5483,6 +5483,64 @@ end: linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } + +static void _call_with_rtcp_mux(bool_t caller_rtcp_mux, bool_t callee_rtcp_mux, bool_t with_ice){ + LinphoneCoreManager * marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + const LinphoneCallParams *params; + MSList *lcs = NULL; + + lcs = ms_list_append(lcs, marie->lc); + lcs = ms_list_append(lcs, pauline->lc); + + if (caller_rtcp_mux){ + lp_config_set_int(linphone_core_get_config(marie->lc), "rtp", "rtcp_mux", 1); + } + if (callee_rtcp_mux){ + lp_config_set_int(linphone_core_get_config(pauline->lc), "rtp", "rtcp_mux", 1); + } + if (with_ice){ + linphone_core_set_firewall_policy(marie->lc, LinphonePolicyUseIce); + linphone_core_set_firewall_policy(pauline->lc, LinphonePolicyUseIce); + } + + if (!BC_ASSERT_TRUE(call(marie,pauline))) goto end; + + params = linphone_call_get_remote_params(linphone_core_get_current_call(pauline->lc)); + BC_ASSERT_TRUE(caller_rtcp_mux == (linphone_call_params_get_custom_sdp_media_attribute(params, LinphoneStreamTypeAudio, "rtcp-mux") != NULL)); + if (caller_rtcp_mux){ + params = linphone_call_get_remote_params(linphone_core_get_current_call(marie->lc)); + BC_ASSERT_TRUE(callee_rtcp_mux == (linphone_call_params_get_custom_sdp_media_attribute(params, LinphoneStreamTypeAudio, "rtcp-mux") != NULL)); + } + + if (with_ice){ + check_ice(marie, pauline, LinphoneIceStateHostConnection); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); + } + liblinphone_tester_check_rtcp(marie,pauline); + check_media_direction(pauline, linphone_core_get_current_call(pauline->lc), lcs, LinphoneMediaDirectionSendRecv, LinphoneMediaDirectionInvalid); + end_call(marie,pauline); + +end: + ms_list_free(lcs); + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(marie); +} + +static void call_with_rtcp_mux(void){ + _call_with_rtcp_mux(TRUE, TRUE, FALSE); +} + +static void call_with_rtcp_mux_not_accepted(void){ + _call_with_rtcp_mux(TRUE, FALSE, FALSE); +} + +static void call_with_ice_and_rtcp_mux(void){ + _call_with_rtcp_mux(TRUE, TRUE, TRUE); +} + + test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, @@ -5641,7 +5699,10 @@ test_t call_tests[] = { { "Call with network switch in early state 1", call_with_network_switch_in_early_state_1 }, { "Call with network switch in early state 2", call_with_network_switch_in_early_state_2 }, { "Call with network switch and ICE", call_with_network_switch_and_ice }, - { "Call with network switch with socket refresh", call_with_network_switch_and_socket_refresh } + { "Call with network switch with socket refresh", call_with_network_switch_and_socket_refresh }, + { "Call with rtcp-mux", call_with_rtcp_mux}, + { "Call with rtcp-mux not accepted", call_with_rtcp_mux_not_accepted}, + { "Call with ICE and rtcp-mux", call_with_ice_and_rtcp_mux} }; test_suite_t call_test_suite = {"Single Call", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,