qosanalyzer.c 20.4 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 23
/*
mediastreamer2 library - modular sound and video processing and streaming

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

	 Author: Simon Morlat <simon.morlat@linphone.org>

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/bitratecontrol.h"
24
#include "qosanalyzer.h"
25

26
#include <math.h>
27

28 29
#define LOSS_RATE_MIN_INTERVAL 120

30 31 32
/**
 * Analyses a received RTCP packet.
 * Returns TRUE is relevant information has been found in the rtcp message, FALSE otherwise.
33
**/
34
bool_t ms_qos_analyzer_process_rtcp(MSQosAnalyzer *obj,mblk_t *msg){
35 36 37 38 39 40 41
	if (obj->desc->process_rtcp){
		return obj->desc->process_rtcp(obj,msg);
	}
	ms_error("Unimplemented process_rtcp() call.");
	return FALSE;
}

42
void ms_qos_analyzer_suggest_action(MSQosAnalyzer *obj, MSRateControlAction *action){
43 44 45 46 47
	if (obj->desc->suggest_action){
		obj->desc->suggest_action(obj,action);
	}
}

48
void ms_qos_analyzer_update(MSQosAnalyzer *obj){
49 50 51
	if (obj->desc->update){
		obj->desc->update(obj);
	}
52 53
}

54
bool_t ms_qos_analyzer_has_improved(MSQosAnalyzer *obj){
55
	if (obj->desc->has_improved){
56
		return obj->desc->has_improved(obj);
57 58 59 60 61
	}
	ms_error("Unimplemented has_improved() call.");
	return TRUE;
}

62
void ms_qos_analyzer_set_on_action_suggested(MSQosAnalyzer *obj,
63 64 65 66 67
	void (*on_action_suggested)(void*, const char*,const char*), void* u){
	obj->on_action_suggested=on_action_suggested;
	obj->on_action_suggested_user_pointer=u;
}

68 69 70 71 72 73 74 75
void ms_qos_analyser_set_label(MSQosAnalyzer *obj, const char *label){
	if (obj->label){
		ms_free(obj->label);
		obj->label=NULL;
	}
	if (label) obj->label=ms_strdup(label);
}

76
MSQosAnalyzer *ms_qos_analyzer_ref(MSQosAnalyzer *obj){
77 78 79 80
	obj->refcnt++;
	return obj;
}

81
void ms_qos_analyzer_unref(MSQosAnalyzer *obj){
82 83 84 85
	obj->refcnt--;
	if (obj->refcnt<=0){
		if (obj->desc->uninit)
			obj->desc->uninit(obj);
86
		if (obj->label) ms_free(obj->label);
87 88
		ms_free(obj);
	}
89 90
}

91
const char *ms_rate_control_action_type_name(MSRateControlActionType t){
92 93 94 95 96 97 98 99 100 101 102 103 104
	switch(t){
		case MSRateControlActionDoNothing:
			return "DoNothing";
		case MSRateControlActionIncreaseQuality:
			return "IncreaseQuality";
		case MSRateControlActionDecreaseBitrate:
			return "DecreaseBitrate";
		case MSRateControlActionDecreasePacketRate:
			return "DecreasePacketRate";
	}
	return "bad action type";
}

105
/******************************************************************************/
106
/***************************** Simple QoS analyzer ****************************/
107
/******************************************************************************/
108 109
static bool_t rt_prop_doubled(rtpstats_t *cur,rtpstats_t *prev){
	//ms_message("AudioBitrateController: cur=%f, prev=%f",cur->rt_prop,prev->rt_prop);
110
	if (cur->rt_prop>=significant_delay && prev->rt_prop>0){
111 112 113 114 115 116 117 118
		if (cur->rt_prop>=(prev->rt_prop*2.0)){
			/*propagation doubled since last report */
			return TRUE;
		}
	}
	return FALSE;
}

119
static bool_t simple_rt_prop_increased(MSSimpleQosAnalyzer *obj){
120 121 122 123 124 125 126 127 128 129
	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
	rtpstats_t *prev=&obj->stats[(STATS_HISTORY+obj->curindex-1) % STATS_HISTORY];

	if (rt_prop_doubled(cur,prev)){
		obj->rt_prop_doubled=TRUE;
		return TRUE;
	}
	return FALSE;
}

130 131
static bool_t simple_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
132 133 134 135 136 137 138
	rtpstats_t *cur;
	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);
	}
139
	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
140

141 142
		obj->curindex++;
		cur=&obj->stats[obj->curindex % STATS_HISTORY];
143

144 145 146 147 148
		if (obj->clockrate==0){
			PayloadType *pt=rtp_profile_get_payload(rtp_session_get_send_profile(obj->session),rtp_session_get_send_payload_type(obj->session));
			if (pt!=NULL) obj->clockrate=pt->clock_rate;
			else return FALSE;
		}
149 150 151 152 153 154 155 156 157
		if (ortp_loss_rate_estimator_process_report_block(obj->lre,rb)){
			cur->lost_percentage=ortp_loss_rate_estimator_get_value(obj->lre);
			cur->int_jitter=1000.0*(float)report_block_get_interarrival_jitter(rb)/(float)obj->clockrate;
			cur->rt_prop=rtp_session_get_round_trip_propagation(obj->session);
			/*
			ms_message("MSQosAnalyzer[%s]: lost_percentage=%f, int_jitter=%f ms, rt_prop=%f sec",
				objbase->label ? objbase->label : "", cur->lost_percentage,cur->int_jitter,cur->rt_prop);
			*/
		}
158 159 160 161
	}
	return rb!=NULL;
}

162 163
static void simple_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
164
	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
165

166
	/*big losses and big jitter */
167
	if (cur->lost_percentage>=unacceptable_loss_rate && cur->int_jitter>=big_jitter){
168 169
		action->type=MSRateControlActionDecreaseBitrate;
		action->value=MIN(cur->lost_percentage,50);
170
		ms_message("MSQosAnalyzer: loss rate unacceptable and big jitter");
171
	}else if (simple_rt_prop_increased(obj)){
172 173
		action->type=MSRateControlActionDecreaseBitrate;
		action->value=20;
174
		ms_message("MSQosAnalyzer: rt_prop doubled.");
175 176 177
	}else if (cur->lost_percentage>=unacceptable_loss_rate){
		/*big loss rate but no jitter, and no big rtp_prop: pure lossy network*/
		action->type=MSRateControlActionDecreasePacketRate;
178
		ms_message("MSQosAnalyzer: loss rate unacceptable.");
179 180
	}else{
		action->type=MSRateControlActionDoNothing;
181
		ms_message("MSQosAnalyzer: everything is fine.");
182
	}
183 184 185 186

	if (objbase->on_action_suggested!=NULL){
		char *input=ms_strdup_printf("lost_percentage=%d rt_prop_increased=%d int_jitter_ms=%d rt_prop_ms=%d"
			, (int)cur->lost_percentage
187
			, (simple_rt_prop_increased(obj)==TRUE)
188 189 190 191 192 193 194 195 196 197 198
			, (int)cur->int_jitter
			, (int)(1000*cur->rt_prop));
		char *output=ms_strdup_printf("action_type=%s action_value=%d"
			, ms_rate_control_action_type_name(action->type)
			, action->value);

		objbase->on_action_suggested(objbase->on_action_suggested_user_pointer, input, output);

		ms_free(input);
		ms_free(output);
	}
199 200
}

201 202
static bool_t simple_analyzer_has_improved(MSQosAnalyzer *objbase){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
203 204 205 206 207
	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
	rtpstats_t *prev=&obj->stats[(STATS_HISTORY+obj->curindex-1) % STATS_HISTORY];

	if (prev->lost_percentage>=unacceptable_loss_rate){
		if (cur->lost_percentage<prev->lost_percentage){
208
			ms_message("MSQosAnalyzer: lost percentage has improved");
209 210 211 212
			return TRUE;
		}else goto end;
	}
	if (obj->rt_prop_doubled && cur->rt_prop<prev->rt_prop){
213
		ms_message("MSQosAnalyzer: rt prop decreased");
214 215 216 217
		obj->rt_prop_doubled=FALSE;
		return TRUE;
	}

218
	end:
219
	ms_message("MSQosAnalyzer: no improvements.");
220 221 222
	return FALSE;
}

223 224 225 226 227
static void simple_analyzer_uninit(MSQosAnalyzer *objbase){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
	ortp_loss_rate_estimator_destroy(obj->lre);
}

228 229 230
static MSQosAnalyzerDesc simple_analyzer_desc={
	simple_analyzer_process_rtcp,
	simple_analyzer_suggest_action,
231 232 233
	simple_analyzer_has_improved,
	NULL,
	simple_analyzer_uninit
234 235
};

236 237
MSQosAnalyzer * ms_simple_qos_analyzer_new(RtpSession *session){
	MSSimpleQosAnalyzer *obj=ms_new0(MSSimpleQosAnalyzer,1);
238
	obj->session=session;
239
	obj->parent.desc=&simple_analyzer_desc;
240
	obj->parent.type=Simple;
241
	obj->lre=ortp_loss_rate_estimator_new(LOSS_RATE_MIN_INTERVAL, rtp_session_get_seq_number(session));
242
	return (MSQosAnalyzer*)obj;
243 244 245 246 247 248
}




/******************************************************************************/
249
/***************************** Stateful QoS analyzer ****************************/
250
/******************************************************************************/
251 252 253 254 255 256 257 258 259 260 261
#define RED 		""
#define YELLOW 		""
#define GREEN 		""
#define RESET 		""
#define VA_ARGS(...) , ##__VA_ARGS__
#if 1
#define P(c, ...) 	printf(GREEN c RESET VA_ARGS(__VA_ARGS__))
#else
#define P(c, ...) ms_message(c VA_ARGS(__VA_ARGS__))
#endif

262
const char *ms_qos_analyzer_network_state_name(MSQosAnalyzerNetworkState state){
263
	switch(state){
264
		case MSQosAnalyzerNetworkFine:
265
			return "fine";
266
		case MSQosAnalyzerNetworkUnstable:
267
			return "unstable";
268
		case MSQosAnalyzerNetworkCongested:
269
			return "congested";
270
		case MSQosAnalyzerNetworkLossy:
271 272 273 274 275
			return "lossy";
	}
	return "bad state type";
}

276 277 278 279 280 281
static int earlier_than(rtcpstatspoint_t *p, const time_t * now){
	if (p->timestamp < *now){
		ms_free(p);
		return FALSE;
	}
	return TRUE;
282
}
283 284 285 286
static int sort_points(const rtcpstatspoint_t *p1, const rtcpstatspoint_t *p2){
	return p1->bandwidth > p2->bandwidth;
}

287
static int stateful_qos_analyzer_get_total_emitted(const MSStatefulQosAnalyzer *obj, const report_block_t *rb){
288 289 290 291 292 293 294 295
	double dup = obj->burst_ratio;
	int burst_within_start = MAX(obj->previous_ext_high_seq_num_rec, obj->start_seq_number);
	int burst_within_end = MIN(report_block_get_high_ext_seq(rb), obj->last_seq_number);
	int uniq_emitted=report_block_get_high_ext_seq(rb) - obj->previous_ext_high_seq_num_rec;

	return uniq_emitted + MAX(0,burst_within_end - burst_within_start) * dup;
}

296
static double stateful_qos_analyzer_upload_bandwidth(MSStatefulQosAnalyzer *obj){
297 298
	double up_bw=rtp_session_get_send_bandwidth(obj->session)/1000.0;

299 300 301 302 303
	if (obj->upload_bandwidth_count){
		obj->upload_bandwidth_latest=obj->upload_bandwidth_sum/obj->upload_bandwidth_count;
	}

	obj->upload_bandwidth_count=0;
304 305
	obj->upload_bandwidth_sum=0;

306
	P(GREEN "latest_up_bw=%f vs sum_up_bw=%f\n", up_bw, obj->upload_bandwidth_latest);
307
	return obj->upload_bandwidth_latest;
308 309
}

310 311
static bool_t stateful_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
312 313 314 315 316 317 318 319
	rtpstats_t *cur;
	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);
	}
	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
320 321
		double up_bw = stateful_qos_analyzer_upload_bandwidth(obj);
		int total_emitted=stateful_qos_analyzer_get_total_emitted(obj, rb);
322 323 324 325 326 327 328 329 330 331 332 333
		obj->curindex++;
		cur=&obj->stats[obj->curindex % STATS_HISTORY];

		if (obj->clockrate==0){
			PayloadType *pt=rtp_profile_get_payload(rtp_session_get_send_profile(obj->session),rtp_session_get_send_payload_type(obj->session));
			if (pt!=NULL) obj->clockrate=pt->clock_rate;
			else return FALSE;
		}

		cur->lost_percentage=100.0*(float)report_block_get_fraction_lost(rb)/256.0;
		cur->int_jitter=1000.0*(float)report_block_get_interarrival_jitter(rb)/(float)obj->clockrate;
		cur->rt_prop=rtp_session_get_round_trip_propagation(obj->session);
334
		ms_message("MSQosAnalyzer: lost_percentage=%f, int_jitter=%f ms, rt_prop=%f sec",cur->lost_percentage,cur->int_jitter,cur->rt_prop);
335
		if (obj->curindex>2){
336 337
			double loss_rate = cur->lost_percentage/100.0;
			int cum_loss=report_block_get_cum_packet_loss(rb);
338
			int cum_loss_curr=cum_loss - obj->cum_loss_prev;
339
			int uniq_emitted=report_block_get_high_ext_seq(rb) - obj->previous_ext_high_seq_num_rec;
340

341
			if (obj->previous_ext_high_seq_num_rec > 0){
342
				loss_rate=(1. - (uniq_emitted - cum_loss_curr) * 1.f / total_emitted);
343
				P("RECEIVE estimated loss rate=%f vs 'real'=%f\n", loss_rate, report_block_get_fraction_lost(rb)/256.);
344 345
			}

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
			obj->latest=ms_new0(rtcpstatspoint_t, 1);
			obj->latest->timestamp=ms_time(0);
			obj->latest->bandwidth=up_bw;
			obj->latest->loss_percent=MAX(0,loss_rate);
			obj->latest->rtt=cur->rt_prop;

			obj->rtcpstatspoint=ms_list_insert_sorted(obj->rtcpstatspoint, obj->latest, (MSCompareFunc)sort_points);

			if (obj->latest->loss_percent < 1e-5){
				MSList *it=obj->rtcpstatspoint;
				MSList *latest_pos=ms_list_find(obj->rtcpstatspoint,obj->latest);
				while (it!=latest_pos->next){
					((rtcpstatspoint_t *)it->data)->loss_percent=0.f;
					it = it->next;
				}
361
			}
362
			P(YELLOW "one more %d: %f %f\n", obj->curindex-2, obj->latest->bandwidth, obj->latest->loss_percent);
363 364 365

			if (ms_list_size(obj->rtcpstatspoint) > ESTIM_HISTORY){
				P(RED "Reached list maximum capacity (count=%d)", ms_list_size(obj->rtcpstatspoint));
366
				/*clean everything which occurred 60 sec or more ago*/
367
				time_t clear_time = ms_time(0) - 60;
368
				obj->rtcpstatspoint = ms_list_remove_custom(obj->rtcpstatspoint, (MSCompareFunc)earlier_than, &clear_time);
369 370
				P(RED "--> Cleaned list (count=%d)\n", ms_list_size(obj->rtcpstatspoint));
			}
371
		}
372 373
		obj->cum_loss_prev=report_block_get_cum_packet_loss(rb);
		obj->previous_ext_high_seq_num_rec=report_block_get_high_ext_seq(rb);
374 375 376 377
	}
	return rb!=NULL;
}

378 379 380 381
static float lerp(float inf, float sup, float v){
	return inf + (sup - inf) * v;
}

382 383 384 385 386 387 388 389 390
static MSList *find_first_with_loss(MSList *list){
	for(;list!=NULL;list=list->next){
		if (((rtcpstatspoint_t *)list->data)->loss_percent > 1e-5){
			return list;
		}
	}
	return NULL;
}

391
static void smooth_values(MSStatefulQosAnalyzer *obj){
392
	MSList *first_loss = find_first_with_loss(obj->rtcpstatspoint);
393 394
	MSList *it = obj->rtcpstatspoint;
	rtcpstatspoint_t *curr = (rtcpstatspoint_t *)it->data;
395 396 397 398 399 400 401 402 403
	double prev_loss = 0.;

	if (first_loss == obj->rtcpstatspoint){
		prev_loss = curr->loss_percent;
		curr->loss_percent = lerp(curr->loss_percent, ((rtcpstatspoint_t *)it->next->data)->loss_percent, .25);
		it = it->next;
	}else{
		it = first_loss;
	}
404 405 406 407 408 409

	/*nothing to smooth*/
	if (it == NULL){
		return;
	}

410
	curr = (rtcpstatspoint_t *)it->data;
411

412 413 414 415 416 417 418 419 420
	while (it->next != NULL){
		rtcpstatspoint_t *prev = ((rtcpstatspoint_t *)it->prev->data);
		rtcpstatspoint_t *next = ((rtcpstatspoint_t *)it->next->data);

		float v = (curr->bandwidth - prev->bandwidth) / (next->bandwidth - prev->bandwidth);
		float new_loss = lerp(prev_loss, next->loss_percent, v);
		prev_loss = curr->loss_percent;
		curr->loss_percent = (curr->loss_percent + new_loss) / 2.;
		it = it->next;
421
		curr = (rtcpstatspoint_t *)it->data;
422
	}
423
	curr->loss_percent = lerp(prev_loss, curr->loss_percent, .75);
424
}
425

426
static float compute_available_bw(MSStatefulQosAnalyzer *obj){
427
	MSList *it;
428
	double constant_network_loss = 0.;
429
	double mean_bw = 0.;
430 431 432 433
	MSList *current = obj->rtcpstatspoint;
	MSList *last = current;
	int size = ms_list_size(obj->rtcpstatspoint);
	if (current == NULL){
434
		P(RED "Not points available for computation.\n");
435
		return -1;
436 437
	}

438 439 440
	while (last->next){
		last = last->next;
	}
441

442 443
	if (size > 3){
		smooth_values(obj);
444
	}
445
	/*suppose that first point is a reliable estimation of the constant network loss rate*/
446
	constant_network_loss = ((rtcpstatspoint_t *)obj->rtcpstatspoint->data)->loss_percent;
447

448

449
	P("\tconstant_network_loss=%f\n", constant_network_loss);
450 451
	for (it = obj->rtcpstatspoint; it != NULL; it=it->next){
		rtcpstatspoint_t * point = (rtcpstatspoint_t *)it->data;
452
		P(YELLOW "\t\tsorted values %d: %f %f\n",
453
			ms_list_position(obj->rtcpstatspoint, it), point->bandwidth, point->loss_percent);
454 455
	}

456 457 458 459 460 461 462 463 464 465 466 467 468 469
	if (size == 1){
		P(RED "One single point");
		rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->data;
		mean_bw = p->bandwidth * ((p->loss_percent>1e-5) ? (1-p->loss_percent):2);
	}else{
		while (current!=NULL && ((rtcpstatspoint_t*)current->data)->loss_percent<0.03+constant_network_loss){
			P("\t%d is stable\n", ms_list_position(obj->rtcpstatspoint, current));

			for (it=last;it!=current;it=it->prev){
				if (((rtcpstatspoint_t *)it->data)->loss_percent <= 0.03 + ((rtcpstatspoint_t*)current->data)->loss_percent){
					P("\t%d is less than %d\n", ms_list_position(obj->rtcpstatspoint, it), ms_list_position(obj->rtcpstatspoint, current));
					current = it;
					break;
				}
470 471
			}

472 473
			current = current->next;
		}
474

475 476 477 478 479 480 481 482 483 484 485
		if (current == NULL){
			/*constant loss rate - bad network conditions but no congestion*/
			mean_bw = 2 * ((rtcpstatspoint_t*)last->data)->bandwidth;
		}else if (current->prev == obj->rtcpstatspoint){
			/*only first packet is stable - might still be above real bandwidth*/
			rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->prev->data;
			mean_bw = p->bandwidth * (1 - p->loss_percent);
		}else{
			/*there is some congestion*/
			mean_bw = .5*(((rtcpstatspoint_t*)current->prev->data)->bandwidth+((rtcpstatspoint_t*)current->data)->bandwidth);
		}
486

487 488 489 490 491 492 493 494 495 496 497
		P(RED "[0->%d] Last stable is %d(%f;%f)"
			, ms_list_position(obj->rtcpstatspoint, last)
			, ms_list_position(obj->rtcpstatspoint, (current ? current->prev : last))
			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->bandwidth
			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->loss_percent);
		if (current!=NULL){
			P(RED ", first unstable is %d(%f;%f)"
				, ms_list_position(obj->rtcpstatspoint, current)
				, ((rtcpstatspoint_t*) current->data)->bandwidth
				, ((rtcpstatspoint_t*) current->data)->loss_percent);
		}
498
	}
499 500
	P(RED " --> estimated_available_bw=%f\n", mean_bw);

501
	obj->network_loss_rate = constant_network_loss;
502
	obj->congestion_bandwidth = mean_bw;
503
	obj->network_state =
504 505 506
		(current==NULL && constant_network_loss < .1) ?	MSQosAnalyzerNetworkFine
		: (constant_network_loss > .1) ?				MSQosAnalyzerNetworkLossy
		:												MSQosAnalyzerNetworkCongested;
507

508
	return mean_bw;
509 510
}

511 512
static void stateful_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
513

514
	float curbw = obj->latest ? obj->latest->bandwidth : 0.f;
515
	float bw = compute_available_bw(obj);
516

517
	/*try a burst every 50 seconds (10 RTCP packets)*/
518
	if (obj->curindex % 10 == 0){
519
		P(YELLOW "try burst!\n");
520
		obj->burst_state = MSStatefulQosAnalyzerBurstEnable;
521
	}
522
	/*test a min burst to avoid overestimation of available bandwidth*/
523
	else if (obj->curindex % 10 == 2 || obj->curindex % 10 == 3){
524
		P(YELLOW "try minimal burst!\n");
525
		bw *= .33;
526
	}
527

528
	/*no bandwidth estimation computed*/
529
	if (bw <= 0 || curbw <= 0){
530
		action->type=MSRateControlActionDoNothing;
531
		action->value=0;
532
	}else if (bw > curbw){
533
		action->type=MSRateControlActionIncreaseQuality;
534
		action->value=MAX(0, 100. * (bw / curbw - 1));
535 536
	}else{
		action->type=MSRateControlActionDecreaseBitrate;
537
		action->value=MAX(10, -100. * (bw / curbw - 1));
538
	}
539

540
	P(YELLOW "%s of value %d\n", ms_rate_control_action_type_name(action->type), action->value);
541 542
}

543
static bool_t stateful_analyzer_has_improved(MSQosAnalyzer *objbase){
544
	/*never tell the controller that situation has improved to avoid 'Stable' state
545
	which is not necessary for this analyzer*/
546 547 548
	return FALSE;
}

549 550
static void stateful_analyzer_update(MSQosAnalyzer *objbase){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
551 552 553
	static time_t last_measure;

	if (last_measure != ms_time(0)){
554
		obj->upload_bandwidth_count++;
555 556 557 558
		obj->upload_bandwidth_sum+=rtp_session_get_send_bandwidth(obj->session)/1000.0;
	}
	last_measure = ms_time(0);

559 560
	if (obj->burst_duration_ms>0){
		switch (obj->burst_state){
561 562
		case MSStatefulQosAnalyzerBurstEnable:{
			obj->burst_state=MSStatefulQosAnalyzerBurstInProgress;
563 564 565
			ortp_gettimeofday(&obj->start_time, NULL);
			rtp_session_set_duplication_ratio(obj->session, obj->burst_ratio);
			obj->start_seq_number=obj->last_seq_number=obj->session->rtp.snd_seq;
566
		} case MSStatefulQosAnalyzerBurstInProgress: {
567 568 569 570 571 572 573 574 575
			struct timeval now;
			double elapsed;

			ortp_gettimeofday(&now,NULL);
			elapsed=((now.tv_sec-obj->start_time.tv_sec)*1000.0) +  ((now.tv_usec-obj->start_time.tv_usec)/1000.0);

			obj->last_seq_number=obj->session->rtp.snd_seq;

			if (elapsed > obj->burst_duration_ms){
576
				obj->burst_state=MSStatefulQosAnalyzerBurstDisable;
577 578
				rtp_session_set_duplication_ratio(obj->session, 0);
			}
579
		} case MSStatefulQosAnalyzerBurstDisable: {
580
		}
581 582 583
		}
	}
}
584

585 586
static void stateful_analyzer_uninit(MSQosAnalyzer *objbase){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
587 588 589
	ms_list_for_each(obj->rtcpstatspoint, ms_free);
}

590 591 592 593 594 595
static MSQosAnalyzerDesc stateful_analyzer_desc={
	stateful_analyzer_process_rtcp,
	stateful_analyzer_suggest_action,
	stateful_analyzer_has_improved,
	stateful_analyzer_update,
	stateful_analyzer_uninit,
596 597
};

598 599
MSQosAnalyzer * ms_stateful_qos_analyzer_new(RtpSession *session){
	MSStatefulQosAnalyzer *obj=ms_new0(MSStatefulQosAnalyzer,1);
600
	obj->session=session;
601
	obj->parent.desc=&stateful_analyzer_desc;
602
	obj->parent.type=Stateful;
603
	/*double the upload bandwidth based on a 5 sec RTCP reports interval*/
604
	obj->burst_duration_ms=1000;
605
	obj->burst_ratio=9;
606
	return (MSQosAnalyzer*)obj;
607 608
}

609