qosanalyzer.c 24 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
#define LOSS_RATE_MIN_INTERVAL 60
Simon Morlat's avatar
Simon Morlat committed
29
#define LOSS_RATE_MIN_TIME 3000
30

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

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

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

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

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

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

77 78 79 80 81
const char* ms_qos_analyzer_algorithm_to_string(MSQosAnalyzerAlgorithm alg) {
	switch (alg){
		case MSQosAnalyzerAlgorithmSimple: return "Simple";
		case MSQosAnalyzerAlgorithmStateful: return "Stateful";
		default: return NULL;
82 83
	}
}
84
MSQosAnalyzerAlgorithm ms_qos_analyzer_algorithm_from_string(const char* alg) {
85
	if (alg == NULL || strcasecmp(alg, "Simple")==0)
86
		return MSQosAnalyzerAlgorithmSimple;
87
	else if (strcasecmp(alg, "Stateful")==0)
88 89
		return MSQosAnalyzerAlgorithmStateful;

90
	ms_error("MSQosAnalyzer: Invalid QoS analyzer: %s", alg);
91 92
	return MSQosAnalyzerAlgorithmSimple;
}
93

94 95 96 97
const char* ms_qos_analyzer_get_name(MSQosAnalyzer *obj){
	return ms_qos_analyzer_algorithm_to_string(obj->type);
}

98
MSQosAnalyzer *ms_qos_analyzer_ref(MSQosAnalyzer *obj){
99 100 101 102
	obj->refcnt++;
	return obj;
}

103
void ms_qos_analyzer_unref(MSQosAnalyzer *obj){
104 105 106 107
	obj->refcnt--;
	if (obj->refcnt<=0){
		if (obj->desc->uninit)
			obj->desc->uninit(obj);
108
		if (obj->label) ms_free(obj->label);
109
		if (obj->lre) ortp_loss_rate_estimator_destroy(obj->lre);
110

111 112
		ms_free(obj);
	}
113 114
}

115
const char *ms_rate_control_action_type_name(MSRateControlActionType t){
116 117 118 119 120 121 122 123 124 125 126 127 128
	switch(t){
		case MSRateControlActionDoNothing:
			return "DoNothing";
		case MSRateControlActionIncreaseQuality:
			return "IncreaseQuality";
		case MSRateControlActionDecreaseBitrate:
			return "DecreaseBitrate";
		case MSRateControlActionDecreasePacketRate:
			return "DecreasePacketRate";
	}
	return "bad action type";
}

129
/******************************************************************************/
130
/***************************** Simple QoS analyzer ****************************/
131
/******************************************************************************/
132 133
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);
134
	if (cur->rt_prop>=significant_delay && prev->rt_prop>0){
135 136 137 138 139 140 141 142
		if (cur->rt_prop>=(prev->rt_prop*2.0)){
			/*propagation doubled since last report */
			return TRUE;
		}
	}
	return FALSE;
}

143
static bool_t simple_rt_prop_increased(MSSimpleQosAnalyzer *obj){
144 145 146 147 148 149 150 151 152 153
	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;
}

154 155
static bool_t simple_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
156 157
	rtpstats_t *cur;
	const report_block_t *rb=NULL;
158
	bool_t got_stats=FALSE;
159

160 161 162 163 164
	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);
	}
165
	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
166

167 168
		obj->curindex++;
		cur=&obj->stats[obj->curindex % STATS_HISTORY];
169

170 171 172 173 174
		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;
		}
175
		if (ortp_loss_rate_estimator_process_report_block(objbase->lre,obj->session,rb)){
176
			cur->lost_percentage=ortp_loss_rate_estimator_get_value(objbase->lre);
177
			cur->int_jitter=1000.0f*(float)report_block_get_interarrival_jitter(rb)/(float)obj->clockrate;
178
			cur->rt_prop=rtp_session_get_round_trip_propagation(obj->session);
179 180 181

			ms_message("MSSimpleQosAnalyzer: lost_percentage=%f, int_jitter=%f ms, rt_prop=%f sec",
				cur->lost_percentage,cur->int_jitter,cur->rt_prop);
182
			got_stats=TRUE;
183
		}
184
	}
185
	return got_stats;
186 187
}

188 189
static void simple_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
190
	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
191

192
	/*big losses and big jitter */
193
	if (cur->lost_percentage>=unacceptable_loss_rate && cur->int_jitter>=big_jitter){
194
		action->type=MSRateControlActionDecreaseBitrate;
195
		action->value=(int)MIN(cur->lost_percentage,50);
196
		ms_message("MSSimpleQosAnalyzer: loss rate unacceptable and big jitter");
197
	}else if (simple_rt_prop_increased(obj)){
198 199
		action->type=MSRateControlActionDecreaseBitrate;
		action->value=20;
200
		ms_message("MSSimpleQosAnalyzer: rt_prop doubled.");
201 202
	}else if (cur->lost_percentage>=unacceptable_loss_rate){
		/*big loss rate but no jitter, and no big rtp_prop: pure lossy network*/
203
		action->type=MSRateControlActionDecreaseBitrate;
204
		action->value=(int)MIN(cur->lost_percentage,50);
205
		ms_message("MSSimpleQosAnalyzer: loss rate unacceptable.");
206 207
	}else{
		action->type=MSRateControlActionDoNothing;
208
		ms_message("MSSimpleQosAnalyzer: everything is fine.");
209
	}
210 211

	if (objbase->on_action_suggested!=NULL){
212
		int i;
213 214
		char *data[4];
		int datac = sizeof(data) / sizeof(data[0]);
215
		data[0]=ms_strdup("%loss rt_prop_increased int_jitter_ms rt_prop_ms");
216
		data[1]=ms_strdup_printf("%d %d %d %d"
217
			, (int)cur->lost_percentage
218
			, (simple_rt_prop_increased(obj)==TRUE)
219 220
			, (int)cur->int_jitter
			, (int)(1000*cur->rt_prop));
221 222
		data[2]=ms_strdup("action_type action_value");
		data[3]=ms_strdup_printf("%s %d"
223 224 225
			, ms_rate_control_action_type_name(action->type)
			, action->value);

226
		objbase->on_action_suggested(objbase->on_action_suggested_user_pointer, datac, (const char**)data);
227

228 229 230
		for (i=0;i<datac;++i){
			ms_free(data[i]);
		}
231
	}
232 233
}

234 235
static bool_t simple_analyzer_has_improved(MSQosAnalyzer *objbase){
	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
236 237 238 239 240
	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){
241
			ms_message("MSSimpleQosAnalyzer: lost percentage has improved");
242 243 244 245
			return TRUE;
		}else goto end;
	}
	if (obj->rt_prop_doubled && cur->rt_prop<prev->rt_prop){
246
		ms_message("MSSimpleQosAnalyzer: rt prop decreased");
247 248 249 250
		obj->rt_prop_doubled=FALSE;
		return TRUE;
	}

251
	end:
252
	ms_message("MSSimpleQosAnalyzer: no improvements.");
253 254 255
	return FALSE;
}

256 257 258
static MSQosAnalyzerDesc simple_analyzer_desc={
	simple_analyzer_process_rtcp,
	simple_analyzer_suggest_action,
259 260
	simple_analyzer_has_improved,
	NULL,
261
	NULL
262 263
};

264 265
MSQosAnalyzer * ms_simple_qos_analyzer_new(RtpSession *session){
	MSSimpleQosAnalyzer *obj=ms_new0(MSSimpleQosAnalyzer,1);
266
	obj->session=session;
267
	obj->parent.desc=&simple_analyzer_desc;
268
	obj->parent.type=MSQosAnalyzerAlgorithmSimple;
Simon Morlat's avatar
Simon Morlat committed
269
	obj->parent.lre=ortp_loss_rate_estimator_new(LOSS_RATE_MIN_INTERVAL, LOSS_RATE_MIN_TIME, session);
270
	return (MSQosAnalyzer*)obj;
271 272 273 274 275 276
}




/******************************************************************************/
277
/***************************** Stateful QoS analyzer **************************/
278
/******************************************************************************/
279 280 281 282 283 284
static int earlier_than(rtcpstatspoint_t *p, const time_t * now){
	if (p->timestamp < *now){
		ms_free(p);
		return FALSE;
	}
	return TRUE;
285
}
286
static int sort_by_bandwidth(const rtcpstatspoint_t *p1, const rtcpstatspoint_t *p2){
287 288 289
	return p1->bandwidth > p2->bandwidth;
}

290
static float stateful_qos_analyzer_upload_bandwidth(MSStatefulQosAnalyzer *obj, uint32_t seq_num){
291
	int latest_bw;
292 293 294 295
	float bw_per_seqnum=0.f;
	float bw_per_avg=0.f;

	/*First method to compute bandwidth*/
296
	if (obj->upload_bandwidth_count){
297
		bw_per_avg=(float)(obj->upload_bandwidth_sum/obj->upload_bandwidth_count);
298 299
	}
	obj->upload_bandwidth_count=0;
300 301
	obj->upload_bandwidth_sum=0;

302 303 304 305
	for (latest_bw=0;latest_bw<BW_HISTORY;++latest_bw){
		ms_debug("MSStatefulQosAnalyzer[%p]:\t%u\t-->\t%f", obj,
			obj->upload_bandwidth[latest_bw].seq_number,
			obj->upload_bandwidth[latest_bw].up_bandwidth);
306 307
	}

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
	if (obj->upload_bandwidth[(obj->upload_bandwidth_cur+1)%BW_HISTORY].seq_number>seq_num){
		ms_warning("MSStatefulQosAnalyzer[%p]: saved to much points - seq_number lower "
			"than oldest measure! Increase BW_HISTORY or reduce ptime!", obj);
	}else{
		int count = 0;
		latest_bw=obj->upload_bandwidth_cur;
		/*Get the average of all measures with seq number lower than the one from the report*/
		for (latest_bw=0; latest_bw<BW_HISTORY; ++latest_bw){
			if (obj->upload_bandwidth[latest_bw].seq_number>0
				&& obj->upload_bandwidth[latest_bw].seq_number<seq_num){
				count++;
				bw_per_seqnum+=obj->upload_bandwidth[latest_bw].up_bandwidth;
			}
		}
		// invalid, no measures available
		if (count==0){
			ms_error("MSStatefulQosAnalyzer[%p]: no measures available to compute bandwidth for ext_seq=%u", obj, seq_num);
325
			bw_per_seqnum = rtp_session_get_send_bandwidth(obj->session)/1000.0f;
326 327 328 329 330
		}else{
			bw_per_seqnum /= count;//((BW_HISTORY + obj->upload_bandwidth_cur - latest_bw) % BW_HISTORY);
			ms_debug("MSStatefulQosAnalyzer[%p]: found average bandwidth for seq_num=%u", obj, seq_num);
		}
	}
331

332
	ms_message("MSStatefulQosAnalyzer[%p]: bw_curent=%f vs bw_per_avg=%f vs bw_per_seqnum=%f"
333 334
				, obj
				, rtp_session_get_send_bandwidth(obj->session)/1000.0
335 336
				, bw_per_avg
				, bw_per_seqnum);
337

338
	obj->upload_bandwidth_latest = bw_per_seqnum;
339
	return (float)obj->upload_bandwidth_latest;
340 341
}

342 343
static bool_t stateful_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
344 345 346 347 348 349 350
	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);
	}

351

352
	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
353
		if (ortp_loss_rate_estimator_process_report_block(objbase->lre,obj->session,rb)){
354
			int i;
355
			float loss_rate = ortp_loss_rate_estimator_get_value(objbase->lre);
356
			float up_bw = stateful_qos_analyzer_upload_bandwidth(obj,report_block_get_high_ext_seq(rb));
357
			obj->curindex++;
358

359 360 361 362 363 364 365 366
			/*flush bandwidth estimation measures for seq number lower than remote report block received*/
			for (i=0;i<BW_HISTORY;i++){
				if (obj->upload_bandwidth[i].seq_number<report_block_get_high_ext_seq(rb)){
					obj->upload_bandwidth[i].seq_number=0;
					obj->upload_bandwidth[i].up_bandwidth=0.f;
				}
			}

367 368 369
			/* Always skip the first report, since values might be erroneous due
			to initialization of multiples objects (encoder/decoder/stats computing..)
			Instead assume loss rate is a good estimation of network capacity */
370
			if (obj->curindex==1)  {
371 372
				obj->network_loss_rate=loss_rate;
				return TRUE;
373 374
			}

375 376 377
			obj->latest=ms_new0(rtcpstatspoint_t, 1);
			obj->latest->timestamp=ms_time(0);
			obj->latest->bandwidth=up_bw;
378
			obj->latest->loss_percent=loss_rate;
379
			obj->latest->rtt=rtp_session_get_round_trip_propagation(obj->session);
380

381 382
			obj->rtcpstatspoint=ms_list_insert_sorted(obj->rtcpstatspoint,
				obj->latest, (MSCompareFunc)sort_by_bandwidth);
383

384
			/*if the measure was 0% loss, reset to 0% every measures below it*/
385 386 387 388 389 390 391
			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;
				}
392
			}
393
			ms_message("MSStatefulQosAnalyzer[%p]: one more %d: %f %f",
394
				obj, obj->curindex-1, obj->latest->bandwidth, obj->latest->loss_percent);
395 396

			if (ms_list_size(obj->rtcpstatspoint) > ESTIM_HISTORY){
397
				int prev_size = ms_list_size(obj->rtcpstatspoint);
398

399
				/*clean everything which occurred 60 sec or more ago*/
400
				time_t clear_time = ms_time(0) - 60;
401 402 403 404
				obj->rtcpstatspoint = ms_list_remove_custom(obj->rtcpstatspoint,
					(MSCompareFunc)earlier_than, &clear_time);
				ms_message("MSStatefulQosAnalyzer[%p]: reached list maximum capacity "
					"(count=%d) --> Cleaned list (count=%d)",
405
					obj, prev_size, ms_list_size(obj->rtcpstatspoint));
406
			}
407
			return TRUE;
408
		}
409
	}
410
	return FALSE;
411 412
}

413
static double lerp(double inf, double sup, double v){
414 415 416
	return inf + (sup - inf) * v;
}

417 418 419 420 421 422 423 424 425
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;
}

426
static void smooth_values(MSStatefulQosAnalyzer *obj){
427
	MSList *first_loss = find_first_with_loss(obj->rtcpstatspoint);
428 429
	MSList *it = obj->rtcpstatspoint;
	rtcpstatspoint_t *curr = (rtcpstatspoint_t *)it->data;
430
	double prev_loss = 0.;
431 432 433

	if (first_loss == obj->rtcpstatspoint){
		prev_loss = curr->loss_percent;
434 435
		curr->loss_percent = lerp(curr->loss_percent,
			((rtcpstatspoint_t *)it->next->data)->loss_percent, .25);
436 437 438 439
		it = it->next;
	}else{
		it = first_loss;
	}
440 441 442 443 444 445

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

446
	curr = (rtcpstatspoint_t *)it->data;
447

448 449 450 451
	while (it->next != NULL){
		rtcpstatspoint_t *prev = ((rtcpstatspoint_t *)it->prev->data);
		rtcpstatspoint_t *next = ((rtcpstatspoint_t *)it->next->data);

452 453
		double v = ((curr->bandwidth - prev->bandwidth) / (next->bandwidth - prev->bandwidth));
		double new_loss = lerp(prev_loss, next->loss_percent, v);
454 455 456
		prev_loss = curr->loss_percent;
		curr->loss_percent = (curr->loss_percent + new_loss) / 2.;
		it = it->next;
457
		curr = (rtcpstatspoint_t *)it->data;
458
	}
459
	curr->loss_percent = lerp(prev_loss, curr->loss_percent, .75);
460
}
461

462
static double compute_available_bw(MSStatefulQosAnalyzer *obj){
463
	MSList *it;
464 465
	double constant_network_loss = 0.;
	double mean_bw = 0.;
466 467 468 469
	MSList *current = obj->rtcpstatspoint;
	MSList *last = current;
	int size = ms_list_size(obj->rtcpstatspoint);
	if (current == NULL){
470
		ms_message("MSStatefulQosAnalyzer[%p]: no points available for estimation", obj);
471
		return -1;
472 473
	}

474 475 476
	while (last->next){
		last = last->next;
	}
477

478 479
	if (size > 3){
		smooth_values(obj);
480
	}
481
	/*suppose that first point is a reliable estimation of the constant network loss rate*/
482
	constant_network_loss = ((rtcpstatspoint_t *)obj->rtcpstatspoint->data)->loss_percent;
483

484
	ms_message("MSStatefulQosAnalyzer[%p]:\tconstant_network_loss=%f", obj, constant_network_loss);
485
#ifdef DEBUG
486 487
	for (it = obj->rtcpstatspoint; it != NULL; it=it->next){
		rtcpstatspoint_t * point = (rtcpstatspoint_t *)it->data;
Simon Morlat's avatar
Simon Morlat committed
488
		(void)point;
489
		ms_message("MSStatefulQosAnalyzer[%p]:\t\tsorted values %d: %f %f",
490
			obj, ms_list_position(obj->rtcpstatspoint, it), point->bandwidth, point->loss_percent);
491
	}
492
#endif
493

494 495
	if (size == 1){
		rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->data;
496
		ms_message("MSStatefulQosAnalyzer[%p]: one single point", obj);
497
		mean_bw = p->bandwidth * ((p->loss_percent>1e-5) ? (100-p->loss_percent)/100.f:2);
498
	}else{
499 500
		while (current!=NULL && ((rtcpstatspoint_t*)current->data)->loss_percent<3+constant_network_loss){
			ms_message("MSStatefulQosAnalyzer[%p]:\t%d is stable", obj, ms_list_position(obj->rtcpstatspoint, current));
501

502
			/*find the last stable measure point, starting from highest bandwidth*/
503
			for (it=last;it!=current;it=it->prev){
504 505
				if (((rtcpstatspoint_t *)it->data)->loss_percent <= 3 + ((rtcpstatspoint_t*)current->data)->loss_percent){
					ms_message("MSStatefulQosAnalyzer[%p]:\t%d is less than %d",
506
						obj, ms_list_position(obj->rtcpstatspoint, it), ms_list_position(obj->rtcpstatspoint, current));
507 508 509
					current = it;
					break;
				}
510
			}
511
			/*current is the first unstable point, so taking the next one*/
512 513
			current = current->next;
		}
514

515 516
		/*all points are below the constant loss rate threshold:
		there might be bad network conditions but no congestion*/
517 518
		if (current == NULL){
			mean_bw = 2 * ((rtcpstatspoint_t*)last->data)->bandwidth;
519
		/*only first packet is stable*/
520 521
		}else if (current->prev == obj->rtcpstatspoint){
			rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->prev->data;
522
			mean_bw = p->bandwidth * (100 - p->loss_percent) / 100.;
523
		/*otherwise, there is a congestion detected starting at "current"*/
524
		}else{
525 526 527
			rtcpstatspoint_t *laststable = (rtcpstatspoint_t*)current->prev->data;
			rtcpstatspoint_t *firstunstable = (rtcpstatspoint_t*)current->data;
			mean_bw = .5*(laststable->bandwidth+firstunstable->bandwidth);
528
		}
529

530
		ms_message("MSStatefulQosAnalyzer[%p]: [0->%d] last stable is %d(%f;%f)"
531
			, obj
532 533 534
			, ms_list_position(obj->rtcpstatspoint, last)
			, ms_list_position(obj->rtcpstatspoint, (current ? current->prev : last))
			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->bandwidth
535
			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->loss_percent);
536
		if (current!=NULL){
537
			ms_message("MSStatefulQosAnalyzer[%p]: , first unstable is %d(%f;%f)"
538
				, obj
539 540
				, ms_list_position(obj->rtcpstatspoint, current)
				, ((rtcpstatspoint_t*) current->data)->bandwidth
541
				, ((rtcpstatspoint_t*) current->data)->loss_percent);
542
		}
543
	}
544
	ms_message("MSStatefulQosAnalyzer[%p]:  --> estimated_available_bw=%f", obj, mean_bw);
545

546
	obj->network_loss_rate = constant_network_loss;
547
	obj->congestion_bandwidth = mean_bw;
548

549
	return mean_bw;
550 551
}

552 553
static void stateful_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
554

555 556
	double curbw = 0;
	double bw = 0;
557 558 559 560 561 562 563 564 565
	rtcpstatspoint_t* greatest_pt = NULL;
	/*if this is the first measure, there is not enough reliable data to use; we
	assume loss rate is due to non congestionned network. This is mainly useful
	in the case loss rate is high (>30%), to reduce quality even before the second
	RTCP report which can be really used.
	*/
	if (obj->curindex==1){
		if (obj->network_loss_rate!=0.f){
			action->type=MSRateControlActionDecreaseBitrate;
566
			action->value=(int)obj->network_loss_rate;
567 568
		}
	}else {
569
		curbw = obj->latest ? obj->latest->bandwidth : 0.;
570 571 572 573 574 575
		bw = compute_available_bw(obj);
		greatest_pt = ms_list_size(obj->rtcpstatspoint) ?
			(rtcpstatspoint_t*)ms_list_nth_data(obj->rtcpstatspoint, ms_list_size(obj->rtcpstatspoint)-1)
			: NULL;

		/*try a burst every 50 seconds (10 RTCP packets)*/
576
		if (obj->curindex % 10 == 6){
577 578 579 580 581 582 583 584 585 586
			ms_message("MSStatefulQosAnalyzer[%p]: try burst!", obj);
			obj->burst_state = MSStatefulQosAnalyzerBurstEnable;
		}
		/*test a min burst to avoid overestimation of available bandwidth but only
		if there is some loss*/
		else if (greatest_pt!=NULL && greatest_pt->loss_percent>1
				&& (obj->curindex % 10 == 2 || obj->curindex % 10 == 3)){
			ms_message("MSStatefulQosAnalyzer[%p]: try minimal burst!", obj);
			bw *= .33;
		}
587

588 589 590 591 592 593
		/*no bandwidth estimation computed*/
		if (bw <= 0 || curbw <= 0){
			action->type=MSRateControlActionDoNothing;
			action->value=0;
		}else if (bw > curbw){
			action->type=MSRateControlActionIncreaseQuality;
594
			action->value=MAX(0, (int)(100 * (bw / curbw - 1)));
595 596
		}else{
			action->type=MSRateControlActionDecreaseBitrate;
597
			action->value=MAX(10, (int)(-100 * (bw / curbw - 1)));
598
		}
599
	}
600

601
	ms_message("MSStatefulQosAnalyzer[%p]: %s of value %d",
602
		obj, ms_rate_control_action_type_name(action->type), action->value);
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627


	if (objbase->on_action_suggested!=NULL){
		int i;
		char *data[4];
		int datac = sizeof(data) / sizeof(data[0]);
		data[0]=ms_strdup("%loss rtt_ms cur_bw");
		data[1]=ms_strdup_printf("%d %d %d"
			, obj->latest?(int)obj->latest->loss_percent:0
			, obj->latest?(int)obj->latest->rtt:0
			, obj->latest?(int)obj->latest->bandwidth:0
			);
		data[2]=ms_strdup("action_type action_value est_bw");
		data[3]=ms_strdup_printf("%s %d %d"
			, ms_rate_control_action_type_name(action->type)
			, action->value
			, (int)bw
			);

		objbase->on_action_suggested(objbase->on_action_suggested_user_pointer, datac, (const char**)data);

		for (i=0;i<datac;++i){
			ms_free(data[i]);
		}
	}
628 629
}

630
static bool_t stateful_analyzer_has_improved(MSQosAnalyzer *objbase){
631
	/*never tell the controller that situation has improved to avoid 'Stable' state
632
	which is not necessary for this analyzer*/
633 634 635
	return FALSE;
}

636 637
static void stateful_analyzer_update(MSQosAnalyzer *objbase){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
638 639
	static time_t last_measure;

640 641 642 643
	/* Every seconds, save the bandwidth used. This is needed to know how much
	bandwidth was used when receiving a receiver report. Since the report contains
	the "last sequence number", it allows us to precisely know which interval to
	consider */
644
	if (last_measure != ms_time(0)){
645
		obj->upload_bandwidth_count++;
646
		obj->upload_bandwidth_sum+=rtp_session_get_send_bandwidth(obj->session)/1000.0;
647 648 649

		/* Save bandwidth used at this time */
		obj->upload_bandwidth[obj->upload_bandwidth_cur].seq_number = rtp_session_get_seq_number(obj->session);
650
		obj->upload_bandwidth[obj->upload_bandwidth_cur].up_bandwidth = rtp_session_get_send_bandwidth(obj->session)/1000.0f;
651
		obj->upload_bandwidth_cur = (obj->upload_bandwidth_cur+1)%BW_HISTORY;
652 653 654
	}
	last_measure = ms_time(0);

655 656
	if (obj->burst_duration_ms>0){
		switch (obj->burst_state){
657 658
		case MSStatefulQosAnalyzerBurstEnable:{
			obj->burst_state=MSStatefulQosAnalyzerBurstInProgress;
659
			ortp_gettimeofday(&obj->start_time, NULL);
660
			rtp_session_set_duplication_ratio(obj->session, (float)obj->burst_ratio);
661
		} case MSStatefulQosAnalyzerBurstInProgress: {
662
			struct timeval now;
663
			float elapsed;
664 665

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

			if (elapsed > obj->burst_duration_ms){
669
				obj->burst_state=MSStatefulQosAnalyzerBurstDisable;
670 671
				rtp_session_set_duplication_ratio(obj->session, 0);
			}
672
		} case MSStatefulQosAnalyzerBurstDisable: {
673
		}
674 675 676
		}
	}
}
677

678 679
static void stateful_analyzer_uninit(MSQosAnalyzer *objbase){
	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
680
	ms_list_for_each(obj->rtcpstatspoint, ms_free);
681
	ms_list_free(obj->rtcpstatspoint);
682 683
}

684 685 686 687 688 689
static MSQosAnalyzerDesc stateful_analyzer_desc={
	stateful_analyzer_process_rtcp,
	stateful_analyzer_suggest_action,
	stateful_analyzer_has_improved,
	stateful_analyzer_update,
	stateful_analyzer_uninit,
690 691
};

692 693
MSQosAnalyzer * ms_stateful_qos_analyzer_new(RtpSession *session){
	MSStatefulQosAnalyzer *obj=ms_new0(MSStatefulQosAnalyzer,1);
694
	obj->session=session;
695
	obj->parent.desc=&stateful_analyzer_desc;
696
	obj->parent.type=MSQosAnalyzerAlgorithmStateful;
Simon Morlat's avatar
Simon Morlat committed
697
	obj->parent.lre=ortp_loss_rate_estimator_new(LOSS_RATE_MIN_INTERVAL, LOSS_RATE_MIN_TIME, session);
698

699
	/*burst period will float the upload bandwidth assuming 5 sec RTCP reports interval*/
700
	obj->burst_duration_ms=1000;
701
	obj->burst_ratio=9;
702
	return (MSQosAnalyzer*)obj;
703 704
}

705