diff --git a/coreapi/private.h b/coreapi/private.h index aec83fa0e..b59ccd906 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -106,6 +106,7 @@ struct _LinphoneCallParams{ 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*/ + LinphoneQualityReportingReportSendCb on_report_sent; }; struct _LinphoneCallLog{ @@ -860,7 +861,6 @@ LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatu const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref); void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc); - /***************************************************************************** * REMOTE PROVISIONING FUNCTIONS * ****************************************************************************/ diff --git a/coreapi/quality_reporting.c b/coreapi/quality_reporting.c index 78e075809..668e2785a 100644 --- a/coreapi/quality_reporting.c +++ b/coreapi/quality_reporting.c @@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /*************************************************************************** * TODO / REMINDER LIST *************************************************************************** - - move qos data at report's end? <-- unit test recup publish body + bug ms_debug *************************************************************************** * END OF TODO / REMINDER LIST ****************************************************************************/ @@ -122,19 +122,15 @@ static uint8_t are_metrics_filled(const reporting_content_metrics_t rm) { IF_NUM_IN_RANGE(rm.packet_loss.network_packet_loss_rate, 0, 255, ret|=METRICS_PACKET_LOSS); IF_NUM_IN_RANGE(rm.packet_loss.jitter_buffer_discard_rate, 0, 255, ret|=METRICS_PACKET_LOSS); - /*since these are same values than local ones, do not check them*/ - /*if (rm.session_description.payload_type != -1) ret|=METRICS_SESSION_DESCRIPTION;*/ - /*if (rm.session_description.payload_desc != NULL) ret|=METRICS_SESSION_DESCRIPTION;*/ - /*if (rm.session_description.sample_rate != -1) ret|=METRICS_SESSION_DESCRIPTION;*/ - /*if (rm.session_description.fmtp != NULL) ret|=METRICS_SESSION_DESCRIPTION;*/ - if (rm.session_description.frame_duration != -1) ret|=METRICS_SESSION_DESCRIPTION; - if (rm.session_description.packet_loss_concealment != -1) ret|=METRICS_SESSION_DESCRIPTION; + if (rm.session_description.payload_type != -1) ret|=METRICS_SESSION_DESCRIPTION; + if (rm.session_description.payload_desc != NULL) ret|=METRICS_SESSION_DESCRIPTION; + if (rm.session_description.sample_rate != -1) ret|=METRICS_SESSION_DESCRIPTION; + if (rm.session_description.fmtp != NULL) ret|=METRICS_SESSION_DESCRIPTION; IF_NUM_IN_RANGE(rm.jitter_buffer.adaptive, 0, 3, ret|=METRICS_JITTER_BUFFER); IF_NUM_IN_RANGE(rm.jitter_buffer.abs_max, 0, 65535, ret|=METRICS_JITTER_BUFFER); IF_NUM_IN_RANGE(rm.delay.end_system_delay, 0, 65535, ret|=METRICS_DELAY); - /*IF_NUM_IN_RANGE(rm.delay.symm_one_way_delay, 0, 65535, ret|=METRICS_DELAY);*/ IF_NUM_IN_RANGE(rm.delay.interarrival_jitter, 0, 65535, ret|=METRICS_DELAY); IF_NUM_IN_RANGE(rm.delay.mean_abs_jitter, 0, 65535, ret|=METRICS_DELAY); @@ -337,6 +333,13 @@ static int send_report(LinphoneCall* call, reporting_session_report_t * report, content.data = buffer; content.size = strlen(buffer); + if (call->log->reporting.on_report_sent != NULL){ + call->log->reporting.on_report_sent( + call, + (report==call->log->reporting.reports[0])?LINPHONE_CALL_STATS_AUDIO:LINPHONE_CALL_STATS_VIDEO, + &content); + } + if (! linphone_core_publish(call->core, addr, "vq-rtcpxr", expires, &content)){ ret=4; } else { @@ -485,9 +488,9 @@ void linphone_reporting_update_media_info(LinphoneCall * call, int stats_type) { if (local_payload != NULL) { report->local_metrics.session_description.payload_type = local_payload->type; - STR_REASSIGN(report->local_metrics.session_description.payload_desc, ms_strdup(local_payload->mime_type)); + if (local_payload->mime_type!=NULL) STR_REASSIGN(report->local_metrics.session_description.payload_desc, ms_strdup(local_payload->mime_type)); report->local_metrics.session_description.sample_rate = local_payload->clock_rate; - STR_REASSIGN(report->local_metrics.session_description.fmtp, ms_strdup(local_payload->recv_fmtp)); + if (local_payload->recv_fmtp!=NULL) STR_REASSIGN(report->local_metrics.session_description.fmtp, ms_strdup(local_payload->recv_fmtp)); } if (remote_payload != NULL) { @@ -694,3 +697,6 @@ void linphone_reporting_destroy(reporting_session_report_t * report) { } +void linphone_reporting_set_on_report_send(LinphoneCall *call, LinphoneQualityReportingReportSendCb cb){ + call->log->reporting.on_report_sent = cb; +} diff --git a/coreapi/quality_reporting.h b/coreapi/quality_reporting.h index d3ec1013e..bffc39c69 100644 --- a/coreapi/quality_reporting.h +++ b/coreapi/quality_reporting.h @@ -139,6 +139,9 @@ typedef struct reporting_session_report { time_t last_report_date; } reporting_session_report_t; + +typedef void (*LinphoneQualityReportingReportSendCb)(const LinphoneCall *call, int stream_type, const LinphoneContent *content); + reporting_session_report_t * linphone_reporting_new(); void linphone_reporting_destroy(reporting_session_report_t * report); @@ -194,6 +197,16 @@ void linphone_reporting_on_rtcp_update(LinphoneCall *call, int stats_type); */ void linphone_reporting_call_state_updated(LinphoneCall *call); +/** + * Setter of the #LinphoneQualityReportingReportSendCb callback method which is + * notified each time a report will be submitted to the collector, if quality + * reporting is enabled + * @param call #LinphoneCall object to consider + * @param cb #LinphoneQualityReportingReportSendCb callback function to notify + * + */ +void linphone_reporting_set_on_report_send(LinphoneCall *call, LinphoneQualityReportingReportSendCb cb); + #ifdef __cplusplus } #endif diff --git a/tester/quality_reporting_tester.c b/tester/quality_reporting_tester.c index 1c174f810..59e05fc9e 100644 --- a/tester/quality_reporting_tester.c +++ b/tester/quality_reporting_tester.c @@ -22,6 +22,93 @@ #include "private.h" #include "liblinphone_tester.h" +/*avoid crash if x is NULL on libc versions <4.5.26 */ +#define __strstr(x, y) ((x==NULL)?NULL:strstr(x,y)) + +void on_report_send_mandatory(const LinphoneCall *call, int stream_type, const LinphoneContent *content){ + const MediaStream * ms = ((stream_type == LINPHONE_CALL_STATS_AUDIO)?&call->audiostream->ms:&call->videostream->ms); + char * body = (char *)content->data; + char * remote_metrics_start = __strstr(body, "RemoteMetrics:"); + reporting_session_report_t * report = call->log->reporting.reports[stream_type]; + + CU_ASSERT_TRUE( + __strstr(body, "VQIntervalReport\r\n") == body || + __strstr(body, "VQSessionReport\r\n") == body || + __strstr(body, "VQSessionReport: CallTerm\r\n") == body + ); + + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "CallID:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "LocalID:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "RemoteID:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "OrigID:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "LocalGroup:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "RemoteGroup:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "LocalAddr:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "IP=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "PORT=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "SSRC=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "RemoteAddr:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "IP=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "PORT=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "SSRC=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "LocalMetrics:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "Timestamps:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "START=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "STOP=")); + + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "SessionDesc:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "PT=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "PD=")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "SR=")); + + /* We should have not reached RemoteMetrics section yet */ + CU_ASSERT_TRUE(!remote_metrics_start || body < remote_metrics_start); + + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "DialogID:")); + + if (report->remote_metrics.rtcp_sr_count&&ms->rc!=NULL){ + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "AdaptiveAlg:")); + } +} + +char * on_report_send_verify_metrics(const reporting_content_metrics_t *metrics, char * body){ + if (metrics->rtcp_xr_count){ + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "SessionDesc:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "JitterBuffer:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "PacketLoss:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "QualityEst:")); + } + if (metrics->rtcp_sr_count){ + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "Delay:")); + } + + return body; +} + +void on_report_send_with_rtcp_xr_local(const LinphoneCall *call, int stream_type, const LinphoneContent *content){ + char * body = (char*)content->data; + char * remote_metrics_start = __strstr(body, "RemoteMetrics:"); + reporting_session_report_t * report = call->log->reporting.reports[stream_type]; + on_report_send_mandatory(call,stream_type,content); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "LocalMetrics:")); + CU_ASSERT_TRUE(!remote_metrics_start || on_report_send_verify_metrics(&report->local_metrics,body) < remote_metrics_start); +} +void on_report_send_with_rtcp_xr_remote(const LinphoneCall *call, int stream_type, const LinphoneContent *content){ + char * body = (char*)content->data; + reporting_session_report_t * report = call->log->reporting.reports[stream_type]; + + on_report_send_mandatory(call,stream_type,content); + if (report->remote_metrics.rtcp_sr_count+report->remote_metrics.rtcp_xr_count>0){ + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "RemoteMetrics:")); + CU_ASSERT_PTR_NOT_NULL(body=__strstr(body, "Timestamps:")); + on_report_send_verify_metrics(&report->remote_metrics,body); + } +} +void on_report_send_with_rtcp_xr_both(const LinphoneCall *call, int stream_type, const LinphoneContent *content){ + on_report_send_with_rtcp_xr_local(call,stream_type,content); + on_report_send_with_rtcp_xr_remote(call,stream_type,content); +} + void create_call_for_quality_reporting_tests( LinphoneCoreManager* marie, LinphoneCoreManager* pauline, @@ -42,7 +129,7 @@ static void quality_reporting_not_used_without_config() { create_call_for_quality_reporting_tests(marie, pauline, &call_marie, &call_pauline); - // marie has stats collection enabled since pauline has not + // marie has stats collection enabled but pauline has not CU_ASSERT_TRUE(linphone_proxy_config_quality_reporting_enabled(call_marie->dest_proxy)); CU_ASSERT_FALSE(linphone_proxy_config_quality_reporting_enabled(call_pauline->dest_proxy)); @@ -111,11 +198,12 @@ static void quality_reporting_not_sent_if_low_bandwidth() { static void quality_reporting_at_call_termination() { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc_rtcp_xr"); LinphoneCall* call_marie = NULL; LinphoneCall* call_pauline = NULL; create_call_for_quality_reporting_tests(marie, pauline, &call_marie, &call_pauline); + linphone_reporting_set_on_report_send(call_marie, on_report_send_with_rtcp_xr_remote); linphone_core_terminate_all_calls(marie->lc); @@ -128,7 +216,6 @@ static void quality_reporting_at_call_termination() { CU_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc)); CU_ASSERT_PTR_NULL(linphone_core_get_current_call(pauline->lc)); - // PUBLISH submission to the collector should be ok CU_ASSERT_TRUE(wait_for(marie->lc,NULL,&marie->stat.number_of_LinphonePublishProgress,1)); CU_ASSERT_TRUE(wait_for(marie->lc,NULL,&marie->stat.number_of_LinphonePublishOk,1)); @@ -138,12 +225,13 @@ static void quality_reporting_at_call_termination() { } static void quality_reporting_interval_report() { - LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc_rtcp_xr"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc_rtcp_xr"); LinphoneCall* call_marie = NULL; LinphoneCall* call_pauline = NULL; create_call_for_quality_reporting_tests(marie, pauline, &call_marie, &call_pauline); + linphone_reporting_set_on_report_send(call_marie, on_report_send_mandatory); linphone_proxy_config_set_quality_reporting_interval(call_marie->dest_proxy, 3); CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc)); @@ -158,7 +246,7 @@ static void quality_reporting_interval_report() { } static void quality_reporting_session_report_if_video_stopped() { - LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc_rtcp_xr"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneCall* call_pauline = NULL; LinphoneCallParams* pauline_params; @@ -174,6 +262,7 @@ static void quality_reporting_session_report_if_video_stopped() { linphone_call_params_enable_video(pauline_params,TRUE); CU_ASSERT_TRUE(call_with_params(pauline,marie,pauline_params,marie_params)); call_pauline=linphone_core_get_current_call(pauline->lc); + linphone_reporting_set_on_report_send(linphone_core_get_current_call(marie->lc), on_report_send_with_rtcp_xr_local); CU_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishProgress,0); CU_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishOk,0); diff --git a/tester/rcfiles/marie_rc_rtcp_xr b/tester/rcfiles/marie_rc_rtcp_xr new file mode 100644 index 000000000..93f8e5bd0 --- /dev/null +++ b/tester/rcfiles/marie_rc_rtcp_xr @@ -0,0 +1,55 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=marie +userid=marie +passwd=secret +realm=sip.example.org + + +[proxy_0] +reg_proxy=sip.example.org;transport=tcp +reg_route=sip.example.org;transport=tcp;lr +reg_identity=sip:marie@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 +quality_reporting_collector=sip:collector@sip.example.org +quality_reporting_enabled=1 + +[friend_0] +url="Paupoche" +pol=accept +subscribe=0 + + +[rtp] +audio_rtp_port=8070 +video_rtp_port=9072 +rtcp_xr_enabled=1 +rtcp_xr_rcvr_rtt_mode=all +rtcp_xr_rcvr_rtt_max_size=10000 +rtcp_xr_stat_summary_enabled=1 +rtcp_xr_voip_metrics_enabled=1 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG diff --git a/tester/rcfiles/pauline_rc_rtcp_xr b/tester/rcfiles/pauline_rc_rtcp_xr new file mode 100644 index 000000000..331f942ef --- /dev/null +++ b/tester/rcfiles/pauline_rc_rtcp_xr @@ -0,0 +1,52 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=pauline +userid=pauline +passwd=secret +realm=sip.example.org + + +[proxy_0] +reg_proxy=sip2.linphone.org;transport=tls +reg_route=sip2.linphone.org;transport=tls +reg_identity=sip:pauline@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 + +#[friend_0] +#url="Mariette" +#pol=accept +#subscribe=0 + +[rtp] +audio_rtp_port=8090 +video_rtp_port=9092 +rtcp_xr_enabled=1 +rtcp_xr_rcvr_rtt_mode=all +rtcp_xr_rcvr_rtt_max_size=10000 +rtcp_xr_stat_summary_enabled=1 +rtcp_xr_voip_metrics_enabled=1 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG