From ce7a644616b694f9a612bb04e85b0e1537b01ed6 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 5 Jun 2014 11:57:51 +0200 Subject: [PATCH] Quality reporting: add possibility to send interval reports to a given spacing interval during a call --- coreapi/linphonecall.c | 2 +- coreapi/linphonecore.h | 52 ++++++++++++++++++++++++++++++++----- coreapi/private.h | 1 + coreapi/proxy.c | 23 +++++++++++----- coreapi/quality_reporting.c | 43 +++++++++++++++++++++++++----- coreapi/quality_reporting.h | 15 +++++++++-- 6 files changed, 114 insertions(+), 22 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index a1172229c..2c6c188b4 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -907,7 +907,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const if (cstate==LinphoneCallEnd){ if (call->log->status == LinphoneCallSuccess) - linphone_reporting_publish_on_call_term(call); + linphone_reporting_publish_session_report(call); } if (cstate==LinphoneCallReleased){ diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 372caeb70..c3ebf9aaf 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -834,16 +834,54 @@ LINPHONE_PUBLIC int linphone_proxy_config_get_publish_expires(const LinphoneProx LINPHONE_PUBLIC void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val); LINPHONE_PUBLIC void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix); -/** - * Indicates either or not, quality statistics during call should be stored and sent to a collector at termination. - * @param cfg #LinphoneProxyConfig object - * @param val if true, quality statistics publish will be stored and sent to the collector - * + /** + * Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035. + * @param[in] cfg #LinphoneProxyConfig object + * @param[in] enable True to sotre quality statistics and sent them to the collector, false to disable it. + */ +LINPHONE_PUBLIC void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t enable); + +/** + * Indicates whether quality statistics during call should be stored and sent to a collector according to RFC 6035. + * @param[in] cfg #LinphoneProxyConfig object + * @return True if quality repotring is enabled, false otherwise. */ -LINPHONE_PUBLIC void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t val); LINPHONE_PUBLIC bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg); + + /** + * Set the SIP address of the collector end-point when using quality reporting. This SIP address + * should be used on server-side to process packets directly then discard packets. Collector address + * should be a non existing account and should not received any packets. + * @param[in] cfg #LinphoneProxyConfig object + * @param[in] collector SIP address of the collector end-point. + */ LINPHONE_PUBLIC void linphone_proxy_config_set_quality_reporting_collector(LinphoneProxyConfig *cfg, const char *collector); -LINPHONE_PUBLIC const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *obj); + + /** + * Get the SIP address of the collector end-point when using quality reporting. This SIP address + * should be used on server-side to process packets directly then discard packets. Collector address + * should be a non existing account and should not received any packets. + * @param[in] cfg #LinphoneProxyConfig object + * @return The SIP address of the collector end-point. + */ +LINPHONE_PUBLIC const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *cfg); + +/** + * Set the interval between 2 interval reports sending when using quality reporting. If call exceed interval size, an + * interval report will be sent to the collector. On call termination, a session report will be sent + * for the remaining period. + * @param[in] cfg #LinphoneProxyConfig object + * @param[in] interval The interval in seconds. + */ +void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, uint8_t interval); + +/** + * Get the interval between interval reports when using quality reporting. + * @param[in] cfg #LinphoneProxyConfig object + * @return The interval in seconds. + */ + +int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg); /** * Get the registration state of the given proxy config. diff --git a/coreapi/private.h b/coreapi/private.h index ed9e78878..5270f5440 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -437,6 +437,7 @@ struct _LinphoneProxyConfig bool_t avpf_enabled; bool_t pad; uint8_t avpf_rr_interval; + uint8_t quality_reporting_interval; void* user_data; time_t deletion_date; LinphonePrivacyMask privacy; diff --git a/coreapi/proxy.c b/coreapi/proxy.c index a72e3523a..cedb97b29 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -108,8 +108,9 @@ static void linphone_proxy_config_init(LinphoneCore* lc, LinphoneProxyConfig *ob obj->reg_identity = identity ? ms_strdup(identity) : NULL; obj->reg_proxy = proxy ? ms_strdup(proxy) : NULL; obj->reg_route = route ? ms_strdup(route) : NULL; - obj->quality_reporting_collector = quality_reporting_collector ? ms_strdup(quality_reporting_collector) : NULL; obj->quality_reporting_enabled = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_enabled", 0) : 0; + obj->quality_reporting_collector = quality_reporting_collector ? ms_strdup(quality_reporting_collector) : NULL; + obj->quality_reporting_interval = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_interval", 0) : 0; obj->contact_params = contact_params ? ms_strdup(contact_params) : NULL; obj->contact_uri_params = contact_uri_params ? ms_strdup(contact_uri_params) : NULL; obj->avpf_enabled = lc ? lp_config_get_default_int(lc->config, "proxy", "avpf", 0) : 0; @@ -489,6 +490,14 @@ bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg) return cfg->quality_reporting_enabled && cfg->quality_reporting_collector != NULL; } +void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, uint8_t interval) { + cfg->quality_reporting_interval = interval; +} + +int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg) { + return cfg->quality_reporting_interval; +} + void linphone_proxy_config_set_quality_reporting_collector(LinphoneProxyConfig *cfg, const char *collector){ if (collector!=NULL && strlen(collector)>0){ LinphoneAddress *addr=linphone_address_new(collector); @@ -1174,9 +1183,6 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC if (obj->reg_route!=NULL){ lp_config_set_string(config,key,"reg_route",obj->reg_route); } - if (obj->quality_reporting_collector!=NULL){ - lp_config_set_string(config,key,"quality_reporting_collector",obj->quality_reporting_collector); - } if (obj->reg_identity!=NULL){ lp_config_set_string(config,key,"reg_identity",obj->reg_identity); } @@ -1186,13 +1192,17 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC if (obj->contact_uri_params!=NULL){ lp_config_set_string(config,key,"contact_uri_parameters",obj->contact_uri_params); } + if (obj->quality_reporting_collector!=NULL){ + lp_config_set_string(config,key,"quality_reporting_collector",obj->quality_reporting_collector); + } + lp_config_set_int(config,key,"quality_reporting_enabled",obj->quality_reporting_enabled); + lp_config_set_int(config,key,"quality_reporting_interval",obj->quality_reporting_interval); lp_config_set_int(config,key,"reg_expires",obj->expires); lp_config_set_int(config,key,"reg_sendregister",obj->reg_sendregister); lp_config_set_int(config,key,"publish",obj->publish); lp_config_set_int(config, key, "avpf", obj->avpf_enabled); lp_config_set_int(config, key, "avpf_rr_interval", obj->avpf_rr_interval); lp_config_set_int(config,key,"dial_escape_plus",obj->dial_escape_plus); - lp_config_set_int(config,key,"quality_reporting_enabled",obj->quality_reporting_enabled); lp_config_set_string(config,key,"dial_prefix",obj->dial_prefix); lp_config_set_int(config,key,"privacy",obj->privacy); } @@ -1224,9 +1234,10 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config tmp=lp_config_get_string(config,key,"reg_route",NULL); if (tmp!=NULL) linphone_proxy_config_set_route(cfg,tmp); + linphone_proxy_config_enable_quality_reporting(cfg,lp_config_get_int(config,key,"quality_reporting_enabled",0)); tmp=lp_config_get_string(config,key,"quality_reporting_collector",NULL); if (tmp!=NULL) linphone_proxy_config_set_quality_reporting_collector(cfg,tmp); - linphone_proxy_config_enable_quality_reporting(cfg,lp_config_get_int(config,key,"quality_reporting_enabled",0)); + linphone_proxy_config_set_quality_reporting_interval(cfg, lp_config_get_int(config, key, "quality_reporting_interval", 5)); linphone_proxy_config_set_contact_parameters(cfg,lp_config_get_string(config,key,"contact_parameters",NULL)); diff --git a/coreapi/quality_reporting.c b/coreapi/quality_reporting.c index c9c5e7fd7..4d8a68b23 100644 --- a/coreapi/quality_reporting.c +++ b/coreapi/quality_reporting.c @@ -33,8 +33,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *************************************************************************** For codecs that are able to change sample rates, the lowest and highest sample rates MUST be reported (e.g., 8000;16000). moslq == moscq +valgrind video: what happens if doing stop/resume? rlq value: need algo to compute it +3.4 overload avoidance? - The Session report when session terminates, media change (codec change or a session fork), session terminates due to no media packets being received - The Interval report SHOULD be used for periodic or interval reporting @@ -282,7 +284,7 @@ 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) { +static void send_report(const LinphoneCall* call, reporting_session_report_t * report, const char * report_event) { LinphoneContent content = {0}; LinphoneAddress *addr; int expires = -1; @@ -295,7 +297,15 @@ static void send_report(const LinphoneCall* call, reporting_session_report_t * r 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 " - "not be retrieved so dropping this report", linphone_call_get_duration(call)); + "not be retrieved so dropping this report" + , linphone_call_get_duration(call)); + return; + } + + /*do not send report if the previous one was sent less than 30seconds ago*/ + if (ms_time(NULL) - report->last_report_date < 30){ + ms_warning("Already sent a report %ld sec ago. Cancel sending this report" + , ms_time(NULL) - report->last_report_date); return; } @@ -309,7 +319,7 @@ static void send_report(const LinphoneCall* call, reporting_session_report_t * r content.type = ms_strdup("application"); content.subtype = ms_strdup("vq-rtcpxr"); - append_to_buffer(&buffer, &size, &offset, "VQSessionReport: CallTerm\r\n"); + append_to_buffer(&buffer, &size, &offset, "%s\r\n", report_event); append_to_buffer(&buffer, &size, &offset, "CallID: %s\r\n", report->info.call_id); append_to_buffer(&buffer, &size, &offset, "LocalID: %s\r\n", report->info.local_id); append_to_buffer(&buffer, &size, &offset, "RemoteID: %s\r\n", report->info.remote_id); @@ -339,6 +349,8 @@ static void send_report(const LinphoneCall* call, reporting_session_report_t * r reset_avg_metrics(report); linphone_content_uninit(&content); + + report->last_report_date = ms_time(NULL); } static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDescription * smd, SalStreamType sal_stream_type) { @@ -533,20 +545,39 @@ void linphone_reporting_on_rtcp_received(LinphoneCall *call, int stats_type) { } } } + + /* check if we should send an interval report */ + if (ms_time(NULL) - report->last_report_date > linphone_proxy_config_get_quality_reporting_interval(call->dest_proxy)){ + linphone_reporting_publish_interval_report(call); + } } -void linphone_reporting_publish_on_call_term(LinphoneCall* call) { +void linphone_reporting_publish_session_report(LinphoneCall* call) { + if (! is_reporting_enabled(call)) + return; + + if (call->log->reports[LINPHONE_CALL_STATS_AUDIO] != NULL) { + send_report(call, call->log->reports[LINPHONE_CALL_STATS_AUDIO], "VQSessionReport: CallTerm"); + } + + if (call->log->reports[LINPHONE_CALL_STATS_VIDEO] != NULL + && linphone_call_params_video_enabled(linphone_call_get_current_params(call))) { + send_report(call, call->log->reports[LINPHONE_CALL_STATS_VIDEO], "VQSessionReport: CallTerm"); + } +} + +void linphone_reporting_publish_interval_report(LinphoneCall* call) { if (! is_reporting_enabled(call)) return; if (call->log->reports[LINPHONE_CALL_STATS_AUDIO] != NULL) { - send_report(call, call->log->reports[LINPHONE_CALL_STATS_AUDIO]); + send_report(call, call->log->reports[LINPHONE_CALL_STATS_AUDIO], "VQIntervalReport"); } if (call->log->reports[LINPHONE_CALL_STATS_VIDEO] != NULL && linphone_call_params_video_enabled(linphone_call_get_current_params(call))) { - send_report(call, call->log->reports[LINPHONE_CALL_STATS_VIDEO]); + send_report(call, call->log->reports[LINPHONE_CALL_STATS_VIDEO], "VQIntervalReport"); } } diff --git a/coreapi/quality_reporting.h b/coreapi/quality_reporting.h index 44704c32a..00fb07215 100644 --- a/coreapi/quality_reporting.h +++ b/coreapi/quality_reporting.h @@ -129,6 +129,9 @@ typedef struct reporting_session_report { reporting_content_metrics_t remote_metrics; // optional char * dialog_id; // optional + + // for internal processing + time_t last_report_date; } reporting_session_report_t; reporting_session_report_t * linphone_reporting_new(); @@ -154,11 +157,19 @@ void linphone_reporting_update_media_info(LinphoneCall * call, int stats_type); void linphone_reporting_update_ip(LinphoneCall * call); /** - * Publish the report on the call end. + * 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 * */ -void linphone_reporting_publish_on_call_term(LinphoneCall* call); +void linphone_reporting_publish_session_report(LinphoneCall* call); + +/** + * Publish an interval report. This function should be used for periodic interval + * @param call #LinphoneCall object to consider + * + */ +void linphone_reporting_publish_interval_report(LinphoneCall* call); /** * Update publish report data with fresh RTCP stats, if needed.