1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 
4  * Copyright (C) 2011  Belledonne Communications, Grenoble, France
5 
6 	 Author: Simon Morlat <simon.morlat@linphone.org>
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 #include "mediastreamer2/bitratecontrol.h"
24 #include "qosanalyzer.h"
25 
26 #include <bctoolbox/defs.h>
27 
28 #include <math.h>
29 
30 #define LOSS_RATE_MIN_INTERVAL 60
31 #define LOSS_RATE_MIN_TIME 3000
32 
33 /**
34  * Analyses a received RTCP packet.
35  * Returns TRUE is relevant information has been found in the rtcp message, FALSE otherwise.
36 **/
ms_qos_analyzer_process_rtcp(MSQosAnalyzer * obj,mblk_t * msg)37 bool_t ms_qos_analyzer_process_rtcp(MSQosAnalyzer *obj,mblk_t *msg){
38 	if (obj->desc->process_rtcp){
39 		return obj->desc->process_rtcp(obj,msg);
40 	}
41 	ms_error("MSQosAnalyzer: Unimplemented process_rtcp() call.");
42 	return FALSE;
43 }
44 
ms_qos_analyzer_suggest_action(MSQosAnalyzer * obj,MSRateControlAction * action)45 void ms_qos_analyzer_suggest_action(MSQosAnalyzer *obj, MSRateControlAction *action){
46 	if (obj->desc->suggest_action){
47 		obj->desc->suggest_action(obj,action);
48 	}
49 }
50 
ms_qos_analyzer_update(MSQosAnalyzer * obj)51 void ms_qos_analyzer_update(MSQosAnalyzer *obj){
52 	if (obj->desc->update){
53 		obj->desc->update(obj);
54 	}
55 }
56 
ms_qos_analyzer_has_improved(MSQosAnalyzer * obj)57 bool_t ms_qos_analyzer_has_improved(MSQosAnalyzer *obj){
58 	if (obj->desc->has_improved){
59 		return obj->desc->has_improved(obj);
60 	}
61 	ms_error("MSQosAnalyzer: Unimplemented has_improved() call.");
62 	return TRUE;
63 }
64 
ms_qos_analyzer_set_on_action_suggested(MSQosAnalyzer * obj,void (* on_action_suggested)(void *,int,const char **),void * u)65 void ms_qos_analyzer_set_on_action_suggested(MSQosAnalyzer *obj,
66 	void (*on_action_suggested)(void*, int, const char**), void* u){
67 	obj->on_action_suggested=on_action_suggested;
68 	obj->on_action_suggested_user_pointer=u;
69 }
70 
ms_qos_analyser_set_label(MSQosAnalyzer * obj,const char * label)71 void ms_qos_analyser_set_label(MSQosAnalyzer *obj, const char *label){
72 	if (obj->label){
73 		ms_free(obj->label);
74 		obj->label=NULL;
75 	}
76 	if (label) obj->label=ms_strdup(label);
77 }
78 
ms_qos_analyzer_algorithm_to_string(MSQosAnalyzerAlgorithm alg)79 const char* ms_qos_analyzer_algorithm_to_string(MSQosAnalyzerAlgorithm alg) {
80 	switch (alg){
81 		case MSQosAnalyzerAlgorithmSimple: return "Simple";
82 		case MSQosAnalyzerAlgorithmStateful: return "Stateful";
83 		default: return NULL;
84 	}
85 }
ms_qos_analyzer_algorithm_from_string(const char * alg)86 MSQosAnalyzerAlgorithm ms_qos_analyzer_algorithm_from_string(const char* alg) {
87 	if (alg == NULL || strcasecmp(alg, "Simple")==0)
88 		return MSQosAnalyzerAlgorithmSimple;
89 	else if (strcasecmp(alg, "Stateful")==0)
90 		return MSQosAnalyzerAlgorithmStateful;
91 
92 	ms_error("MSQosAnalyzer: Invalid QoS analyzer: %s", alg);
93 	return MSQosAnalyzerAlgorithmSimple;
94 }
95 
ms_qos_analyzer_get_name(MSQosAnalyzer * obj)96 const char* ms_qos_analyzer_get_name(MSQosAnalyzer *obj){
97 	return ms_qos_analyzer_algorithm_to_string(obj->type);
98 }
99 
ms_qos_analyzer_ref(MSQosAnalyzer * obj)100 MSQosAnalyzer *ms_qos_analyzer_ref(MSQosAnalyzer *obj){
101 	obj->refcnt++;
102 	return obj;
103 }
104 
ms_qos_analyzer_unref(MSQosAnalyzer * obj)105 void ms_qos_analyzer_unref(MSQosAnalyzer *obj){
106 	obj->refcnt--;
107 	if (obj->refcnt<=0){
108 		if (obj->desc->uninit)
109 			obj->desc->uninit(obj);
110 		if (obj->label) ms_free(obj->label);
111 		if (obj->lre) ortp_loss_rate_estimator_destroy(obj->lre);
112 
113 		ms_free(obj);
114 	}
115 }
116 
ms_rate_control_action_type_name(MSRateControlActionType t)117 const char *ms_rate_control_action_type_name(MSRateControlActionType t){
118 	switch(t){
119 		case MSRateControlActionDoNothing:
120 			return "DoNothing";
121 		case MSRateControlActionIncreaseQuality:
122 			return "IncreaseQuality";
123 		case MSRateControlActionDecreaseBitrate:
124 			return "DecreaseBitrate";
125 		case MSRateControlActionDecreasePacketRate:
126 			return "DecreasePacketRate";
127 	}
128 	return "bad action type";
129 }
130 
131 /******************************************************************************/
132 /***************************** Simple QoS analyzer ****************************/
133 /******************************************************************************/
rt_prop_doubled(rtpstats_t * cur,rtpstats_t * prev)134 static bool_t rt_prop_doubled(rtpstats_t *cur,rtpstats_t *prev){
135 	//ms_message("AudioBitrateController: cur=%f, prev=%f",cur->rt_prop,prev->rt_prop);
136 	if (cur->rt_prop>=significant_delay && prev->rt_prop>0){
137 		if (cur->rt_prop>=(prev->rt_prop*2.0)){
138 			/*propagation doubled since last report */
139 			return TRUE;
140 		}
141 	}
142 	return FALSE;
143 }
144 
simple_rt_prop_increased(MSSimpleQosAnalyzer * obj)145 static bool_t simple_rt_prop_increased(MSSimpleQosAnalyzer *obj){
146 	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
147 	rtpstats_t *prev=&obj->stats[(STATS_HISTORY+obj->curindex-1) % STATS_HISTORY];
148 
149 	if (rt_prop_doubled(cur,prev)){
150 		obj->rt_prop_doubled=TRUE;
151 		return TRUE;
152 	}
153 	return FALSE;
154 }
155 
simple_analyzer_process_rtcp(MSQosAnalyzer * objbase,mblk_t * rtcp)156 static bool_t simple_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
157 	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
158 	rtpstats_t *cur;
159 	const report_block_t *rb=NULL;
160 	bool_t got_stats=FALSE;
161 
162 	if (rtcp_is_SR(rtcp)){
163 		rb=rtcp_SR_get_report_block(rtcp,0);
164 	}else if (rtcp_is_RR(rtcp)){
165 		rb=rtcp_RR_get_report_block(rtcp,0);
166 	}
167 	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
168 
169 		obj->curindex++;
170 		cur=&obj->stats[obj->curindex % STATS_HISTORY];
171 
172 		if (obj->clockrate==0){
173 			PayloadType *pt=rtp_profile_get_payload(rtp_session_get_send_profile(obj->session),rtp_session_get_send_payload_type(obj->session));
174 			if (pt!=NULL) obj->clockrate=pt->clock_rate;
175 			else return FALSE;
176 		}
177 		if (ortp_loss_rate_estimator_process_report_block(objbase->lre,obj->session,rb)){
178 			cur->lost_percentage=ortp_loss_rate_estimator_get_value(objbase->lre);
179 			cur->int_jitter=1000.0f*(float)report_block_get_interarrival_jitter(rb)/(float)obj->clockrate;
180 			cur->rt_prop=rtp_session_get_round_trip_propagation(obj->session);
181 
182 			ms_message("MSSimpleQosAnalyzer: lost_percentage=%f, int_jitter=%f ms, rt_prop=%f sec",
183 				cur->lost_percentage,cur->int_jitter,cur->rt_prop);
184 			got_stats=TRUE;
185 		}
186 	}
187 	return got_stats;
188 }
189 
simple_analyzer_suggest_action(MSQosAnalyzer * objbase,MSRateControlAction * action)190 static void simple_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
191 	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
192 	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
193 
194 	/*big losses and big jitter */
195 	if (cur->lost_percentage>=unacceptable_loss_rate && cur->int_jitter>=big_jitter){
196 		action->type=MSRateControlActionDecreaseBitrate;
197 		action->value=(int)MIN(cur->lost_percentage,50);
198 		ms_message("MSSimpleQosAnalyzer: loss rate unacceptable and big jitter");
199 	}else if (simple_rt_prop_increased(obj)){
200 		action->type=MSRateControlActionDecreaseBitrate;
201 		action->value=20;
202 		ms_message("MSSimpleQosAnalyzer: rt_prop doubled.");
203 	}else if (cur->lost_percentage>=unacceptable_loss_rate){
204 		/*big loss rate but no jitter, and no big rtp_prop: pure lossy network*/
205 		action->type=MSRateControlActionDecreaseBitrate;
206 		action->value=(int)MIN(cur->lost_percentage,50);
207 		ms_message("MSSimpleQosAnalyzer: loss rate unacceptable.");
208 	}else{
209 		action->type=MSRateControlActionDoNothing;
210 		ms_message("MSSimpleQosAnalyzer: everything is fine.");
211 	}
212 
213 	if (objbase->on_action_suggested!=NULL){
214 		int i;
215 		char *data[4];
216 		int datac = sizeof(data) / sizeof(data[0]);
217 		data[0]=ms_strdup("%loss rt_prop_increased int_jitter_ms rt_prop_ms");
218 		data[1]=ms_strdup_printf("%d %d %d %d"
219 			, (int)cur->lost_percentage
220 			, (simple_rt_prop_increased(obj)==TRUE)
221 			, (int)cur->int_jitter
222 			, (int)(1000*cur->rt_prop));
223 		data[2]=ms_strdup("action_type action_value");
224 		data[3]=ms_strdup_printf("%s %d"
225 			, ms_rate_control_action_type_name(action->type)
226 			, action->value);
227 
228 		objbase->on_action_suggested(objbase->on_action_suggested_user_pointer, datac, (const char**)data);
229 
230 		for (i=0;i<datac;++i){
231 			ms_free(data[i]);
232 		}
233 	}
234 }
235 
simple_analyzer_has_improved(MSQosAnalyzer * objbase)236 static bool_t simple_analyzer_has_improved(MSQosAnalyzer *objbase){
237 	MSSimpleQosAnalyzer *obj=(MSSimpleQosAnalyzer*)objbase;
238 	rtpstats_t *cur=&obj->stats[obj->curindex % STATS_HISTORY];
239 	rtpstats_t *prev=&obj->stats[(STATS_HISTORY+obj->curindex-1) % STATS_HISTORY];
240 
241 	if (prev->lost_percentage>=unacceptable_loss_rate){
242 		if (cur->lost_percentage<prev->lost_percentage){
243 			ms_message("MSSimpleQosAnalyzer: lost percentage has improved");
244 			return TRUE;
245 		}else goto end;
246 	}
247 	if (obj->rt_prop_doubled && cur->rt_prop<prev->rt_prop){
248 		ms_message("MSSimpleQosAnalyzer: rt prop decreased");
249 		obj->rt_prop_doubled=FALSE;
250 		return TRUE;
251 	}
252 
253 	end:
254 	ms_message("MSSimpleQosAnalyzer: no improvements.");
255 	return FALSE;
256 }
257 
258 static MSQosAnalyzerDesc simple_analyzer_desc={
259 	simple_analyzer_process_rtcp,
260 	simple_analyzer_suggest_action,
261 	simple_analyzer_has_improved,
262 	NULL,
263 	NULL
264 };
265 
ms_simple_qos_analyzer_new(RtpSession * session)266 MSQosAnalyzer * ms_simple_qos_analyzer_new(RtpSession *session){
267 	MSSimpleQosAnalyzer *obj=ms_new0(MSSimpleQosAnalyzer,1);
268 	obj->session=session;
269 	obj->parent.desc=&simple_analyzer_desc;
270 	obj->parent.type=MSQosAnalyzerAlgorithmSimple;
271 	obj->parent.lre=ortp_loss_rate_estimator_new(LOSS_RATE_MIN_INTERVAL, LOSS_RATE_MIN_TIME, session);
272 	return (MSQosAnalyzer*)obj;
273 }
274 
275 
276 
277 
278 /******************************************************************************/
279 /***************************** Stateful QoS analyzer **************************/
280 /******************************************************************************/
earlier_than(rtcpstatspoint_t * p,const time_t * now)281 static int earlier_than(rtcpstatspoint_t *p, const time_t * now){
282 	if (p->timestamp < *now){
283 		ms_free(p);
284 		return FALSE;
285 	}
286 	return TRUE;
287 }
sort_by_bandwidth(const rtcpstatspoint_t * p1,const rtcpstatspoint_t * p2)288 static int sort_by_bandwidth(const rtcpstatspoint_t *p1, const rtcpstatspoint_t *p2){
289 	return p1->bandwidth > p2->bandwidth;
290 }
291 
stateful_qos_analyzer_upload_bandwidth(MSStatefulQosAnalyzer * obj,uint32_t seq_num)292 static float stateful_qos_analyzer_upload_bandwidth(MSStatefulQosAnalyzer *obj, uint32_t seq_num){
293 	int latest_bw;
294 	float bw_per_seqnum=0.f;
295 	float bw_per_avg=0.f;
296 
297 	/*First method to compute bandwidth*/
298 	if (obj->upload_bandwidth_count){
299 		bw_per_avg=(float)(obj->upload_bandwidth_sum/obj->upload_bandwidth_count);
300 	}
301 	obj->upload_bandwidth_count=0;
302 	obj->upload_bandwidth_sum=0;
303 
304 	for (latest_bw=0;latest_bw<BW_HISTORY;++latest_bw){
305 		ms_debug("MSStatefulQosAnalyzer[%p]:\t%u\t-->\t%f", obj,
306 			obj->upload_bandwidth[latest_bw].seq_number,
307 			obj->upload_bandwidth[latest_bw].up_bandwidth);
308 	}
309 
310 	if (obj->upload_bandwidth[(obj->upload_bandwidth_cur+1)%BW_HISTORY].seq_number>seq_num){
311 		ms_warning("MSStatefulQosAnalyzer[%p]: saved to much points - seq_number lower "
312 			"than oldest measure! Increase BW_HISTORY or reduce ptime!", obj);
313 	}else{
314 		int count = 0;
315 		latest_bw=obj->upload_bandwidth_cur;
316 		/*Get the average of all measures with seq number lower than the one from the report*/
317 		for (latest_bw=0; latest_bw<BW_HISTORY; ++latest_bw){
318 			if (obj->upload_bandwidth[latest_bw].seq_number>0
319 				&& obj->upload_bandwidth[latest_bw].seq_number<seq_num){
320 				count++;
321 				bw_per_seqnum+=obj->upload_bandwidth[latest_bw].up_bandwidth;
322 			}
323 		}
324 		// invalid, no measures available
325 		if (count==0){
326 			ms_error("MSStatefulQosAnalyzer[%p]: no measures available to compute bandwidth for ext_seq=%u", obj, seq_num);
327 			bw_per_seqnum = rtp_session_get_send_bandwidth(obj->session)/1000.0f;
328 		}else{
329 			bw_per_seqnum /= count;//((BW_HISTORY + obj->upload_bandwidth_cur - latest_bw) % BW_HISTORY);
330 			ms_debug("MSStatefulQosAnalyzer[%p]: found average bandwidth for seq_num=%u", obj, seq_num);
331 		}
332 	}
333 
334 	ms_message("MSStatefulQosAnalyzer[%p]: bw_curent=%f vs bw_per_avg=%f vs bw_per_seqnum=%f"
335 				, obj
336 				, rtp_session_get_send_bandwidth(obj->session)/1000.0
337 				, bw_per_avg
338 				, bw_per_seqnum);
339 
340 	obj->upload_bandwidth_latest = bw_per_seqnum;
341 	return (float)obj->upload_bandwidth_latest;
342 }
343 
stateful_analyzer_process_rtcp(MSQosAnalyzer * objbase,mblk_t * rtcp)344 static bool_t stateful_analyzer_process_rtcp(MSQosAnalyzer *objbase, mblk_t *rtcp){
345 	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
346 	const report_block_t *rb=NULL;
347 	if (rtcp_is_SR(rtcp)){
348 		rb=rtcp_SR_get_report_block(rtcp,0);
349 	}else if (rtcp_is_RR(rtcp)){
350 		rb=rtcp_RR_get_report_block(rtcp,0);
351 	}
352 
353 
354 	if (rb && report_block_get_ssrc(rb)==rtp_session_get_send_ssrc(obj->session)){
355 		if (ortp_loss_rate_estimator_process_report_block(objbase->lre,obj->session,rb)){
356 			int i;
357 			float loss_rate = ortp_loss_rate_estimator_get_value(objbase->lre);
358 			float up_bw = stateful_qos_analyzer_upload_bandwidth(obj,report_block_get_high_ext_seq(rb));
359 			obj->curindex++;
360 
361 			/*flush bandwidth estimation measures for seq number lower than remote report block received*/
362 			for (i=0;i<BW_HISTORY;i++){
363 				if (obj->upload_bandwidth[i].seq_number<report_block_get_high_ext_seq(rb)){
364 					obj->upload_bandwidth[i].seq_number=0;
365 					obj->upload_bandwidth[i].up_bandwidth=0.f;
366 				}
367 			}
368 
369 			/* Always skip the first report, since values might be erroneous due
370 			to initialization of multiples objects (encoder/decoder/stats computing..)
371 			Instead assume loss rate is a good estimation of network capacity */
372 			if (obj->curindex==1)  {
373 				obj->network_loss_rate=loss_rate;
374 				return TRUE;
375 			}
376 
377 			obj->latest=ms_new0(rtcpstatspoint_t, 1);
378 			obj->latest->timestamp=ms_time(0);
379 			obj->latest->bandwidth=up_bw;
380 			obj->latest->loss_percent=loss_rate;
381 			obj->latest->rtt=rtp_session_get_round_trip_propagation(obj->session);
382 
383 			obj->rtcpstatspoint=bctbx_list_insert_sorted(obj->rtcpstatspoint,
384 				obj->latest, (bctbx_compare_func)sort_by_bandwidth);
385 
386 			/*if the measure was 0% loss, reset to 0% every measures below it*/
387 			if (obj->latest->loss_percent < 1e-5){
388 				bctbx_list_t *it=obj->rtcpstatspoint;
389 				bctbx_list_t *latest_pos=bctbx_list_find(obj->rtcpstatspoint,obj->latest);
390 				while (it!=latest_pos->next){
391 					((rtcpstatspoint_t *)it->data)->loss_percent=0.f;
392 					it = it->next;
393 				}
394 			}
395 			ms_message("MSStatefulQosAnalyzer[%p]: one more %d: %f %f",
396 				obj, obj->curindex-1, obj->latest->bandwidth, obj->latest->loss_percent);
397 
398 			if (bctbx_list_size(obj->rtcpstatspoint) > ESTIM_HISTORY){
399 				size_t prev_size = bctbx_list_size(obj->rtcpstatspoint);
400 
401 				/*clean everything which occurred 60 sec or more ago*/
402 				time_t clear_time = ms_time(0) - 60;
403 				obj->rtcpstatspoint = bctbx_list_remove_custom(obj->rtcpstatspoint,
404 					(bctbx_compare_func)earlier_than, &clear_time);
405 				ms_message("MSStatefulQosAnalyzer[%p]: reached list maximum capacity "
406 					"(count=%u) --> Cleaned list (count=%u)",
407 					obj, (unsigned int)prev_size, (unsigned int)bctbx_list_size(obj->rtcpstatspoint));
408 			}
409 			return TRUE;
410 		}
411 	}
412 	return FALSE;
413 }
414 
lerp(double inf,double sup,double v)415 static double lerp(double inf, double sup, double v){
416 	return inf + (sup - inf) * v;
417 }
418 
find_first_with_loss(bctbx_list_t * list)419 static bctbx_list_t *find_first_with_loss(bctbx_list_t *list){
420 	for(;list!=NULL;list=list->next){
421 		if (((rtcpstatspoint_t *)list->data)->loss_percent > 1e-5){
422 			return list;
423 		}
424 	}
425 	return NULL;
426 }
427 
smooth_values(MSStatefulQosAnalyzer * obj)428 static void smooth_values(MSStatefulQosAnalyzer *obj){
429 	bctbx_list_t *first_loss = find_first_with_loss(obj->rtcpstatspoint);
430 	bctbx_list_t *it = obj->rtcpstatspoint;
431 	rtcpstatspoint_t *curr = (rtcpstatspoint_t *)it->data;
432 	double prev_loss = 0.;
433 
434 	if (first_loss == obj->rtcpstatspoint){
435 		prev_loss = curr->loss_percent;
436 		curr->loss_percent = lerp(curr->loss_percent,
437 			((rtcpstatspoint_t *)it->next->data)->loss_percent, .25);
438 		it = it->next;
439 	}else{
440 		it = first_loss;
441 	}
442 
443 	/*nothing to smooth*/
444 	if (it == NULL){
445 		return;
446 	}
447 
448 	curr = (rtcpstatspoint_t *)it->data;
449 
450 	while (it->next != NULL){
451 		rtcpstatspoint_t *prev = ((rtcpstatspoint_t *)it->prev->data);
452 		rtcpstatspoint_t *next = ((rtcpstatspoint_t *)it->next->data);
453 
454 		double v = ((curr->bandwidth - prev->bandwidth) / (next->bandwidth - prev->bandwidth));
455 		double new_loss = lerp(prev_loss, next->loss_percent, v);
456 		prev_loss = curr->loss_percent;
457 		curr->loss_percent = (curr->loss_percent + new_loss) / 2.;
458 		it = it->next;
459 		curr = (rtcpstatspoint_t *)it->data;
460 	}
461 	curr->loss_percent = lerp(prev_loss, curr->loss_percent, .75);
462 }
463 
compute_available_bw(MSStatefulQosAnalyzer * obj)464 static double compute_available_bw(MSStatefulQosAnalyzer *obj){
465 	bctbx_list_t *it;
466 	double constant_network_loss = 0.;
467 	double mean_bw = 0.;
468 	bctbx_list_t *current = obj->rtcpstatspoint;
469 	bctbx_list_t *last = current;
470 	size_t size = bctbx_list_size(obj->rtcpstatspoint);
471 	if (current == NULL){
472 		ms_message("MSStatefulQosAnalyzer[%p]: no points available for estimation", obj);
473 		return -1;
474 	}
475 
476 	while (last->next){
477 		last = last->next;
478 	}
479 
480 	if (size > 3){
481 		smooth_values(obj);
482 	}
483 	/*suppose that first point is a reliable estimation of the constant network loss rate*/
484 	constant_network_loss = ((rtcpstatspoint_t *)obj->rtcpstatspoint->data)->loss_percent;
485 
486 	ms_message("MSStatefulQosAnalyzer[%p]:\tconstant_network_loss=%f", obj, constant_network_loss);
487 #ifdef DEBUG
488 	for (it = obj->rtcpstatspoint; it != NULL; it=it->next){
489 		rtcpstatspoint_t * point = (rtcpstatspoint_t *)it->data;
490 		(void)point;
491 		ms_message("MSStatefulQosAnalyzer[%p]:\t\tsorted values %d: %f %f",
492 			obj, bctbx_list_position(obj->rtcpstatspoint, it), point->bandwidth, point->loss_percent);
493 	}
494 #endif
495 
496 	if (size == 1){
497 		rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->data;
498 		ms_message("MSStatefulQosAnalyzer[%p]: one single point", obj);
499 		mean_bw = p->bandwidth * ((p->loss_percent>1e-5) ? (100-p->loss_percent)/100.f:2);
500 	}else{
501 		while (current!=NULL && ((rtcpstatspoint_t*)current->data)->loss_percent<3+constant_network_loss){
502 			ms_message("MSStatefulQosAnalyzer[%p]:\t%d is stable", obj, bctbx_list_position(obj->rtcpstatspoint, current));
503 
504 			/*find the last stable measure point, starting from highest bandwidth*/
505 			for (it=last;it!=current;it=it->prev){
506 				if (((rtcpstatspoint_t *)it->data)->loss_percent <= 3 + ((rtcpstatspoint_t*)current->data)->loss_percent){
507 					ms_message("MSStatefulQosAnalyzer[%p]:\t%d is less than %d",
508 						obj, bctbx_list_position(obj->rtcpstatspoint, it), bctbx_list_position(obj->rtcpstatspoint, current));
509 					current = it;
510 					break;
511 				}
512 			}
513 			/*current is the first unstable point, so taking the next one*/
514 			current = current->next;
515 		}
516 
517 		/*all points are below the constant loss rate threshold:
518 		there might be bad network conditions but no congestion*/
519 		if (current == NULL){
520 			mean_bw = 2 * ((rtcpstatspoint_t*)last->data)->bandwidth;
521 		/*only first packet is stable*/
522 		}else if (current->prev == obj->rtcpstatspoint){
523 			rtcpstatspoint_t *p = (rtcpstatspoint_t *)current->prev->data;
524 			mean_bw = p->bandwidth * (100 - p->loss_percent) / 100.;
525 		/*otherwise, there is a congestion detected starting at "current"*/
526 		}else{
527 			rtcpstatspoint_t *laststable = (rtcpstatspoint_t*)current->prev->data;
528 			rtcpstatspoint_t *firstunstable = (rtcpstatspoint_t*)current->data;
529 			mean_bw = .5*(laststable->bandwidth+firstunstable->bandwidth);
530 		}
531 
532 		ms_message("MSStatefulQosAnalyzer[%p]: [0->%d] last stable is %d(%f;%f)"
533 			, obj
534 			, bctbx_list_position(obj->rtcpstatspoint, last)
535 			, bctbx_list_position(obj->rtcpstatspoint, (current ? current->prev : last))
536 			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->bandwidth
537 			, ((rtcpstatspoint_t*) (current ? current->prev->data : last->data))->loss_percent);
538 		if (current!=NULL){
539 			ms_message("MSStatefulQosAnalyzer[%p]: , first unstable is %d(%f;%f)"
540 				, obj
541 				, bctbx_list_position(obj->rtcpstatspoint, current)
542 				, ((rtcpstatspoint_t*) current->data)->bandwidth
543 				, ((rtcpstatspoint_t*) current->data)->loss_percent);
544 		}
545 	}
546 	ms_message("MSStatefulQosAnalyzer[%p]:  --> estimated_available_bw=%f", obj, mean_bw);
547 
548 	obj->network_loss_rate = constant_network_loss;
549 	obj->congestion_bandwidth = mean_bw;
550 
551 	return mean_bw;
552 }
553 
stateful_analyzer_suggest_action(MSQosAnalyzer * objbase,MSRateControlAction * action)554 static void stateful_analyzer_suggest_action(MSQosAnalyzer *objbase, MSRateControlAction *action){
555 	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
556 
557 	double curbw = 0;
558 	double bw = 0;
559 	rtcpstatspoint_t* greatest_pt = NULL;
560 	/*if this is the first measure, there is not enough reliable data to use; we
561 	assume loss rate is due to non congestionned network. This is mainly useful
562 	in the case loss rate is high (>30%), to reduce quality even before the second
563 	RTCP report which can be really used.
564 	*/
565 	if (obj->curindex==1){
566 		if (obj->network_loss_rate!=0.f){
567 			action->type=MSRateControlActionDecreaseBitrate;
568 			action->value=(int)obj->network_loss_rate;
569 		}
570 	}else {
571 		curbw = obj->latest ? obj->latest->bandwidth : 0.;
572 		bw = compute_available_bw(obj);
573 		greatest_pt = bctbx_list_size(obj->rtcpstatspoint) ?
574 			(rtcpstatspoint_t*)bctbx_list_nth_data(obj->rtcpstatspoint, (int)bctbx_list_size(obj->rtcpstatspoint)-1)
575 			: NULL;
576 
577 		/*try a burst every 50 seconds (10 RTCP packets)*/
578 		if (obj->curindex % 10 == 6){
579 			ms_message("MSStatefulQosAnalyzer[%p]: try burst!", obj);
580 			obj->burst_state = MSStatefulQosAnalyzerBurstEnable;
581 		}
582 		/*test a min burst to avoid overestimation of available bandwidth but only
583 		if there is some loss*/
584 		else if (greatest_pt!=NULL && greatest_pt->loss_percent>1
585 				&& (obj->curindex % 10 == 2 || obj->curindex % 10 == 3)){
586 			ms_message("MSStatefulQosAnalyzer[%p]: try minimal burst!", obj);
587 			bw *= .33;
588 		}
589 
590 		/*no bandwidth estimation computed*/
591 		if (bw <= 0 || curbw <= 0){
592 			action->type=MSRateControlActionDoNothing;
593 			action->value=0;
594 		}else if (bw > curbw){
595 			action->type=MSRateControlActionIncreaseQuality;
596 			action->value=MAX(0, (int)(100 * (bw / curbw - 1)));
597 		}else{
598 			action->type=MSRateControlActionDecreaseBitrate;
599 			action->value=MAX(10, (int)(-100 * (bw / curbw - 1)));
600 		}
601 	}
602 
603 	ms_message("MSStatefulQosAnalyzer[%p]: %s of value %d",
604 		obj, ms_rate_control_action_type_name(action->type), action->value);
605 
606 
607 	if (objbase->on_action_suggested!=NULL){
608 		int i;
609 		char *data[4];
610 		int datac = sizeof(data) / sizeof(data[0]);
611 		data[0]=ms_strdup("%loss rtt_ms cur_bw");
612 		data[1]=ms_strdup_printf("%d %d %d"
613 			, obj->latest?(int)obj->latest->loss_percent:0
614 			, obj->latest?(int)obj->latest->rtt:0
615 			, obj->latest?(int)obj->latest->bandwidth:0
616 			);
617 		data[2]=ms_strdup("action_type action_value est_bw");
618 		data[3]=ms_strdup_printf("%s %d %d"
619 			, ms_rate_control_action_type_name(action->type)
620 			, action->value
621 			, (int)bw
622 			);
623 
624 		objbase->on_action_suggested(objbase->on_action_suggested_user_pointer, datac, (const char**)data);
625 
626 		for (i=0;i<datac;++i){
627 			ms_free(data[i]);
628 		}
629 	}
630 }
631 
stateful_analyzer_has_improved(MSQosAnalyzer * objbase)632 static bool_t stateful_analyzer_has_improved(MSQosAnalyzer *objbase){
633 	/*never tell the controller that situation has improved to avoid 'Stable' state
634 	which is not necessary for this analyzer*/
635 	return FALSE;
636 }
637 
stateful_analyzer_update(MSQosAnalyzer * objbase)638 static void stateful_analyzer_update(MSQosAnalyzer *objbase){
639 	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
640 	static time_t last_measure;
641 
642 	/* Every seconds, save the bandwidth used. This is needed to know how much
643 	bandwidth was used when receiving a receiver report. Since the report contains
644 	the "last sequence number", it allows us to precisely know which interval to
645 	consider */
646 	if (last_measure != ms_time(0)){
647 		obj->upload_bandwidth_count++;
648 		obj->upload_bandwidth_sum+=rtp_session_get_send_bandwidth(obj->session)/1000.0;
649 
650 		/* Save bandwidth used at this time */
651 		obj->upload_bandwidth[obj->upload_bandwidth_cur].seq_number = rtp_session_get_seq_number(obj->session);
652 		obj->upload_bandwidth[obj->upload_bandwidth_cur].up_bandwidth = rtp_session_get_send_bandwidth(obj->session)/1000.0f;
653 		obj->upload_bandwidth_cur = (obj->upload_bandwidth_cur+1)%BW_HISTORY;
654 	}
655 	last_measure = ms_time(0);
656 
657 	if (obj->burst_duration_ms>0){
658 		switch (obj->burst_state){
659 		case MSStatefulQosAnalyzerBurstEnable:{
660 			obj->burst_state=MSStatefulQosAnalyzerBurstInProgress;
661 			ortp_gettimeofday(&obj->start_time, NULL);
662 			rtp_session_set_duplication_ratio(obj->session, (float)obj->burst_ratio);
663 			BCTBX_NO_BREAK; /*intentionally no break*/
664 		} case MSStatefulQosAnalyzerBurstInProgress: {
665 			struct timeval now;
666 			float elapsed;
667 
668 			ortp_gettimeofday(&now,NULL);
669 			elapsed=((now.tv_sec-obj->start_time.tv_sec)*1000.0f) +  ((now.tv_usec-obj->start_time.tv_usec)/1000.0f);
670 
671 			if (elapsed > obj->burst_duration_ms){
672 				obj->burst_state=MSStatefulQosAnalyzerBurstDisable;
673 				rtp_session_set_duplication_ratio(obj->session, 0);
674 			}
675 			BCTBX_NO_BREAK; /*intentionally no break*/
676 		} case MSStatefulQosAnalyzerBurstDisable: {
677 		}
678 		}
679 	}
680 }
681 
stateful_analyzer_uninit(MSQosAnalyzer * objbase)682 static void stateful_analyzer_uninit(MSQosAnalyzer *objbase){
683 	MSStatefulQosAnalyzer *obj=(MSStatefulQosAnalyzer*)objbase;
684 	bctbx_list_for_each(obj->rtcpstatspoint, ms_free);
685 	bctbx_list_free(obj->rtcpstatspoint);
686 }
687 
688 static MSQosAnalyzerDesc stateful_analyzer_desc={
689 	stateful_analyzer_process_rtcp,
690 	stateful_analyzer_suggest_action,
691 	stateful_analyzer_has_improved,
692 	stateful_analyzer_update,
693 	stateful_analyzer_uninit,
694 };
695 
ms_stateful_qos_analyzer_new(RtpSession * session)696 MSQosAnalyzer * ms_stateful_qos_analyzer_new(RtpSession *session){
697 	MSStatefulQosAnalyzer *obj=ms_new0(MSStatefulQosAnalyzer,1);
698 	obj->session=session;
699 	obj->parent.desc=&stateful_analyzer_desc;
700 	obj->parent.type=MSQosAnalyzerAlgorithmStateful;
701 	obj->parent.lre=ortp_loss_rate_estimator_new(LOSS_RATE_MIN_INTERVAL, LOSS_RATE_MIN_TIME, session);
702 
703 	/*burst period will float the upload bandwidth assuming 5 sec RTCP reports interval*/
704 	obj->burst_duration_ms=1000;
705 	obj->burst_ratio=9;
706 	return (MSQosAnalyzer*)obj;
707 }
708