qualityindicator.c 7.03 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
mediastreamer2 library - modular sound and video processing and streaming

 * Copyright (C) 2011  Belledonne Communications, Grenoble, France

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/qualityindicator.h"

23 24
#include <math.h>

25 26 27
#define RATING_SCALE 5.0f
#define WORSE_JITTER 0.2f
#define WORSE_RT_PROP 5.0f
28
#define SEQ_INTERVAL 60
Simon Morlat's avatar
Simon Morlat committed
29
#define TIME_INTERVAL 3000
30 31 32 33


struct _MSQualityIndicator{
	RtpSession *session;
34 35
	char *label;
	OrtpLossRateEstimator *lr_estimator;
36
	int clockrate;
37
	double sum_ratings;
38
	double sum_lq_ratings;
39
	float rating;
40
	float lq_rating;	/* Listening-quality rating */
41 42
	float local_rating;
	float remote_rating;
43 44
	float local_lq_rating;	/* Local listening-quality rating */
	float remote_lq_rating;	/* Remote listening-quality rating */
45
	uint64_t last_packet_count;
46
	uint32_t last_ext_seq;
47
	uint32_t last_late;
48
	int count;
49 50
	float cur_late_rate;
	float cur_loss_rate;
51 52 53 54 55
};

MSQualityIndicator *ms_quality_indicator_new(RtpSession *session){
	MSQualityIndicator *qi=ms_new0(MSQualityIndicator,1);
	qi->session=session;
Simon Morlat's avatar
Simon Morlat committed
56
	qi->lr_estimator=ortp_loss_rate_estimator_new(SEQ_INTERVAL, TIME_INTERVAL, qi->session);
57
	qi->rating=5.0;
58
	qi->lq_rating=5.0;
59 60
	qi->local_rating=1.0;
	qi->remote_rating=1.0;
61 62
	qi->local_lq_rating=1.0;
	qi->remote_lq_rating=1.0;
63 64 65
	return qi;
}

66 67 68 69 70 71 72 73
void ms_quality_indicator_set_label(MSQualityIndicator *obj, const char *label){
	if (obj->label){
		ms_free(obj->label);
		obj->label=NULL;
	}
	if (label) obj->label=ms_strdup(label);
}

74
float ms_quality_indicator_get_rating(const MSQualityIndicator *qi){
75 76 77
	return qi->rating;
}

78
float ms_quality_indicator_get_lq_rating(const MSQualityIndicator *qi) {
79 80 81
	return qi->lq_rating;
}

82 83 84
static float inter_jitter_rating(float inter_jitter){
	float tmp=inter_jitter/WORSE_JITTER;
	if (tmp>1) tmp=1;
85
	return 1.0f-(0.3f*tmp);
86 87 88 89 90
}

static float rt_prop_rating(float rt_prop){
	float tmp=rt_prop/WORSE_RT_PROP;
	if (tmp>1) tmp=1;
91
	return 1.0f-(0.7f*tmp);
92 93
}

94 95 96 97 98 99 100 101
static float loss_rating(float loss){
	/* the exp function allows to have a rating that decrease rapidly at the begining.
	 Indeed even with 10% loss, the quality is significantly affected. It is not good at all.
	 With this formula:
	 5% losses gives a rating of 4/5
	 20% losses gives a rating of 2.2/5
	 80% losses gives a rating of 0.2
	*/
102
	return expf(-loss*4.0f);
103 104
}

105
static float compute_rating(float loss_rate, float inter_jitter, float late_rate, float rt_prop){
106
	return loss_rating(loss_rate)*inter_jitter_rating(inter_jitter)*loss_rating(late_rate)*rt_prop_rating(rt_prop);
107 108
}

109 110 111 112 113
/* Compute listening-quality rating */
static float compute_lq_rating(float loss_rate, float inter_jitter, float late_rate) {
	return loss_rating(loss_rate) * inter_jitter_rating(inter_jitter) * loss_rating(late_rate);
}

114 115
static void update_global_rating(MSQualityIndicator *qi){
	qi->rating=RATING_SCALE*qi->remote_rating*qi->local_rating;
116
	qi->sum_ratings+=qi->rating;
117 118
	qi->lq_rating=RATING_SCALE*qi->remote_lq_rating*qi->local_lq_rating;
	qi->sum_lq_ratings+=qi->lq_rating;
119
	qi->count++;
120 121 122 123 124 125 126 127 128
}

void ms_quality_indicator_update_from_feedback(MSQualityIndicator *qi, mblk_t *rtcp){
	const report_block_t *rb=NULL;
	if (rtcp_is_SR(rtcp)){
		rb=rtcp_SR_get_report_block(rtcp,0);
	}else if (rtcp_is_RR(rtcp)){
		rb=rtcp_RR_get_report_block(rtcp,0);
	}else{
129
		return;
130 131 132 133 134 135 136
	}
	if (qi->clockrate==0){
		PayloadType *pt=rtp_profile_get_payload(rtp_session_get_send_profile(qi->session),rtp_session_get_send_payload_type(qi->session));
		if (pt!=NULL) qi->clockrate=pt->clock_rate;
		else return;
	}
	if (rb){
137
		float loss_rate;
138 139
		float inter_jitter=(float)report_block_get_interarrival_jitter(rb)/(float)qi->clockrate;
		float rt_prop=rtp_session_get_round_trip_propagation(qi->session);
140
		bool_t new_value;
141

142
		new_value=ortp_loss_rate_estimator_process_report_block(qi->lr_estimator,&qi->session->rtp,rb);
143
		loss_rate=ortp_loss_rate_estimator_get_value(qi->lr_estimator);
144 145
		qi->remote_rating=compute_rating(loss_rate/100.0f,inter_jitter,0,rt_prop);
		qi->remote_lq_rating=compute_lq_rating(loss_rate/100.0f,inter_jitter,0);
146
		update_global_rating(qi);
147
		if (new_value){
148 149
			ms_message("MSQualityIndicator[%p][%s], remote statistics available:"
						"\n\t%-20s: %3.1f%%"
150 151
						"\n\t%-20s: %3.1fms"
						"\n\t%-20s: %3.1fms"
152
						,qi,qi->label ? qi->label : "no label"
153
						,"Loss rate", loss_rate
154 155
						,"Inter-arrival jitter",100*inter_jitter
						,"RT propagation",100*rt_prop);
156
		}
157 158 159 160 161 162 163
	}
}

void ms_quality_indicator_update_local(MSQualityIndicator *qi){
	const rtp_stats_t *stats=rtp_session_get_stats(qi->session);
	int lost,late,recvcnt;
	float loss_rate=0,late_rate=0;
164
	uint32_t ext_seq=rtp_session_get_rcv_ext_seq_number(qi->session);
165

166
	recvcnt=(int)(stats->packet_recv-qi->last_packet_count);
167
	if (recvcnt==0){
168
		// ms_message("ms_quality_indicator_update_local(): no packet received since last call");
169 170 171
		return;/* no information usable*/
	}else if (recvcnt<0){
		qi->last_packet_count=stats->packet_recv;
172
		qi->last_ext_seq=ext_seq;
173 174
		recvcnt=0; /*should not happen obviously*/
		return;
175 176
	}else if (qi->last_packet_count==0){
		qi->last_ext_seq=ext_seq;
177
	}
178

179 180 181
	lost=(ext_seq-qi->last_ext_seq) - (recvcnt);
	qi->last_ext_seq=ext_seq;
	qi->last_packet_count=stats->packet_recv;
182

183 184
	late=(int)(stats->outoftime-qi->last_late);
	qi->last_late=(uint32_t)stats->outoftime;
185

186

187
	if (lost<0) lost=0; /* will be the case at least the first time, because we don't know the initial sequence number*/
188 189
	if (late<0) late=0;

190
	loss_rate=(float)lost/(float)recvcnt;
191
	qi->cur_loss_rate=loss_rate*100.0f;
192

193
	late_rate=(float)late/(float)recvcnt;
194
	qi->cur_late_rate=late_rate*100.0f;
195

196
	qi->local_rating=compute_rating(loss_rate,0,late_rate,rtp_session_get_round_trip_propagation(qi->session));
197
	qi->local_lq_rating=compute_lq_rating(loss_rate,0,late_rate);
198 199 200
	update_global_rating(qi);
}

201
float ms_quality_indicator_get_average_rating(const MSQualityIndicator *qi){
202
	if (qi->count==0) return -1; /*no rating available*/
203 204 205
	return (float)(qi->sum_ratings/(double)qi->count);
}

206
float ms_quality_indicator_get_average_lq_rating(const MSQualityIndicator *qi) {
207 208 209 210
	if (qi->count == 0) return -1; /* No rating available */
	return (float)(qi->sum_lq_ratings / (double)qi->count);
}

211 212 213 214 215 216 217 218
float ms_quality_indicator_get_local_loss_rate(const MSQualityIndicator *qi){
	return qi->cur_loss_rate;
}

float ms_quality_indicator_get_local_late_rate(const MSQualityIndicator *qi){
	return qi->cur_late_rate;
}

219
void ms_quality_indicator_destroy(MSQualityIndicator *qi){
220
	ortp_loss_rate_estimator_destroy(qi->lr_estimator);
Simon Morlat's avatar
Simon Morlat committed
221
	if (qi->label) ms_free(qi->label);
222 223 224
	ms_free(qi);
}

225