diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 31f5b9c07..b3e4ea740 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -819,6 +819,7 @@ static void linphone_call_set_terminated(LinphoneCall *call){ void linphone_call_fix_call_parameters(LinphoneCall *call){ call->params.has_video=call->current_params.has_video; + if (call->params.media_encryption != LinphoneMediaEncryptionZRTP) /*in case of ZRTP call parameter are handle after zrtp negociation*/ call->params.media_encryption=call->current_params.media_encryption; } @@ -906,17 +907,10 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const call->media_start_time=time(NULL); } - if (cstate == LinphoneCallStreamsRunning) { - linphone_reporting_update_ip(call); - } - if (lc->vtable.call_state_changed) lc->vtable.call_state_changed(lc,call,cstate,message); - if (cstate==LinphoneCallEnd){ - if (call->log->status == LinphoneCallSuccess) - linphone_reporting_publish_session_report(call); - } + linphone_reporting_call_state_updated(call); if (cstate==LinphoneCallReleased){ if (call->op!=NULL) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index afa7a7265..e9c5585e3 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -129,8 +129,8 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *fro cl->status=LinphoneCallAborted; /*default status*/ cl->quality=-1; - cl->reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new(); - cl->reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new(); + cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new(); + cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new(); return cl; } @@ -394,8 +394,8 @@ void linphone_call_log_destroy(LinphoneCallLog *cl){ if (cl->to!=NULL) linphone_address_destroy(cl->to); if (cl->refkey!=NULL) ms_free(cl->refkey); if (cl->call_id) ms_free(cl->call_id); - if (cl->reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reports[LINPHONE_CALL_STATS_AUDIO]); - if (cl->reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reports[LINPHONE_CALL_STATS_VIDEO]); + if (cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]); + if (cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]); ms_free(cl); } @@ -3257,7 +3257,6 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call) linphone_core_update_streams (lc,call,md); linphone_call_fix_call_parameters(call); } - if (call->state != LinphoneCallOutgoingEarlyMedia) /*don't change the state in case of outgoing early (SIP UPDATE)*/ linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); return 0; diff --git a/coreapi/private.h b/coreapi/private.h index 2b6f8308d..aec83fa0e 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -103,6 +103,11 @@ struct _LinphoneCallParams{ uint8_t avpf_rr_interval; }; +struct _LinphoneQualityReporting{ + reporting_session_report_t * reports[2]; /**Store information on audio and video media streams (RFC 6035) */ + bool_t was_video_running; /*Keep video state since last check in order to detect its (de)activation*/ +}; + struct _LinphoneCallLog{ struct _LinphoneCore *lc; LinphoneCallDir dir; /**< The direction of the call*/ @@ -119,10 +124,12 @@ struct _LinphoneCallLog{ time_t start_date_time; /**Start date of the call in seconds as expressed in a time_t */ char* call_id; /**unique id of a call*/ - reporting_session_report_t * reports[2]; /**log->reports[stats_type] != NULL); + return (call->log->reporting.reports[stats_type] != NULL); } static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * offset, const reporting_content_metrics_t rm) { @@ -308,28 +308,30 @@ static void append_metrics_to_buffer(char ** buffer, size_t * size, size_t * off ms_free(moscq_str); } -static void send_report(const LinphoneCall* call, reporting_session_report_t * report, const char * report_event) { +static int send_report(const LinphoneCall* call, reporting_session_report_t * report, const char * report_event) { LinphoneContent content = {0}; LinphoneAddress *addr; int expires = -1; size_t offset = 0; size_t size = 2048; char * buffer; + int ret = 0; /*if the call was hung up too early, we might have invalid IPs information in that case, we abort the report since it's not useful data*/ if (report->info.local_addr.ip == NULL || strlen(report->info.local_addr.ip) == 0 || report->info.remote_addr.ip == NULL || strlen(report->info.remote_addr.ip) == 0) { - ms_warning("The call was hang up too early (duration: %d sec) and IP could " + ms_warning("QualityReporting: Trying to submit a %s too early (call duration: %d sec) and IP could " "not be retrieved so dropping this report" + , report_event , linphone_call_get_duration(call)); - return; + return 1; } addr = linphone_address_new(linphone_proxy_config_get_quality_reporting_collector(call->dest_proxy)); if (addr == NULL) { - ms_warning("Asked to submit reporting statistics but no collector address found"); - return; + ms_warning("QualityReporting: Asked to submit reporting statistics but no collector address found"); + return 2; } buffer = (char *) ms_malloc(size); @@ -362,11 +364,14 @@ static void send_report(const LinphoneCall* call, reporting_session_report_t * r content.size = strlen(buffer); /*(WIP) Memory leak: PUBLISH message is never freed (issue 1283)*/ - linphone_core_publish(call->core, addr, "vq-rtcpxr", expires, &content); + if (! linphone_core_publish(call->core, addr, "vq-rtcpxr", expires, &content)){ + ret=3; + } linphone_address_destroy(addr); reset_avg_metrics(report); linphone_content_uninit(&content); + return ret; } static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDescription * smd, SalStreamType sal_stream_type) { @@ -379,7 +384,7 @@ static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDesc } } - ms_warning("Could not find the associated stream of type %d", sal_stream_type); + ms_warning("QualityReporting: Could not find the associated stream of type %d", sal_stream_type); return NULL; } @@ -391,19 +396,19 @@ static void update_ip(LinphoneCall * call, int stats_type) { /*local info are always up-to-date and correct*/ if (local_desc != NULL) { - call->log->reports[stats_type]->info.local_addr.port = local_desc->rtp_port; - STR_REASSIGN(call->log->reports[stats_type]->info.local_addr.ip, ms_strdup(local_desc->rtp_addr)); + call->log->reporting.reports[stats_type]->info.local_addr.port = local_desc->rtp_port; + STR_REASSIGN(call->log->reporting.reports[stats_type]->info.local_addr.ip, ms_strdup(local_desc->rtp_addr)); } if (remote_desc != NULL) { /*port is always stored in stream description struct*/ - call->log->reports[stats_type]->info.remote_addr.port = remote_desc->rtp_port; + call->log->reporting.reports[stats_type]->info.remote_addr.port = remote_desc->rtp_port; /*for IP it can be not set if we are using a direct route*/ if (remote_desc->rtp_addr != NULL && strlen(remote_desc->rtp_addr) > 0) { - STR_REASSIGN(call->log->reports[stats_type]->info.remote_addr.ip, ms_strdup(remote_desc->rtp_addr)); + STR_REASSIGN(call->log->reporting.reports[stats_type]->info.remote_addr.ip, ms_strdup(remote_desc->rtp_addr)); } else { - STR_REASSIGN(call->log->reports[stats_type]->info.remote_addr.ip, ms_strdup(sal_call_get_remote_media_description(call->op)->addr)); + STR_REASSIGN(call->log->reporting.reports[stats_type]->info.remote_addr.ip, ms_strdup(sal_call_get_remote_media_description(call->op)->addr)); } } } @@ -432,11 +437,12 @@ void linphone_reporting_update_media_info(LinphoneCall * call, int stats_type) { const PayloadType * local_payload = NULL; const PayloadType * remote_payload = NULL; const LinphoneCallParams * current_params = linphone_call_get_current_params(call); - reporting_session_report_t * report = call->log->reports[stats_type]; + reporting_session_report_t * report = call->log->reporting.reports[stats_type]; if (!media_report_enabled(call, stats_type)) return; + STR_REASSIGN(report->info.call_id, ms_strdup(call->log->call_id)); STR_REASSIGN(report->info.local_group, ms_strdup_printf("linphone-%s-%s-%s", (stats_type == LINPHONE_CALL_STATS_AUDIO ? "audio" : "video"), linphone_core_get_user_agent_name(), report->info.call_id)); @@ -496,7 +502,7 @@ void linphone_reporting_update_media_info(LinphoneCall * call, int stats_type) { } void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type) { - reporting_session_report_t * report = call->log->reports[stats_type]; + reporting_session_report_t * report = call->log->reporting.reports[stats_type]; reporting_content_metrics_t * metrics = NULL; MSQosAnalyzer *analyzer=NULL; LinphoneCallStats stats = call->stats[stats_type]; @@ -554,21 +560,31 @@ void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type) { } } -static void publish_report(LinphoneCall *call, const char *event_type){ +static int publish_report(LinphoneCall *call, const char *event_type){ + int ret = 0; int i; for (i = 0; i < 2; i++){ if (media_report_enabled(call, i)){ + int sndret; linphone_reporting_update_media_info(call, i); - send_report(call, call->log->reports[i], event_type); + sndret=send_report(call, call->log->reporting.reports[i], event_type); + if (sndret>0){ + ret += 10+(i+1)*sndret; + } + } else{ + ret += i+1; } } -} -void linphone_reporting_publish_session_report(LinphoneCall* call) { - publish_report(call, "VQSessionReport: CallTerm"); + return ret; } -void linphone_reporting_publish_interval_report(LinphoneCall* call) { - publish_report(call, "VQIntervalReport"); +int linphone_reporting_publish_session_report(LinphoneCall* call, bool_t call_term) { + char * session_type = call_term?"VQSessionReport: CallTerm":"VQSessionReport"; + return publish_report(call, session_type); +} + +int linphone_reporting_publish_interval_report(LinphoneCall* call) { + return publish_report(call, "VQIntervalReport"); } reporting_session_report_t * linphone_reporting_new() { @@ -628,3 +644,35 @@ void linphone_reporting_destroy(reporting_session_report_t * report) { ms_free(report); } + +void linphone_reporting_call_state_updated(LinphoneCall *call){ + LinphoneCallState state=linphone_call_get_state(call); + bool_t enabled=media_report_enabled(call, LINPHONE_CALL_STATS_VIDEO); + switch (state){ + case LinphoneCallStreamsRunning: + if (enabled!=call->log->reporting.was_video_running){ + if (enabled){ + linphone_reporting_update_ip(call); + }else{ + ms_message("Send midterm report with status %d", + send_report(call, call->log->reporting.reports[LINPHONE_CALL_STATS_VIDEO], "VQSessionReport") + ); + } + }else{ + linphone_reporting_update_ip(call); + } + + call->log->reporting.was_video_running=enabled; + break; + case LinphoneCallEnd: + if (call->log->status==LinphoneCallSuccess || call->log->status==LinphoneCallAborted){ + ms_message("Send report with status %d", + linphone_reporting_publish_session_report(call, TRUE) + ); + } + break; + default: + + break; + } +} diff --git a/coreapi/quality_reporting.h b/coreapi/quality_reporting.h index c909baecc..29fb3c4d0 100644 --- a/coreapi/quality_reporting.h +++ b/coreapi/quality_reporting.h @@ -164,25 +164,35 @@ void linphone_reporting_update_ip(LinphoneCall * call); * Publish a session report. This function should be called when session terminates, * media change (codec change or session fork), session terminates due to no media packets being received. * @param call #LinphoneCall object to consider + * @param call_term whether the call has ended or is continuing * + * @return error code. 0 for success, positive value otherwise. */ -void linphone_reporting_publish_session_report(LinphoneCall* call); +int linphone_reporting_publish_session_report(LinphoneCall* call, bool_t call_term); /** * Publish an interval report. This function should be used for periodic interval * @param call #LinphoneCall object to consider + * @return error code. 0 for success, positive value otherwise. * */ -void linphone_reporting_publish_interval_report(LinphoneCall* call); +int linphone_reporting_publish_interval_report(LinphoneCall* call); /** - * Update publish report data with fresh RTCP stats, if needed. + * Update publish reports with newly received RTCP-XR packets (if available). * @param call #LinphoneCall object to consider * @param stats_type the media type (LINPHONE_CALL_STATS_AUDIO or LINPHONE_CALL_STATS_VIDEO) * */ void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type); +/** + * Update publish reports on call state change. + * @param call #LinphoneCall object to consider + * + */ +void linphone_reporting_call_state_updated(LinphoneCall *call); + #ifdef __cplusplus } #endif diff --git a/tester/call_tester.c b/tester/call_tester.c index bab8e8666..b8a66f82e 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -2199,11 +2199,11 @@ static void quality_reporting_not_used_without_config() { linphone_proxy_config_get_quality_reporting_collector(call_marie->dest_proxy)), 0); // this field should be already filled - CU_ASSERT_PTR_NOT_NULL(call_marie->log->reports[0]->info.local_addr.ip); - CU_ASSERT_PTR_NULL(call_pauline->log->reports[0]->info.local_addr.ip); + CU_ASSERT_PTR_NOT_NULL(call_marie->log->reporting.reports[0]->info.local_addr.ip); + CU_ASSERT_PTR_NULL(call_pauline->log->reporting.reports[0]->info.local_addr.ip); // but not this one since it is updated at the end of call - CU_ASSERT_PTR_NULL(call_marie->log->reports[0]->dialog_id); + CU_ASSERT_PTR_NULL(call_marie->log->reporting.reports[0]->dialog_id); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); @@ -2247,7 +2247,7 @@ static void quality_reporting_at_call_termination() { linphone_core_terminate_all_calls(marie->lc); // now dialog id should be filled - CU_ASSERT_PTR_NOT_NULL(call_marie->log->reports[0]->dialog_id); + CU_ASSERT_PTR_NOT_NULL(call_marie->log->reporting.reports[0]->dialog_id); CU_ASSERT_TRUE(wait_for_until(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallReleased,1, 10000)); CU_ASSERT_TRUE(wait_for_until(pauline->lc,NULL,&pauline->stat.number_of_LinphoneCallReleased,1, 10000));