qualityindicator.c 4.54 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 28 29 30 31 32 33
#define RATING_SCALE 5.0
#define WORSE_JITTER 0.2
#define WORSE_RT_PROP 5.0



struct _MSQualityIndicator{
	RtpSession *session;
	int clockrate;
34
	double sum_ratings;
35 36 37 38 39 40
	float rating;
	float local_rating;
	float remote_rating;
	uint64_t last_packet_count;
	uint32_t last_lost;
	uint32_t last_late;
41
	int count;
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
};

MSQualityIndicator *ms_quality_indicator_new(RtpSession *session){
	MSQualityIndicator *qi=ms_new0(MSQualityIndicator,1);
	qi->session=session;
	qi->rating=5.0;
	qi->local_rating=1.0;
	qi->remote_rating=1.0;
	return qi;
}

float ms_quality_indicator_get_rating(MSQualityIndicator *qi){
	return qi->rating;
}

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);
}

69 70 71 72 73 74 75 76 77 78 79
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);
}

80
static float compute_rating(float loss_rate, float inter_jitter, float late_rate, float rt_prop){
81
	return loss_rating(loss_rate)*inter_jitter_rating(inter_jitter)*loss_rating(late_rate)*rt_prop_rating(rt_prop);
82 83 84 85
}

static void update_global_rating(MSQualityIndicator *qi){
	qi->rating=RATING_SCALE*qi->remote_rating*qi->local_rating;
86 87
	qi->sum_ratings+=qi->rating;
	qi->count++;
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}

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{
		ms_warning("ms_quality_indicator_update_from_feedback(): not a RTCP report");
	}
	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){
		float loss_rate=(float)report_block_get_fraction_lost(rb)/256.0;
		float inter_jitter=(float)report_block_get_interarrival_jitter(rb)/(float)qi->clockrate;
		float rt_prop=rtp_session_get_round_trip_propagation(qi->session);
		qi->remote_rating=compute_rating(loss_rate,inter_jitter,0,rt_prop);
		update_global_rating(qi);
	}
}

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;

	recvcnt=stats->packet_recv-qi->last_packet_count;
	if (recvcnt==0){
		ms_message("ms_quality_indicator_update_local(): no packet received since last call");
		return;/* no information usable*/
	}else if (recvcnt<0){
		qi->last_packet_count=stats->packet_recv;
		recvcnt=0; /*should not happen obviously*/
		return;
	}
	
	lost=stats->cum_packet_loss-qi->last_lost;
	qi->last_lost=stats->cum_packet_loss;
	late=stats->outoftime-qi->last_late;
	qi->last_late=stats->outoftime;
	qi->last_packet_count=stats->packet_recv;

	if (lost<0) lost=0;
	if (late<0) late=0;

	loss_rate=(float)lost/(float)recvcnt;
	late_rate=(float)late/(float)recvcnt;
	
	qi->local_rating=compute_rating(loss_rate,0,late_rate,rtp_session_get_round_trip_propagation(qi->session));
	update_global_rating(qi);
}

144
float ms_quality_indicator_get_average_rating(MSQualityIndicator *qi){
145
	if (qi->count==0) return -1; /*no rating available*/
146 147 148
	return (float)(qi->sum_ratings/(double)qi->count);
}

149 150 151 152
void ms_quality_indicator_destroy(MSQualityIndicator *qi){
	ms_free(qi);
}

153