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