Commit 83b94af9 authored by Sylvain Berfini's avatar Sylvain Berfini 🎩

LinphoneCallStats is now a belle_sip_object. BREAKS BINARY COMPATIBILITY

parent 1dcd1719
......@@ -21,7 +21,7 @@
############################################################################
cmake_minimum_required(VERSION 3.0)
project(linphone VERSION 3.11.1 LANGUAGES C CXX)
project(linphone VERSION 3.11.2 LANGUAGES C CXX)
set(LINPHONE_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
......
dnl Process this file with autoconf to produce a configure script.
AC_INIT([linphone],[3.11.1],[linphone-developers@nongnu.org])
AC_INIT([linphone],[3.11.2],[linphone-developers@nongnu.org])
AC_CANONICAL_SYSTEM
AC_CONFIG_SRCDIR([coreapi/linphonecore.c])
......
This diff is collapsed.
......@@ -548,19 +548,19 @@ static void linphone_core_add_local_ice_candidates(LinphoneCall *call, int famil
if ((ice_check_list_state(audio_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_cl) == FALSE)) {
ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL);
ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL);
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
call->audio_stats->ice_state = LinphoneIceStateInProgress;
}
if (linphone_core_video_enabled(call->core) && (video_cl != NULL)
&& (ice_check_list_state(video_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(video_cl) == FALSE)) {
ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL);
ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL);
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
call->video_stats->ice_state = LinphoneIceStateInProgress;
}
if (call->params->realtimetext_enabled && (text_cl != NULL)
&& (ice_check_list_state(text_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(text_cl) == FALSE)) {
ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL);
ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL);
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress;
call->text_stats->ice_state = LinphoneIceStateInProgress;
}
}
......@@ -697,14 +697,14 @@ void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
if (ice_check_list_state(audio_check_list) == ICL_Completed) {
switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
case ICT_HostCandidate:
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
call->audio_stats->ice_state = LinphoneIceStateHostConnection;
break;
case ICT_ServerReflexiveCandidate:
case ICT_PeerReflexiveCandidate:
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
call->audio_stats->ice_state = LinphoneIceStateReflexiveConnection;
break;
case ICT_RelayedCandidate:
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
call->audio_stats->ice_state = LinphoneIceStateRelayConnection;
break;
case ICT_CandidateInvalid:
case ICT_CandidateTypeMax:
......@@ -712,22 +712,22 @@ void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
break;
}
} else {
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
call->audio_stats->ice_state = LinphoneIceStateFailed;
}
}else call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
}else call->audio_stats->ice_state = LinphoneIceStateNotActivated;
if (call->params->has_video && (video_check_list != NULL)) {
if (ice_check_list_state(video_check_list) == ICL_Completed) {
switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
case ICT_HostCandidate:
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
call->video_stats->ice_state = LinphoneIceStateHostConnection;
break;
case ICT_ServerReflexiveCandidate:
case ICT_PeerReflexiveCandidate:
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
call->video_stats->ice_state = LinphoneIceStateReflexiveConnection;
break;
case ICT_RelayedCandidate:
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
call->video_stats->ice_state = LinphoneIceStateRelayConnection;
break;
case ICT_CandidateInvalid:
case ICT_CandidateTypeMax:
......@@ -735,22 +735,22 @@ void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
break;
}
} else {
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
call->video_stats->ice_state = LinphoneIceStateFailed;
}
}else call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
}else call->video_stats->ice_state = LinphoneIceStateNotActivated;
if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
if (ice_check_list_state(text_check_list) == ICL_Completed) {
switch (ice_check_list_selected_valid_candidate_type(text_check_list)) {
case ICT_HostCandidate:
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateHostConnection;
call->text_stats->ice_state = LinphoneIceStateHostConnection;
break;
case ICT_ServerReflexiveCandidate:
case ICT_PeerReflexiveCandidate:
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateReflexiveConnection;
call->text_stats->ice_state = LinphoneIceStateReflexiveConnection;
break;
case ICT_RelayedCandidate:
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateRelayConnection;
call->text_stats->ice_state = LinphoneIceStateRelayConnection;
break;
case ICT_CandidateInvalid:
case ICT_CandidateTypeMax:
......@@ -758,28 +758,28 @@ void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
break;
}
} else {
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateFailed;
call->text_stats->ice_state = LinphoneIceStateFailed;
}
}else call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateNotActivated;
}else call->text_stats->ice_state = LinphoneIceStateNotActivated;
} else if (session_state == IS_Running) {
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
call->audio_stats->ice_state = LinphoneIceStateInProgress;
if (call->params->has_video && (video_check_list != NULL)) {
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
call->video_stats->ice_state = LinphoneIceStateInProgress;
}
if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress;
call->text_stats->ice_state = LinphoneIceStateInProgress;
}
} else {
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
call->audio_stats->ice_state = LinphoneIceStateFailed;
if (call->params->has_video && (video_check_list != NULL)) {
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
call->video_stats->ice_state = LinphoneIceStateFailed;
}
if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateFailed;
call->text_stats->ice_state = LinphoneIceStateFailed;
}
}
ms_message("Call [%p] New ICE state: audio: [%s] video: [%s] text: [%s]", call,
linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state), linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state), linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_TEXT].ice_state));
linphone_ice_state_to_string(call->audio_stats->ice_state), linphone_ice_state_to_string(call->video_stats->ice_state), linphone_ice_state_to_string(call->text_stats->ice_state));
}
void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDescription *desc) {
......
......@@ -351,7 +351,9 @@ struct _LinphoneCall{
OrtpEvQueue *videostream_app_evq;
OrtpEvQueue *textstream_app_evq;
CallCallbackObj nextVideoFrameDecoded;
LinphoneCallStats stats[3]; /* audio, video, text */
LinphoneCallStats *audio_stats;
LinphoneCallStats *video_stats;
LinphoneCallStats *text_stats;
#ifdef BUILD_UPNP
UpnpSession *upnp_session;
#endif //BUILD_UPNP
......@@ -1761,6 +1763,41 @@ BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneVideoActivationPolicy);
LinphoneVideoActivationPolicy *linphone_video_activation_policy_new(void);
/**
* The LinphoneCallStats objects carries various statistic informations regarding quality of audio or video streams.
*
* To receive these informations periodically and as soon as they are computed, the application is invited to place a #LinphoneCoreCallStatsUpdatedCb callback in the LinphoneCoreVTable structure
* it passes for instantiating the LinphoneCore object (see linphone_core_new() ).
*
* At any time, the application can access last computed statistics using linphone_call_get_audio_stats() or linphone_call_get_video_stats().
**/
struct _LinphoneCallStats {
belle_sip_object_t base;
void *user_data;
LinphoneStreamType type; /**< Type of the stream which the stats refer to */
jitter_stats_t jitter_stats; /**<jitter buffer statistics, see oRTP documentation for details */
mblk_t *received_rtcp; /**<Last RTCP packet received, as a mblk_t structure. See oRTP documentation for details how to extract information from it*/
mblk_t *sent_rtcp;/**<Last RTCP packet sent, as a mblk_t structure. See oRTP documentation for details how to extract information from it*/
float round_trip_delay; /**<Round trip propagation time in seconds if known, -1 if unknown.*/
LinphoneIceState ice_state; /**< State of ICE processing. */
LinphoneUpnpState upnp_state; /**< State of uPnP processing. */
float download_bandwidth; /**<Download bandwidth measurement of received stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float upload_bandwidth; /**<Download bandwidth measurement of sent stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float local_late_rate; /**<percentage of packet received too late over last second*/
float local_loss_rate; /**<percentage of lost packet over last second*/
int updated; /**< Tell which RTCP packet has been updated (received_rtcp or sent_rtcp). Can be either LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE or LINPHONE_CALL_STATS_SENT_RTCP_UPDATE */
float rtcp_download_bandwidth; /**<RTCP download bandwidth measurement of received stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float rtcp_upload_bandwidth; /**<RTCP download bandwidth measurement of sent stream, expressed in kbit/s, including IP/UDP/RTP headers*/
rtp_stats_t rtp_stats; /**< RTP stats */
int rtp_remote_family; /**< Ip adress family of the remote destination */
int clockrate; /*RTP clockrate of the stream, provided here for easily converting timestamp units expressed in RTCP packets in milliseconds*/
bool_t rtcp_received_via_mux; /*private flag, for non-regression test only*/
};
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneCallStats);
LinphoneCallStats *linphone_call_stats_new(void);
/** Belle Sip-based objects need unique ids
*/
......@@ -1814,7 +1851,8 @@ BELLE_SIP_TYPE_ID(LinphonePayloadType),
BELLE_SIP_TYPE_ID(LinphoneRange),
BELLE_SIP_TYPE_ID(LinphoneVideoDefinition),
BELLE_SIP_TYPE_ID(LinphoneTransports),
BELLE_SIP_TYPE_ID(LinphoneVideoActivationPolicy)
BELLE_SIP_TYPE_ID(LinphoneVideoActivationPolicy),
BELLE_SIP_TYPE_ID(LinphoneCallStats)
BELLE_SIP_DECLARE_TYPES_END
......
......@@ -592,21 +592,29 @@ static float reporting_rand(float t){
void linphone_reporting_on_rtcp_update(LinphoneCall *call, SalStreamType stats_type) {
reporting_session_report_t * report = call->log->reporting.reports[stats_type];
reporting_content_metrics_t * metrics = NULL;
LinphoneCallStats stats = call->stats[stats_type];
LinphoneCallStats *stats = NULL;
mblk_t *block = NULL;
int report_interval;
if (stats_type == 0) {
stats = call->audio_stats;
} else if (stats_type == 1) {
stats = call->video_stats;
} else {
stats = call->text_stats;
}
if (! media_report_enabled(call,stats_type))
return;
report_interval = linphone_proxy_config_get_quality_reporting_interval(call->dest_proxy);
if (stats.updated == LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE) {
if (stats->updated == LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE) {
metrics = &report->remote_metrics;
block = stats.received_rtcp;
} else if (stats.updated == LINPHONE_CALL_STATS_SENT_RTCP_UPDATE) {
block = stats->received_rtcp;
} else if (stats->updated == LINPHONE_CALL_STATS_SENT_RTCP_UPDATE) {
metrics = &report->local_metrics;
block = stats.sent_rtcp;
block = stats->sent_rtcp;
}
do{
if (rtcp_is_XR(block) && (rtcp_XR_get_block_type(block) == RTCP_XR_VOIP_METRICS)){
......@@ -617,7 +625,7 @@ void linphone_reporting_on_rtcp_update(LinphoneCall *call, SalStreamType stats_t
// for local mos rating, we'll use the quality indicator directly
// because rtcp XR might not be enabled
if (stats.updated == LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE){
if (stats->updated == LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE){
metrics->quality_estimates.moslq = (rtcp_XR_voip_metrics_get_mos_lq(block)==127) ?
127 : rtcp_XR_voip_metrics_get_mos_lq(block) / 10.f;
metrics->quality_estimates.moscq = (rtcp_XR_voip_metrics_get_mos_cq(block)==127) ?
......
......@@ -61,5 +61,5 @@ void AudioStreamStatsCommand::exec(Daemon *app, const string& args) {
return;
}
app->sendResponse(Response(AudioStreamStatsResponse(app, stream->stream, &stream->stats, false).getBody(), Response::Ok));
app->sendResponse(Response(AudioStreamStatsResponse(app, stream->stream, stream->stats, false).getBody(), Response::Ok));
}
......@@ -141,13 +141,13 @@ DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
}
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
ostr << prefix << "ICE state: " << ice_state_str[stats->ice_state] << "\n";
ostr << prefix << "RoundTripDelay: " << stats->round_trip_delay << "\n";
ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
ostr << prefix << "ICE state: " << ice_state_str[linphone_call_stats_get_ice_state(stats)] << "\n";
ostr << prefix << "RoundTripDelay: " << linphone_call_stats_get_round_trip_delay(stats) << "\n";
// ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
// ostr << prefix << "MaxJitter: " << stats->jitter_stats.max_jitter << "\n";
// ostr << prefix << "SumJitter: " << stats->jitter_stats.sum_jitter << "\n";
// ostr << prefix << "MaxJitterTs: " << stats->jitter_stats.max_jitter_ts << "\n";
ostr << prefix << "JitterBufferSizeMs: " << stats->jitter_stats.jitter_buffer_size_ms << "\n";
ostr << prefix << "JitterBufferSizeMs: " << linphone_call_stats_get_jitter_buffer_size_ms(stats) << "\n";
ostr << prefix << "Received-InterarrivalJitter: " << linphone_call_stats_get_receiver_interarrival_jitter(stats) << "\n";
ostr << prefix << "Received-FractionLost: " << linphone_call_stats_get_receiver_loss_rate(stats) << "\n";
......@@ -199,14 +199,14 @@ AudioStreamStatsResponse::AudioStreamStatsResponse(Daemon* daemon, AudioStream*
ostr << "Event-type: audio-stream-stats\n";
ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
ostr << "Type: ";
if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
ostr << "Audio";
} else {
ostr << "Video";
}
ostr << "\n";
} else {
prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
prefix = ((linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
}
printCallStatsHelper(ostr, stats, prefix);
......@@ -547,9 +547,9 @@ void Daemon::iterateStreamStats() {
while (it->second->queue && (NULL != (ev=ortp_ev_queue_get(it->second->queue)))){
OrtpEventType evt=ortp_event_get_type(ev);
if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
linphone_call_stats_fill(&it->second->stats, &it->second->stream->ms, ev);
linphone_call_stats_fill(it->second->stats, &it->second->stream->ms, ev);
if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsResponse(this,
it->second->stream, &it->second->stats, true));
it->second->stream, it->second->stats, true));
}
ortp_event_destroy(ev);
}
......
......@@ -191,11 +191,10 @@ private:
struct AudioStreamAndOther {
AudioStream *stream;
OrtpEvQueue *queue;
LinphoneCallStats stats;
LinphoneCallStats *stats;
AudioStreamAndOther(AudioStream *as) : stream(as) {
queue = ortp_ev_queue_new();
rtp_session_register_event_queue(as->ms.sessions.rtp_session, queue);
memset(&stats, 0, sizeof(stats));
}
~AudioStreamAndOther() {
rtp_session_unregister_event_queue(stream->ms.sessions.rtp_session, queue);
......
......@@ -243,8 +243,10 @@ static const char *upnp_state_to_string(LinphoneUpnpState ice_state){
}
static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){
const LinphoneCallStats *as=linphone_call_get_audio_stats(call);
const LinphoneCallStats *vs=linphone_call_get_video_stats(call);
LinphoneUpnpState upnp_state;
LinphoneIceState ice_state;
LinphoneCallStats *as=linphone_call_get_audio_stats(call);
LinphoneCallStats *vs=linphone_call_get_video_stats(call);
const char *audio_media_connectivity = _("Direct or through server");
const char *video_media_connectivity = _("Direct or through server");
const LinphoneCallParams *curparams=linphone_call_get_current_params(call);
......@@ -256,7 +258,7 @@ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"rtp_profile")),tmp);
g_free(tmp);
tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),
as->download_bandwidth,as->upload_bandwidth);
linphone_call_stats_get_download_bandwidth(as),linphone_call_stats_get_upload_bandwidth(as));
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_bandwidth_usage")),tmp);
g_free(tmp);
if (has_video){
......@@ -267,7 +269,7 @@ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_size_recv")),size_r);
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_size_sent")),size_s);
tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),vs->download_bandwidth,vs->upload_bandwidth);
tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),linphone_call_stats_get_download_bandwidth(vs),linphone_call_stats_get_upload_bandwidth(vs));
g_free(size_r);
g_free(size_s);
} else {
......@@ -275,27 +277,33 @@ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){
}
gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_bandwidth_usage")),tmp);
if (tmp) g_free(tmp);
if(as->upnp_state != LinphoneUpnpStateNotAvailable && as->upnp_state != LinphoneUpnpStateIdle) {
audio_media_connectivity = upnp_state_to_string(as->upnp_state);
} else if(as->ice_state != LinphoneIceStateNotActivated) {
audio_media_connectivity = ice_state_to_string(as->ice_state);
upnp_state = linphone_call_stats_get_upnp_state(as);
ice_state = linphone_call_stats_get_ice_state(as);
if(upnp_state != LinphoneUpnpStateNotAvailable && upnp_state != LinphoneUpnpStateIdle) {
audio_media_connectivity = upnp_state_to_string(upnp_state);
} else if(ice_state != LinphoneIceStateNotActivated) {
audio_media_connectivity = ice_state_to_string(ice_state);
}
gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_media_connectivity")),audio_media_connectivity);
if (has_video){
if(vs->upnp_state != LinphoneUpnpStateNotAvailable && vs->upnp_state != LinphoneUpnpStateIdle) {
video_media_connectivity = upnp_state_to_string(vs->upnp_state);
} else if(vs->ice_state != LinphoneIceStateNotActivated) {
video_media_connectivity = ice_state_to_string(vs->ice_state);
upnp_state = linphone_call_stats_get_upnp_state(vs);
ice_state = linphone_call_stats_get_ice_state(vs);
if(upnp_state != LinphoneUpnpStateNotAvailable && upnp_state != LinphoneUpnpStateIdle) {
video_media_connectivity = upnp_state_to_string(upnp_state);
} else if(ice_state != LinphoneIceStateNotActivated) {
video_media_connectivity = ice_state_to_string(ice_state);
}
}else video_media_connectivity=NULL;
gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_media_connectivity")),video_media_connectivity);
if (as->round_trip_delay>0){
tmp=g_strdup_printf(_("%.3f seconds"),as->round_trip_delay);
if (linphone_call_stats_get_round_trip_delay(as)>0){
tmp=g_strdup_printf(_("%.3f seconds"),linphone_call_stats_get_round_trip_delay(as));
gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"round_trip_time")),tmp);
g_free(tmp);
}
linphone_call_stats_unref(as);
linphone_call_stats_unref(vs);
}
static gboolean refresh_call_stats(GtkWidget *callstats){
......
......@@ -834,17 +834,17 @@ LINPHONE_PUBLIC void linphone_call_ogl_render(LinphoneCall *call, bool_t is_prev
LINPHONE_PUBLIC LinphoneStatus linphone_call_send_info_message(LinphoneCall *call, const LinphoneInfoMessage *info);
/**
* Return call statistics for a particular stream type.
* Return a copy of the call statistics for a particular stream type.
* @param call the call
* @param type the stream type
**/
LINPHONE_PUBLIC const LinphoneCallStats *linphone_call_get_stats(LinphoneCall *call, LinphoneStreamType type);
LINPHONE_PUBLIC LinphoneCallStats *linphone_call_get_stats(LinphoneCall *call, LinphoneStreamType type);
LINPHONE_PUBLIC const LinphoneCallStats *linphone_call_get_audio_stats(LinphoneCall *call);
LINPHONE_PUBLIC LinphoneCallStats *linphone_call_get_audio_stats(LinphoneCall *call);
LINPHONE_PUBLIC const LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call);
LINPHONE_PUBLIC LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call);
LINPHONE_PUBLIC const LinphoneCallStats *linphone_call_get_text_stats(LinphoneCall *call);
LINPHONE_PUBLIC LinphoneCallStats *linphone_call_get_text_stats(LinphoneCall *call);
/**
* Add a listener in order to be notified of LinphoneCall events. Once an event is received, registred LinphoneCallCbs are
......
......@@ -40,33 +40,34 @@ extern "C" {
#define LINPHONE_CALL_STATS_PERIODICAL_UPDATE (1 << 2) /**< Every seconds LinphoneCallStats object has been updated */
/**
* The LinphoneCallStats objects carries various statistic informations regarding quality of audio or video streams.
*
* To receive these informations periodically and as soon as they are computed, the application is invited to place a #LinphoneCoreCallStatsUpdatedCb callback in the LinphoneCoreVTable structure
* it passes for instantiating the LinphoneCore object (see linphone_core_new() ).
*
* At any time, the application can access last computed statistics using linphone_call_get_audio_stats() or linphone_call_get_video_stats().
* Increment refcount.
* @param[in] stats LinphoneCallStats object
* @ingroup misc
**/
LINPHONE_PUBLIC LinphoneCallStats *linphone_call_stats_ref(LinphoneCallStats *stats);
/**
* Decrement refcount and possibly free the object.
* @param[in] stats LinphoneCallStats object
* @ingroup misc
**/
struct _LinphoneCallStats {
LinphoneStreamType type; /**< Type of the stream which the stats refer to */
jitter_stats_t jitter_stats; /**<jitter buffer statistics, see oRTP documentation for details */
mblk_t *received_rtcp; /**<Last RTCP packet received, as a mblk_t structure. See oRTP documentation for details how to extract information from it*/
mblk_t *sent_rtcp;/**<Last RTCP packet sent, as a mblk_t structure. See oRTP documentation for details how to extract information from it*/
float round_trip_delay; /**<Round trip propagation time in seconds if known, -1 if unknown.*/
LinphoneIceState ice_state; /**< State of ICE processing. */
LinphoneUpnpState upnp_state; /**< State of uPnP processing. */
float download_bandwidth; /**<Download bandwidth measurement of received stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float upload_bandwidth; /**<Download bandwidth measurement of sent stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float local_late_rate; /**<percentage of packet received too late over last second*/
float local_loss_rate; /**<percentage of lost packet over last second*/
int updated; /**< Tell which RTCP packet has been updated (received_rtcp or sent_rtcp). Can be either LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE or LINPHONE_CALL_STATS_SENT_RTCP_UPDATE */
float rtcp_download_bandwidth; /**<RTCP download bandwidth measurement of received stream, expressed in kbit/s, including IP/UDP/RTP headers*/
float rtcp_upload_bandwidth; /**<RTCP download bandwidth measurement of sent stream, expressed in kbit/s, including IP/UDP/RTP headers*/
rtp_stats_t rtp_stats; /**< RTP stats */
int rtp_remote_family; /**< Ip adress family of the remote destination */
int clockrate; /*RTP clockrate of the stream, provided here for easily converting timestamp units expressed in RTCP packets in milliseconds*/
bool_t rtcp_received_via_mux; /*private flag, for non-regression test only*/
};
LINPHONE_PUBLIC void linphone_call_stats_unref(LinphoneCallStats *stats);
/**
* Gets the user data in the LinphoneCallStats object
* @param[in] stats the LinphoneCallStats
* @return the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void *linphone_call_stats_get_user_data(const LinphoneCallStats *stats);
/**
* Sets the user data in the LinphoneCallStats object
* @param[in] stats the LinphoneCallStats object
* @param[in] data the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_call_stats_set_user_data(LinphoneCallStats *stats, void *data);
/**
* Get the type of the stream the stats refer to.
......@@ -152,6 +153,13 @@ LINPHONE_PUBLIC LinphoneAddressFamily linphone_call_stats_get_ip_family_of_remot
*/
LINPHONE_PUBLIC float linphone_call_stats_get_jitter_buffer_size_ms(const LinphoneCallStats *stats);
/**
* Get the round trip delay in s.
* @param[in] stats LinphoneCallStats object
* @return The round trip delay in s.
*/
LINPHONE_PUBLIC float linphone_call_stats_get_round_trip_delay(const LinphoneCallStats *stats);
/**
* @}
*/
......
......@@ -83,6 +83,7 @@ static void early_media_with_multicast_base(bool_t video) {
LinphoneVideoPolicy marie_policy, pauline_policy;
LpConfig *marie_lp;
LinphoneCallParams *params;
LinphoneCallStats *stats = NULL;
marie = linphone_core_manager_new("marie_rc");
pauline = linphone_core_manager_new("pauline_tcp_rc");
......@@ -167,11 +168,15 @@ static void early_media_with_multicast_base(bool_t video) {
wait_for_list(lcs, &dummy, 1, 3000);
stats = linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc));
BC_ASSERT_GREATER(linphone_core_manager_get_max_audio_down_bw(pauline),70,int,"%i");
BC_ASSERT_LOWER((int)linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth, 90, int, "%i");
BC_ASSERT_LOWER((int)linphone_call_stats_get_download_bandwidth(stats), 90, int, "%i");
linphone_call_stats_unref(stats);
stats = linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc));
BC_ASSERT_GREATER(linphone_core_manager_get_max_audio_down_bw(pauline2),70,int,"%i");
BC_ASSERT_LOWER((int)linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc))->download_bandwidth,90, int, "%i");
BC_ASSERT_LOWER((int)linphone_call_stats_get_download_bandwidth(stats),90, int, "%i");
linphone_call_stats_unref(stats);
BC_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))));
BC_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc))));
......
This diff is collapsed.
......@@ -26,21 +26,30 @@
#if HAVE_SIPP
void check_rtcp(LinphoneCall *call) {
MSTimeSpec ts;
LinphoneCallStats *audio_stats, *video_stats;
linphone_call_ref(call);
liblinphone_tester_clock_start(&ts);
do {
if (linphone_call_get_audio_stats(call)->round_trip_delay > 0.0 && (!linphone_call_log_video_enabled(linphone_call_get_call_log(call)) || linphone_call_get_video_stats(call)->round_trip_delay > 0.0)) {
audio_stats = linphone_call_get_audio_stats(call);
video_stats = linphone_call_get_video_stats(call);
if (linphone_call_stats_get_round_trip_delay(audio_stats) > 0.0 && (!linphone_call_log_video_enabled(linphone_call_get_call_log(call)) || linphone_call_stats_get_round_trip_delay(video_stats) > 0.0)) {
break;
}
linphone_call_stats_unref(audio_stats);
if (video_stats) linphone_call_stats_unref(video_stats);
wait_for_until(call->core, NULL, NULL, 0, 20); /*just to sleep while iterating*/
} while (!liblinphone_tester_clock_elapsed(&ts, 15000));
BC_ASSERT_GREATER(linphone_call_get_audio_stats(call)->round_trip_delay, 0.0, float, "%f");
audio_stats = linphone_call_get_audio_stats(call);
BC_ASSERT_GREATER(linphone_call_stats_get_round_trip_delay(audio_stats), 0.0, float, "%f");
if (linphone_call_log_video_enabled(linphone_call_get_call_log(call))) {
BC_ASSERT_GREATER(linphone_call_get_video_stats(call)->round_trip_delay, 0.0, float, "%f");
video_stats = linphone_call_get_video_stats(call);
BC_ASSERT_GREATER(linphone_call_stats_get_round_trip_delay(video_stats), 0.0, float, "%f");
linphone_call_stats_unref(video_stats);
}
linphone_call_stats_unref(audio_stats);
linphone_call_unref(call);
}
......
......@@ -677,6 +677,7 @@ void liblinphone_tester_uninit(void) {
static void check_ice_from_rtp(LinphoneCall *c1, LinphoneCall *c2, LinphoneStreamType stream_type) {
MediaStream *ms;
LinphoneCallStats *stats;
switch (stream_type) {
case LinphoneStreamTypeAudio:
ms=&c1->audiostream->ms;
......@@ -693,7 +694,8 @@ static void check_ice_from_rtp(LinphoneCall *c1, LinphoneCall *c2, LinphoneStrea
return;
}
if (linphone_call_get_audio_stats(c1)->ice_state == LinphoneIceStateHostConnection && media_stream_started(ms)) {
stats = linphone_call_get_audio_stats(c1);
if (stats->ice_state == LinphoneIceStateHostConnection && media_stream_started(ms)) {
struct sockaddr_storage remaddr;
socklen_t remaddrlen = sizeof(remaddr);
char ip[NI_MAXHOST] = { 0 };
......@@ -718,6 +720,7 @@ static void check_ice_from_rtp(LinphoneCall *c1, LinphoneCall *c2, LinphoneStrea
BC_ASSERT_STRING_EQUAL(ip, expected_addr);
}
}
linphone_call_stats_unref(stats);
}
bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, LinphoneIceState state) {
......@@ -747,13 +750,17 @@ bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, Linph
liblinphone_tester_clock_start(&ts);
do{
if ((c1 != NULL) && (c2 != NULL)) {
if (linphone_call_get_audio_stats(c1)->ice_state==state &&
linphone_call_get_audio_stats(c2)->ice_state==state ){
LinphoneCallStats *stats1 = linphone_call_get_audio_stats(c1);
LinphoneCallStats *stats2 = linphone_call_get_audio_stats(c2);
if (stats1->ice_state==state &&
stats2->ice_state==state){
audio_success=TRUE;
check_ice_from_rtp(c1,c2,LinphoneStreamTypeAudio);
check_ice_from_rtp(c2,c1,LinphoneStreamTypeAudio);
break;
}
linphone_call_stats_unref(stats1);
linphone_call_stats_unref(stats2);
linphone_core_iterate(caller->lc);
linphone_core_iterate(callee->lc);
}
......@@ -765,13 +772,17 @@ bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, Linph
liblinphone_tester_clock_start(&ts);
do{
if ((c1 != NULL) && (c2 != NULL)) {
if (linphone_call_get_video_stats(c1)->ice_state==state &&
linphone_call_get_video_stats(c2)->ice_state==state ){
LinphoneCallStats *stats1 = linphone_call_get_video_stats(c1);
LinphoneCallStats *stats2 = linphone_call_get_video_stats(c2);
if (stats1->ice_state==state &&
stats2->ice_state==state){
video_success=TRUE;
check_ice_from_rtp(c1,c2,LinphoneStreamTypeVideo);
check_ice_from_rtp(c2,c1,LinphoneStreamTypeVideo);
break;
}
linphone_call_stats_unref(stats1);
linphone_call_stats_unref(stats2);
linphone_core_iterate(caller->lc);
linphone_core_iterate(callee->lc);
}
......@@ -783,13 +794,17 @@ bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, Linph
liblinphone_tester_clock_start(&ts);
do{
if ((c1 != NULL) && (c2 != NULL)) {
if (linphone_call_get_text_stats(c1)->ice_state==state &&
linphone_call_get_text_stats(c2)->ice_state==state ){
LinphoneCallStats *stats1 = linphone_call_get_text_stats(c1);
LinphoneCallStats *stats2 = linphone_call_get_text_stats(c2);
if (stats1->ice_state==state &&
stats2->ice_state==state){
text_success=TRUE;
check_ice_from_rtp(c1,c2,LinphoneStreamTypeText);
check_ice_from_rtp(c2,c1,LinphoneStreamTypeText);
break;
}
linphone_call_stats_unref(stats1);
linphone_call_stats_unref(stats2);
linphone_core_iterate(caller->lc);
linphone_core_iterate(callee->lc);
}
......
......@@ -481,18 +481,32 @@ static void forked_outgoing_early_media_video_call_with_inactive_audio_test(void
BC_ASSERT_PTR_NOT_NULL(marie2_call);
if (pauline_call && marie1_call && marie2_call) {
LinphoneCallStats *pauline_audio_stats, *marie1_audio_stats, *marie2_audio_stats;
LinphoneCallStats *pauline_video_stats, *marie1_video_stats, *marie2_video_stats;
linphone_call_set_next_video_frame_decoded_callback(pauline_call, linphone_call_iframe_decoded_cb, pauline->lc);
/* wait a bit that streams are established */
wait_for_list(lcs, &dummy, 1, 3000);
BC_ASSERT_EQUAL(linphone_call_get_audio_stats(pauline_call)->download_bandwidth, 0, float, "%f");
BC_ASSERT_EQUAL(linphone_call_get_audio_stats(marie1_call)->download_bandwidth, 0, float, "%f");
BC_ASSERT_EQUAL(linphone_call_get_audio_stats(marie2_call)->download_bandwidth, 0, float, "%f");
BC_ASSERT_LOWER(linphone_call_get_video_stats(pauline_call)->download_bandwidth, 11, float, "%f"); /* because of stun packets*/
BC_ASSERT_GREATER(linphone_call_get_video_stats(marie1_call)->download_bandwidth, 0, float, "%f");
BC_ASSERT_GREATER(linphone_call_get_video_stats(marie2_call)->download_bandwidth, 0, float, "%f");
pauline_audio_stats = linphone_call_get_audio_stats(pauline_call);
marie1_audio_stats = linphone_call_get_audio_stats(marie1_call);
marie2_audio_stats = linphone_call_get_audio_stats(marie2_call);
pauline_video_stats = linphone_call_get_video_stats(pauline_call);
marie1_video_stats = linphone_call_get_video_stats(marie1_call);
marie2_video_stats = linphone_call_get_video_stats(marie2_call);
BC_ASSERT_EQUAL(pauline_audio_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_EQUAL(marie1_audio_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_EQUAL(marie2_audio_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_LOWER(pauline_video_stats->download_bandwidth, 11, float, "%f"); /* because of stun packets*/
BC_ASSERT_GREATER(marie1_video_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_GREATER(marie2_video_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_GREATER(marie1->stat.number_of_IframeDecoded, 1, int, "%i");
BC_ASSERT_GREATER(marie2->stat.number_of_IframeDecoded, 1, int, "%i");
linphone_call_stats_unref(pauline_audio_stats);
linphone_call_stats_unref(marie1_audio_stats);
linphone_call_stats_unref(marie2_audio_stats);
linphone_call_stats_unref(pauline_video_stats);
linphone_call_stats_unref(marie1_video_stats);
linphone_call_stats_unref(marie2_video_stats);
linphone_call_params_set_audio_direction(marie1_params, LinphoneMediaDirectionSendRecv);
linphone_call_accept_with_params(linphone_core_get_current_call(marie1->lc), marie1_params);
......@@ -504,11 +518,19 @@ static void forked_outgoing_early_media_video_call_with_inactive_audio_test(void
/*wait a bit that streams are established*/
wait_for_list(lcs, &dummy, 1, 3000);
BC_ASSERT_GREATER(linphone_call_get_audio_stats(pauline_call)->download_bandwidth, 71, float, "%f");
BC_ASSERT_GREATER(linphone_call_get_audio_stats(marie1_call)->download_bandwidth, 71, float, "%f");
BC_ASSERT_GREATER(linphone_call_get_video_stats(pauline_call)->download_bandwidth, 0, float, "%f");
BC_ASSERT_GREATER(linphone_call_get_video_stats(marie1_call)->download_bandwidth, 0, float, "%f");
pauline_audio_stats = linphone_call_get_audio_stats(pauline_call);
marie1_audio_stats = linphone_call_get_audio_stats(marie1_call);
pauline_video_stats = linphone_call_get_video_stats(pauline_call);
marie1_video_stats = linphone_call_get_video_stats(marie1_call);
BC_ASSERT_GREATER(pauline_audio_stats->download_bandwidth, 71, float, "%f");
BC_ASSERT_GREATER(marie1_audio_stats->download_bandwidth, 71, float, "%f");
BC_ASSERT_GREATER(pauline_video_stats->download_bandwidth, 0, float, "%f");
BC_ASSERT_GREATER(marie1_video_stats->download_bandwidth, 0, float, "%f");