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])
......
......@@ -1106,9 +1106,9 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from,
linphone_core_get_text_port_range(call->core, &min_port, &max_port);
port_config_set(call,call->main_text_stream_index,min_port,max_port);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_TEXT], LINPHONE_CALL_STATS_TEXT);
linphone_call_init_stats(call->audio_stats, LINPHONE_CALL_STATS_AUDIO);
linphone_call_init_stats(call->video_stats, LINPHONE_CALL_STATS_VIDEO);
linphone_call_init_stats(call->text_stats, LINPHONE_CALL_STATS_TEXT);
}
void linphone_call_init_stats(LinphoneCallStats *stats, int type) {
......@@ -1307,6 +1307,9 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr
call->dir=LinphoneCallOutgoing;
call->core=lc;
call->dest_proxy=cfg;
call->audio_stats = linphone_call_stats_new();
call->video_stats = linphone_call_stats_new();
call->text_stats = linphone_call_stats_new();
linphone_call_outgoing_select_ip_version(call,to,cfg);
linphone_call_get_local_ip(call, to);
call->params = linphone_call_params_copy(params);
......@@ -1489,6 +1492,9 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
LinphoneNatPolicy *nat_policy = NULL;
int i;
call->dir=LinphoneCallIncoming;
call->audio_stats = linphone_call_stats_new();
call->video_stats = linphone_call_stats_new();
call->text_stats = linphone_call_stats_new();
sal_op_set_user_pointer(op,call);
call->op=op;
call->core=lc;
......@@ -1613,9 +1619,9 @@ static void linphone_call_free_media_resources(LinphoneCall *call){
for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i){
ms_media_stream_sessions_uninit(&call->sessions[i]);
}
linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_AUDIO]);
linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_VIDEO]);
linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_TEXT]);
linphone_call_stats_uninit(call->audio_stats);
linphone_call_stats_uninit(call->video_stats);
linphone_call_stats_uninit(call->text_stats);
}
/*
......@@ -1872,6 +1878,18 @@ static void linphone_call_destroy(LinphoneCall *obj){
if (obj->audiostream || obj->videostream){
linphone_call_free_media_resources(obj);
}
if (obj->audio_stats) {
linphone_call_stats_unref(obj->audio_stats);
obj->audio_stats = NULL;
}
if (obj->video_stats) {
linphone_call_stats_unref(obj->video_stats);
obj->video_stats = NULL;
}
if (obj->text_stats) {
linphone_call_stats_unref(obj->text_stats);
obj->text_stats = NULL;
}
if (obj->op!=NULL) {
sal_op_release(obj->op);
obj->op=NULL;
......@@ -3907,9 +3925,9 @@ void linphone_call_delete_ice_session(LinphoneCall *call){
if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = NULL;
if (call->videostream != NULL) call->videostream->ms.ice_check_list = NULL;
if (call->textstream != NULL) call->textstream->ms.ice_check_list = NULL;
call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateNotActivated;
call->audio_stats->ice_state = LinphoneIceStateNotActivated;
call->video_stats->ice_state = LinphoneIceStateNotActivated;
call->text_stats->ice_state = LinphoneIceStateNotActivated;
}
}
......@@ -3934,7 +3952,15 @@ static void linphone_call_log_fill_stats(LinphoneCallLog *log, MediaStream *st){
static void update_rtp_stats(LinphoneCall *call, int stream_index) {
if (call->sessions[stream_index].rtp_session) {
const rtp_stats_t *stats = rtp_session_get_stats(call->sessions[stream_index].rtp_session);
memcpy(&call->stats[stream_index].rtp_stats, stats, sizeof(*stats));
LinphoneCallStats *call_stats = NULL;
if (stream_index == call->main_audio_stream_index) {
call_stats = call->audio_stats;
} else if (stream_index == call->main_video_stream_index) {
call_stats = call->video_stats;
} else {
call_stats = call->text_stats;
}
if (call_stats) memcpy(&(call_stats->rtp_stats), stats, sizeof(*stats));
}
}
......@@ -4202,26 +4228,47 @@ static MediaStream *linphone_call_get_stream(LinphoneCall *call, LinphoneStreamT
return NULL;
}
const LinphoneCallStats *linphone_call_get_stats(LinphoneCall *call, LinphoneStreamType type){
static void _linphone_call_stats_clone(LinphoneCallStats *dst, const LinphoneCallStats *src) {
/*
* Save the belle_sip_object_t part, copy the entire structure and restore the belle_sip_object_t part
*/
belle_sip_object_t tmp = dst->base;
memcpy(dst, src, sizeof(LinphoneCallStats));
dst->base = tmp;
dst->received_rtcp = NULL;
dst->sent_rtcp = NULL;
}
LinphoneCallStats *linphone_call_get_stats(LinphoneCall *call, LinphoneStreamType type){
if ((int)type >=0 && type<=LinphoneStreamTypeText){
LinphoneCallStats *stats = &call->stats[type];
LinphoneCallStats *stats = NULL;
LinphoneCallStats *stats_copy = linphone_call_stats_new();
if (type == LinphoneStreamTypeAudio) {
stats = call->audio_stats;
} else if (type == LinphoneStreamTypeVideo) {
stats = call->video_stats;
} else if (type == LinphoneStreamTypeText) {
stats = call->text_stats;
}
MediaStream *ms = linphone_call_get_stream(call, type);
if (ms) update_local_stats(stats, ms);
return stats;
if (ms && stats) update_local_stats(stats, ms);
_linphone_call_stats_clone(stats_copy, stats);
return stats_copy;
}
ms_error("Invalid stream type %i", (int)type);
return NULL;
}
const LinphoneCallStats *linphone_call_get_audio_stats(LinphoneCall *call) {
LinphoneCallStats *linphone_call_get_audio_stats(LinphoneCall *call) {
return linphone_call_get_stats(call, LinphoneStreamTypeAudio);
}
const LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call) {
LinphoneCallStats *linphone_call_get_video_stats(LinphoneCall *call) {
return linphone_call_get_stats(call, LinphoneStreamTypeVideo);
}
const LinphoneCallStats *linphone_call_get_text_stats(LinphoneCall *call) {
LinphoneCallStats *linphone_call_get_text_stats(LinphoneCall *call) {
return linphone_call_get_stats(call, LinphoneStreamTypeText);
}
......@@ -4231,12 +4278,42 @@ static bool_t ice_in_progress(LinphoneCallStats *stats){
bool_t linphone_call_media_in_progress(LinphoneCall *call){
bool_t ret=FALSE;
if (ice_in_progress(&call->stats[LINPHONE_CALL_STATS_AUDIO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_VIDEO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_TEXT]))
if (ice_in_progress(call->audio_stats) || ice_in_progress(call->video_stats) || ice_in_progress(call->text_stats))
ret=TRUE;
/*TODO: could check zrtp state, upnp state*/
return ret;
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallStats);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallStats, belle_sip_object_t,
NULL, // destroy
_linphone_call_stats_clone, // clone
NULL, // marshal
FALSE
);
LinphoneCallStats *linphone_call_stats_new() {
LinphoneCallStats *stats = belle_sip_object_new(LinphoneCallStats);
return stats;
}
LinphoneCallStats* linphone_call_stats_ref(LinphoneCallStats* stats) {
return (LinphoneCallStats*) belle_sip_object_ref(stats);
}
void linphone_call_stats_unref(LinphoneCallStats* stats) {
belle_sip_object_unref(stats);
}
void *linphone_call_stats_get_user_data(const LinphoneCallStats *stats) {
return stats->user_data;
}
void linphone_call_stats_set_user_data(LinphoneCallStats *stats, void *data) {
stats->user_data = data;
}
LinphoneStreamType linphone_call_stats_get_type(const LinphoneCallStats *stats) {
return stats->type;
}
......@@ -4355,6 +4432,10 @@ float linphone_call_stats_get_jitter_buffer_size_ms(const LinphoneCallStats *sta
return stats->jitter_stats.jitter_buffer_size_ms;
}
float linphone_call_stats_get_round_trip_delay(const LinphoneCallStats *stats) {
return stats->round_trip_delay;
}
void linphone_call_start_recording(LinphoneCall *call){
if (!call->params->record_file){
ms_error("linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().");
......@@ -4375,7 +4456,16 @@ void linphone_call_stop_recording(LinphoneCall *call){
static void report_bandwidth_for_stream(LinphoneCall *call, MediaStream *ms, LinphoneStreamType type){
bool_t active = ms ? (media_stream_get_state(ms) == MSStreamStarted) : FALSE;
LinphoneCallStats *stats = &call->stats[type];
LinphoneCallStats *stats = NULL;
if (type == LinphoneStreamTypeAudio) {
stats = call->audio_stats;
} else if (type == LinphoneStreamTypeAudio) {
stats = call->video_stats;
} else if (type == LinphoneStreamTypeAudio) {
stats = call->text_stats;
} else {
return;
}
stats->download_bandwidth=(active) ? (float)(media_stream_get_down_bw(ms)*1e-3) : 0.f;
stats->upload_bandwidth=(active) ? (float)(media_stream_get_up_bw(ms)*1e-3) : 0.f;
......@@ -4401,18 +4491,18 @@ static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *v
"\tRTP audio=[d=%5.1f,u=%5.1f], video=[d=%5.1f,u=%5.1f], text=[d=%5.1f,u=%5.1f] kbits/sec\n"
"\tRTCP audio=[d=%5.1f,u=%5.1f], video=[d=%5.1f,u=%5.1f], text=[d=%5.1f,u=%5.1f] kbits/sec",
call,
call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth,
call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth,
call->stats[LINPHONE_CALL_STATS_TEXT].download_bandwidth,
call->stats[LINPHONE_CALL_STATS_TEXT].upload_bandwidth,
call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_download_bandwidth,
call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_upload_bandwidth,
call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_download_bandwidth,
call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_upload_bandwidth,
call->stats[LINPHONE_CALL_STATS_TEXT].rtcp_download_bandwidth,
call->stats[LINPHONE_CALL_STATS_TEXT].rtcp_upload_bandwidth
call->audio_stats->download_bandwidth,
call->audio_stats->upload_bandwidth,
call->video_stats->download_bandwidth,
call->video_stats->upload_bandwidth,
call->text_stats->download_bandwidth,
call->text_stats->upload_bandwidth,
call->audio_stats->rtcp_download_bandwidth,
call->audio_stats->rtcp_upload_bandwidth,
call->video_stats->rtcp_download_bandwidth,
call->video_stats->rtcp_upload_bandwidth,
call->text_stats->rtcp_download_bandwidth,
call->text_stats->rtcp_upload_bandwidth
);
}
......@@ -4545,7 +4635,14 @@ void linphone_call_stats_uninit(LinphoneCallStats *stats){
}
void linphone_call_notify_stats_updated_with_stream_index(LinphoneCall *call, int stream_index){
LinphoneCallStats *stats = &call->stats[stream_index];
LinphoneCallStats *stats = NULL;
if (stream_index == call->main_audio_stream_index) {
stats = call->audio_stats;
} else if (stream_index == call->main_video_stream_index) {
stats = call->video_stats;
} else {
stats = call->text_stats;
}
if (stats->updated){
switch(stats->updated) {
case LINPHONE_CALL_STATS_RECEIVED_RTCP_UPDATE:
......@@ -4615,13 +4712,25 @@ void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){
while((evq = linphone_call_get_event_queue(call, stream_index)) != NULL && NULL != (ev=ortp_ev_queue_get(evq))){
OrtpEventType evt=ortp_event_get_type(ev);
OrtpEventData *evd=ortp_event_get_data(ev);
int stats_index = stream_index == call->main_audio_stream_index ? LINPHONE_CALL_STATS_AUDIO : (stream_index == call->main_video_stream_index ? LINPHONE_CALL_STATS_VIDEO : LINPHONE_CALL_STATS_TEXT);
int stats_index;
LinphoneCallStats *stats = NULL;
if (stream_index == call->main_audio_stream_index) {
stats_index = LINPHONE_CALL_STATS_AUDIO;
stats = call->audio_stats;
} else if (stream_index == call->main_video_stream_index) {
stats_index = LINPHONE_CALL_STATS_VIDEO;
stats = call->video_stats;
} else {
stats_index = LINPHONE_CALL_STATS_TEXT;
stats = call->text_stats;
}
/*and yes the MediaStream must be taken at each iteration, because it may have changed due to the handling of events
* in this loop*/
ms = linphone_call_get_media_stream(call, stream_index);
if (ms) linphone_call_stats_fill(&call->stats[stats_index],ms,ev);
if (ms) linphone_call_stats_fill(stats,ms,ev);
linphone_call_notify_stats_updated_with_stream_index(call,stats_index);
if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
......
......@@ -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-");
}