qualityindicator.c 6.9 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.0
#define WORSE_JITTER 0.2
#define WORSE_RT_PROP 5.0
28
#define SEQ_INTERVAL 120
29 30 31 32


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

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

65 66 67 68 69 70 71 72
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);
}

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

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

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

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

93 94 95 96 97 98 99 100 101 102 103
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
	*/
	return expf(-loss*4.0);
}

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

108 109 110 111 112
/* 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);
}

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

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{
128
		return;
129 130 131 132 133 134 135
	}
	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){
136
		float loss_rate;
137 138
		float inter_jitter=(float)report_block_get_interarrival_jitter(rb)/(float)qi->clockrate;
		float rt_prop=rtp_session_get_round_trip_propagation(qi->session);
139
		bool_t new_value;
140

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

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;
159
	uint32_t ext_seq=rtp_session_get_rcv_ext_seq_number(qi->session);
160 161 162

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

174 175 176
	lost=(ext_seq-qi->last_ext_seq) - (recvcnt);
	qi->last_ext_seq=ext_seq;
	qi->last_packet_count=stats->packet_recv;
177

178 179
	late=stats->outoftime-qi->last_late;
	qi->last_late=stats->outoftime;
180

181

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

185 186
	loss_rate=(float)lost/(float)recvcnt;
	qi->cur_loss_rate=loss_rate*100.0;
187

188 189
	late_rate=(float)late/(float)recvcnt;
	qi->cur_late_rate=late_rate*100.0;
190

191
	qi->local_rating=compute_rating(loss_rate,0,late_rate,rtp_session_get_round_trip_propagation(qi->session));
192
	qi->local_lq_rating=compute_lq_rating(loss_rate,0,late_rate);
193 194 195
	update_global_rating(qi);
}

196
float ms_quality_indicator_get_average_rating(const MSQualityIndicator *qi){
197
	if (qi->count==0) return -1; /*no rating available*/
198 199 200
	return (float)(qi->sum_ratings/(double)qi->count);
}

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

206 207 208 209 210 211 212 213
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;
}

214
void ms_quality_indicator_destroy(MSQualityIndicator *qi){
215
	ortp_loss_rate_estimator_destroy(qi->lr_estimator);
Simon Morlat's avatar
Simon Morlat committed
216
	if (qi->label) ms_free(qi->label);
217 218 219
	ms_free(qi);
}

220