Skip to content

Commit 17c86dc

Browse files
kharwellgtjoseph
authored andcommitted
res_rtp_asterisk: Fix standard deviation calculation
For some input to the standard deviation algorithm extremely large, and wrong numbers were being calculated. This patch uses a new formula for correctly calculating both the running mean and standard deviation for the given inputs. ASTERISK-29364 #close Change-Id: Ibc6e18be41c28bed3fde06d612607acc3fbd621f
1 parent 0ad1ff8 commit 17c86dc

File tree

1 file changed

+70
-102
lines changed

1 file changed

+70
-102
lines changed

res/res_rtp_asterisk.c

Lines changed: 70 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ struct ast_rtp {
384384
unsigned int txcount; /*!< How many packets have we sent? */
385385
unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/
386386
unsigned int cycles; /*!< Shifted count of sequence number cycles */
387-
double rxjitter; /*!< Interarrival jitter at the moment in seconds */
387+
double rxjitter; /*!< Interarrival jitter at the moment in seconds to be reported */
388388
double rxtransit; /*!< Relative transit time for previous packet */
389389
struct ast_format *lasttxformat;
390390
struct ast_format *lastrxformat;
@@ -511,34 +511,36 @@ struct ast_rtcp {
511511
unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */
512512
unsigned int reported_lost; /*!< Reported lost packets in their RR */
513513

514-
double reported_maxjitter;
515-
double reported_minjitter;
516-
double reported_normdev_jitter;
517-
double reported_stdev_jitter;
518-
unsigned int reported_jitter_count;
519-
520-
double reported_maxlost;
521-
double reported_minlost;
522-
double reported_normdev_lost;
523-
double reported_stdev_lost;
524-
525-
double rxlost;
526-
double maxrxlost;
527-
double minrxlost;
528-
double normdev_rxlost;
529-
double stdev_rxlost;
530-
unsigned int rxlost_count;
531-
532-
double maxrxjitter;
533-
double minrxjitter;
534-
double normdev_rxjitter;
535-
double stdev_rxjitter;
536-
unsigned int rxjitter_count;
537-
double maxrtt;
538-
double minrtt;
539-
double normdevrtt;
540-
double stdevrtt;
541-
unsigned int rtt_count;
514+
double reported_maxjitter; /*!< Maximum reported interarrival jitter */
515+
double reported_minjitter; /*!< Minimum reported interarrival jitter */
516+
double reported_normdev_jitter; /*!< Mean of reported interarrival jitter */
517+
double reported_stdev_jitter; /*!< Standard deviation of reported interarrival jitter */
518+
unsigned int reported_jitter_count; /*!< Reported interarrival jitter count */
519+
520+
double reported_maxlost; /*!< Maximum reported packets lost */
521+
double reported_minlost; /*!< Minimum reported packets lost */
522+
double reported_normdev_lost; /*!< Mean of reported packets lost */
523+
double reported_stdev_lost; /*!< Standard deviation of reported packets lost */
524+
unsigned int reported_lost_count; /*!< Reported packets lost count */
525+
526+
double rxlost; /*!< Calculated number of lost packets since last report */
527+
double maxrxlost; /*!< Maximum calculated lost number of packets between reports */
528+
double minrxlost; /*!< Minimum calculated lost number of packets between reports */
529+
double normdev_rxlost; /*!< Mean of calculated lost packets between reports */
530+
double stdev_rxlost; /*!< Standard deviation of calculated lost packets between reports */
531+
unsigned int rxlost_count; /*!< Calculated lost packets sample count */
532+
533+
double maxrxjitter; /*!< Maximum of calculated interarrival jitter */
534+
double minrxjitter; /*!< Minimum of calculated interarrival jitter */
535+
double normdev_rxjitter; /*!< Mean of calculated interarrival jitter */
536+
double stdev_rxjitter; /*!< Standard deviation of calculated interarrival jitter */
537+
unsigned int rxjitter_count; /*!< Calculated interarrival jitter count */
538+
539+
double maxrtt; /*!< Maximum of calculated round trip time */
540+
double minrtt; /*!< Minimum of calculated round trip time */
541+
double normdevrtt; /*!< Mean of calculated round trip time */
542+
double stdevrtt; /*!< Standard deviation of calculated round trip time */
543+
unsigned int rtt_count; /*!< Calculated round trip time count */
542544

543545
/* VP8: sequence number for the RTCP FIR FCI */
544546
int firseq;
@@ -3317,49 +3319,32 @@ static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp)
33173319
return interval;
33183320
}
33193321

3320-
/*! \brief Calculate normal deviation */
3321-
static double normdev_compute(double normdev, double sample, unsigned int sample_count)
3322+
static void calc_mean_and_standard_deviation(double new_sample, double *mean, double *std_dev, unsigned int *count)
33223323
{
3323-
normdev = normdev * sample_count + sample;
3324-
sample_count++;
3324+
double delta1;
3325+
double delta2;
33253326

3326-
/*
3327-
It's possible the sample_count hits the maximum value and back to 0.
3328-
Set to 1 to prevent the divide by zero crash if the sample_count is 0.
3329-
*/
3330-
if (sample_count == 0) {
3331-
sample_count = 1;
3332-
}
3333-
3334-
return normdev / sample_count;
3335-
}
3327+
/* First convert the standard deviation back into a sum of squares. */
3328+
double last_sum_of_squares = (*std_dev) * (*std_dev) * (*count ?: 1);
33363329

3337-
static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count)
3338-
{
3339-
/*
3340-
for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf
3341-
return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1));
3342-
we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute
3343-
optimized formula
3344-
*/
3345-
#define SQUARE(x) ((x) * (x))
3346-
3347-
stddev = sample_count * stddev;
3348-
sample_count++;
3330+
if (++(*count) == 0) {
3331+
/* Avoid potential divide by zero on an overflow */
3332+
*count = 1;
3333+
}
33493334

33503335
/*
3351-
It's possible the sample_count hits the maximum value and back to 0.
3352-
Set to 1 to prevent the divide by zero crash if the sample_count is 0.
3336+
* Below is an implementation of Welford's online algorithm [1] for calculating
3337+
* mean and variance in a single pass.
3338+
*
3339+
* [1] https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
33533340
*/
3354-
if (sample_count == 0) {
3355-
sample_count = 1;
3356-
}
33573341

3358-
return stddev +
3359-
( sample_count * SQUARE( (sample - normdev) / sample_count ) ) +
3360-
( SQUARE(sample - normdev_curent) / sample_count );
3342+
delta1 = new_sample - *mean;
3343+
*mean += (delta1 / *count);
3344+
delta2 = new_sample - *mean;
33613345

3362-
#undef SQUARE
3346+
/* Now calculate the new variance, and subsequent standard deviation */
3347+
*std_dev = sqrt((last_sum_of_squares + (delta1 * delta2)) / *count);
33633348
}
33643349

33653350
static int create_new_socket(const char *type, int af)
@@ -4434,7 +4419,6 @@ static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
44344419
unsigned int expected_packets;
44354420
unsigned int expected_interval;
44364421
unsigned int received_interval;
4437-
double rxlost_current;
44384422
int lost_interval;
44394423

44404424
/* Compute statistics */
@@ -4464,6 +4448,13 @@ static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
44644448
/* Update RTCP statistics */
44654449
rtp->rtcp->received_prior = rtp->rxcount;
44664450
rtp->rtcp->expected_prior = expected_packets;
4451+
4452+
/*
4453+
* While rxlost represents the number of packets lost since the last report was sent, for
4454+
* the calculations below it should be thought of as a single sample. Thus min/max are the
4455+
* lowest/highest sample value seen, and the mean is the average number of packets lost
4456+
* between each report. As such rxlost_count only needs to be incremented per report.
4457+
*/
44674458
if (lost_interval <= 0) {
44684459
rtp->rtcp->rxlost = 0;
44694460
} else {
@@ -4478,16 +4469,9 @@ static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
44784469
if (lost_interval > rtp->rtcp->maxrxlost) {
44794470
rtp->rtcp->maxrxlost = rtp->rtcp->rxlost;
44804471
}
4481-
rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost,
4482-
rtp->rtcp->rxlost,
4483-
rtp->rtcp->rxlost_count);
4484-
rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost,
4485-
rtp->rtcp->rxlost,
4486-
rtp->rtcp->normdev_rxlost,
4487-
rxlost_current,
4488-
rtp->rtcp->rxlost_count);
4489-
rtp->rtcp->normdev_rxlost = rxlost_current;
4490-
rtp->rtcp->rxlost_count++;
4472+
4473+
calc_mean_and_standard_deviation(rtp->rtcp->rxlost, &rtp->rtcp->normdev_rxlost,
4474+
&rtp->rtcp->stdev_rxlost, &rtp->rtcp->rxlost_count);
44914475
}
44924476

44934477
static int ast_rtcp_generate_report(struct ast_rtp_instance *instance, unsigned char *rtcpheader,
@@ -5423,7 +5407,6 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
54235407
double prog;
54245408
int rate = ast_rtp_get_rate(rtp->f.subclass.format);
54255409

5426-
double normdev_rxjitter_current;
54275410
if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) {
54285411
gettimeofday(&rtp->rxcore, NULL);
54295412
rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000;
@@ -5458,11 +5441,8 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
54585441
if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter)
54595442
rtp->rtcp->minrxjitter = rtp->rxjitter;
54605443

5461-
normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count);
5462-
rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count);
5463-
5464-
rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current;
5465-
rtp->rtcp->rxjitter_count++;
5444+
calc_mean_and_standard_deviation(rtp->rxjitter, &rtp->rtcp->normdev_rxjitter,
5445+
&rtp->rtcp->stdev_rxjitter, &rtp->rtcp->rxjitter_count);
54665446
}
54675447
}
54685448

@@ -5778,7 +5758,6 @@ static int update_rtt_stats(struct ast_rtp *rtp, unsigned int lsr, unsigned int
57785758
unsigned int rtt_lsw;
57795759
unsigned int lsr_a;
57805760
unsigned int rtt;
5781-
double normdevrtt_current;
57825761

57835762
gettimeofday(&now, NULL);
57845763
timeval2ntp(now, &msw, &lsw);
@@ -5815,16 +5794,8 @@ static int update_rtt_stats(struct ast_rtp *rtp, unsigned int lsr, unsigned int
58155794
rtp->rtcp->maxrtt = rtp->rtcp->rtt;
58165795
}
58175796

5818-
normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt,
5819-
rtp->rtcp->rtt,
5820-
rtp->rtcp->rtt_count);
5821-
rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt,
5822-
rtp->rtcp->rtt,
5823-
rtp->rtcp->normdevrtt,
5824-
normdevrtt_current,
5825-
rtp->rtcp->rtt_count);
5826-
rtp->rtcp->normdevrtt = normdevrtt_current;
5827-
rtp->rtcp->rtt_count++;
5797+
calc_mean_and_standard_deviation(rtp->rtcp->rtt, &rtp->rtcp->normdevrtt,
5798+
&rtp->rtcp->stdevrtt, &rtp->rtcp->rtt_count);
58285799

58295800
return 0;
58305801
}
@@ -5836,7 +5807,6 @@ static int update_rtt_stats(struct ast_rtp *rtp, unsigned int lsr, unsigned int
58365807
static void update_jitter_stats(struct ast_rtp *rtp, unsigned int ia_jitter)
58375808
{
58385809
double reported_jitter;
5839-
double reported_normdev_jitter_current;
58405810

58415811
rtp->rtcp->reported_jitter = ia_jitter;
58425812
reported_jitter = (double) rtp->rtcp->reported_jitter;
@@ -5849,9 +5819,9 @@ static void update_jitter_stats(struct ast_rtp *rtp, unsigned int ia_jitter)
58495819
if (reported_jitter > rtp->rtcp->reported_maxjitter) {
58505820
rtp->rtcp->reported_maxjitter = reported_jitter;
58515821
}
5852-
reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count);
5853-
rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count);
5854-
rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current;
5822+
5823+
calc_mean_and_standard_deviation(reported_jitter, &rtp->rtcp->reported_normdev_jitter,
5824+
&rtp->rtcp->reported_stdev_jitter, &rtp->rtcp->reported_jitter_count);
58555825
}
58565826

58575827
/*!
@@ -5861,11 +5831,10 @@ static void update_jitter_stats(struct ast_rtp *rtp, unsigned int ia_jitter)
58615831
static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
58625832
{
58635833
double reported_lost;
5864-
double reported_normdev_lost_current;
58655834

58665835
rtp->rtcp->reported_lost = lost_packets;
58675836
reported_lost = (double)rtp->rtcp->reported_lost;
5868-
if (rtp->rtcp->reported_jitter_count == 0) {
5837+
if (rtp->rtcp->reported_lost_count == 0) {
58695838
rtp->rtcp->reported_minlost = reported_lost;
58705839
}
58715840
if (reported_lost < rtp->rtcp->reported_minlost) {
@@ -5874,9 +5843,9 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
58745843
if (reported_lost > rtp->rtcp->reported_maxlost) {
58755844
rtp->rtcp->reported_maxlost = reported_lost;
58765845
}
5877-
reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count);
5878-
rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count);
5879-
rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
5846+
5847+
calc_mean_and_standard_deviation(reported_lost, &rtp->rtcp->reported_normdev_lost,
5848+
&rtp->rtcp->reported_stdev_lost, &rtp->rtcp->reported_lost_count);
58805849
}
58815850

58825851
/*! \pre instance is locked */
@@ -6449,7 +6418,6 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, s
64496418
}
64506419
update_jitter_stats(rtp, report_block->ia_jitter);
64516420
update_lost_stats(rtp, report_block->lost_count.packets);
6452-
rtp->rtcp->reported_jitter_count++;
64536421

64546422
if (rtcp_debug_test_addr(addr)) {
64556423
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);

0 commit comments

Comments
 (0)