mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-24 10:31:13 +00:00
1025 lines
29 KiB
C
1025 lines
29 KiB
C
/*
|
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
/*
|
|
* BwEstimator.c
|
|
*
|
|
* This file contains the code for the Bandwidth Estimator designed
|
|
* for iSAC.
|
|
*
|
|
*/
|
|
|
|
#include "bandwidth_estimator.h"
|
|
#include "settings.h"
|
|
#include "isac.h"
|
|
|
|
#include <math.h>
|
|
|
|
/* array of quantization levels for bottle neck info; Matlab code: */
|
|
/* sprintf('%4.1ff, ', logspace(log10(5000), log10(40000), 12)) */
|
|
static const float kQRateTableWb[12] =
|
|
{
|
|
10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f,
|
|
18859.8f, 20963.3f, 23301.4f, 25900.3f, 28789.0f, 32000.0f};
|
|
|
|
|
|
static const float kQRateTableSwb[24] =
|
|
{
|
|
10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f,
|
|
18859.8f, 20963.3f, 23153.1f, 25342.9f, 27532.7f, 29722.5f,
|
|
31912.3f, 34102.1f, 36291.9f, 38481.7f, 40671.4f, 42861.2f,
|
|
45051.0f, 47240.8f, 49430.6f, 51620.4f, 53810.2f, 56000.0f,
|
|
};
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32 WebRtcIsac_InitBandwidthEstimator(
|
|
BwEstimatorstr* bwest_str,
|
|
enum IsacSamplingRate encoderSampRate,
|
|
enum IsacSamplingRate decoderSampRate)
|
|
{
|
|
switch(encoderSampRate)
|
|
{
|
|
case kIsacWideband:
|
|
{
|
|
bwest_str->send_bw_avg = INIT_BN_EST_WB;
|
|
break;
|
|
}
|
|
case kIsacSuperWideband:
|
|
{
|
|
bwest_str->send_bw_avg = INIT_BN_EST_SWB;
|
|
break;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
switch(decoderSampRate)
|
|
{
|
|
case kIsacWideband:
|
|
{
|
|
bwest_str->prev_frame_length = INIT_FRAME_LEN_WB;
|
|
bwest_str->rec_bw_inv = 1.0f /
|
|
(INIT_BN_EST_WB + INIT_HDR_RATE_WB);
|
|
bwest_str->rec_bw = (WebRtc_Word32)INIT_BN_EST_WB;
|
|
bwest_str->rec_bw_avg_Q = INIT_BN_EST_WB;
|
|
bwest_str->rec_bw_avg = INIT_BN_EST_WB + INIT_HDR_RATE_WB;
|
|
bwest_str->rec_header_rate = INIT_HDR_RATE_WB;
|
|
break;
|
|
}
|
|
case kIsacSuperWideband:
|
|
{
|
|
bwest_str->prev_frame_length = INIT_FRAME_LEN_SWB;
|
|
bwest_str->rec_bw_inv = 1.0f /
|
|
(INIT_BN_EST_SWB + INIT_HDR_RATE_SWB);
|
|
bwest_str->rec_bw = (WebRtc_Word32)INIT_BN_EST_SWB;
|
|
bwest_str->rec_bw_avg_Q = INIT_BN_EST_SWB;
|
|
bwest_str->rec_bw_avg = INIT_BN_EST_SWB + INIT_HDR_RATE_SWB;
|
|
bwest_str->rec_header_rate = INIT_HDR_RATE_SWB;
|
|
break;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
bwest_str->prev_rec_rtp_number = 0;
|
|
bwest_str->prev_rec_arr_ts = 0;
|
|
bwest_str->prev_rec_send_ts = 0;
|
|
bwest_str->prev_rec_rtp_rate = 1.0f;
|
|
bwest_str->last_update_ts = 0;
|
|
bwest_str->last_reduction_ts = 0;
|
|
bwest_str->count_tot_updates_rec = -9;
|
|
bwest_str->rec_jitter = 10.0f;
|
|
bwest_str->rec_jitter_short_term = 0.0f;
|
|
bwest_str->rec_jitter_short_term_abs = 5.0f;
|
|
bwest_str->rec_max_delay = 10.0f;
|
|
bwest_str->rec_max_delay_avg_Q = 10.0f;
|
|
bwest_str->num_pkts_rec = 0;
|
|
|
|
bwest_str->send_max_delay_avg = 10.0f;
|
|
|
|
bwest_str->hsn_detect_rec = 0;
|
|
|
|
bwest_str->num_consec_rec_pkts_over_30k = 0;
|
|
|
|
bwest_str->hsn_detect_snd = 0;
|
|
|
|
bwest_str->num_consec_snt_pkts_over_30k = 0;
|
|
|
|
bwest_str->in_wait_period = 0;
|
|
|
|
bwest_str->change_to_WB = 0;
|
|
|
|
bwest_str->numConsecLatePkts = 0;
|
|
bwest_str->consecLatency = 0;
|
|
bwest_str->inWaitLatePkts = 0;
|
|
bwest_str->senderTimestamp = 0;
|
|
bwest_str->receiverTimestamp = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* This function updates both bottle neck rates */
|
|
/* Parameters: */
|
|
/* rtp_number - value from RTP packet, from NetEq */
|
|
/* frame length - length of signal frame in ms, from iSAC decoder */
|
|
/* send_ts - value in RTP header giving send time in samples */
|
|
/* arr_ts - value given by timeGetTime() time of arrival in samples of packet from NetEq */
|
|
/* pksize - size of packet in bytes, from NetEq */
|
|
/* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */
|
|
/* returns 0 if everything went fine, -1 otherwise */
|
|
WebRtc_Word16 WebRtcIsac_UpdateBandwidthEstimator(
|
|
BwEstimatorstr *bwest_str,
|
|
const WebRtc_UWord16 rtp_number,
|
|
const WebRtc_Word32 frame_length,
|
|
const WebRtc_UWord32 send_ts,
|
|
const WebRtc_UWord32 arr_ts,
|
|
const WebRtc_Word32 pksize
|
|
/*, const WebRtc_UWord16 Index*/)
|
|
{
|
|
float weight = 0.0f;
|
|
float curr_bw_inv = 0.0f;
|
|
float rec_rtp_rate;
|
|
float t_diff_proj;
|
|
float arr_ts_diff;
|
|
float send_ts_diff;
|
|
float arr_time_noise;
|
|
float arr_time_noise_abs;
|
|
|
|
float delay_correction_factor = 1;
|
|
float late_diff = 0.0f;
|
|
int immediate_set = 0;
|
|
int num_pkts_expected;
|
|
|
|
|
|
// We have to adjust the header-rate if the first packet has a
|
|
// frame-size different than the initialized value.
|
|
if ( frame_length != bwest_str->prev_frame_length )
|
|
{
|
|
bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f *
|
|
1000.0f / (float)frame_length; /* bits/s */
|
|
}
|
|
|
|
/* UPDATE ESTIMATES ON THIS SIDE */
|
|
/* compute far-side transmission rate */
|
|
rec_rtp_rate = ((float)pksize * 8.0f * 1000.0f / (float)frame_length) +
|
|
bwest_str->rec_header_rate;
|
|
// rec_rtp_rate packet bits/s + header bits/s
|
|
|
|
/* check for timer wrap-around */
|
|
if (arr_ts < bwest_str->prev_rec_arr_ts)
|
|
{
|
|
bwest_str->prev_rec_arr_ts = arr_ts;
|
|
bwest_str->last_update_ts = arr_ts;
|
|
bwest_str->last_reduction_ts = arr_ts + 3*FS;
|
|
bwest_str->num_pkts_rec = 0;
|
|
|
|
/* store frame length */
|
|
bwest_str->prev_frame_length = frame_length;
|
|
|
|
/* store far-side transmission rate */
|
|
bwest_str->prev_rec_rtp_rate = rec_rtp_rate;
|
|
|
|
/* store far-side RTP time stamp */
|
|
bwest_str->prev_rec_rtp_number = rtp_number;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bwest_str->num_pkts_rec++;
|
|
|
|
/* check that it's not one of the first 9 packets */
|
|
if ( bwest_str->count_tot_updates_rec > 0 )
|
|
{
|
|
if(bwest_str->in_wait_period > 0 )
|
|
{
|
|
bwest_str->in_wait_period--;
|
|
}
|
|
|
|
bwest_str->inWaitLatePkts -= ((bwest_str->inWaitLatePkts > 0)? 1:0);
|
|
send_ts_diff = (float)(send_ts - bwest_str->prev_rec_send_ts);
|
|
|
|
if (send_ts_diff <= (16 * frame_length)*2)
|
|
//doesn't allow for a dropped packet, not sure necessary to be
|
|
// that strict -DH
|
|
{
|
|
/* if not been updated for a long time, reduce the BN estimate */
|
|
if((WebRtc_UWord32)(arr_ts - bwest_str->last_update_ts) *
|
|
1000.0f / FS > 3000)
|
|
{
|
|
//how many frames should have been received since the last
|
|
// update if too many have been dropped or there have been
|
|
// big delays won't allow this reduction may no longer need
|
|
// the send_ts_diff here
|
|
num_pkts_expected = (int)(((float)(arr_ts -
|
|
bwest_str->last_update_ts) * 1000.0f /(float) FS) /
|
|
(float)frame_length);
|
|
|
|
if(((float)bwest_str->num_pkts_rec/(float)num_pkts_expected) >
|
|
0.9)
|
|
{
|
|
float inv_bitrate = (float) pow( 0.99995,
|
|
(double)((WebRtc_UWord32)(arr_ts -
|
|
bwest_str->last_reduction_ts)*1000.0f/FS) );
|
|
|
|
if ( inv_bitrate )
|
|
{
|
|
bwest_str->rec_bw_inv /= inv_bitrate;
|
|
|
|
//precautionary, likely never necessary
|
|
if (bwest_str->hsn_detect_snd &&
|
|
bwest_str->hsn_detect_rec)
|
|
{
|
|
if (bwest_str->rec_bw_inv > 0.000066f)
|
|
{
|
|
bwest_str->rec_bw_inv = 0.000066f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bwest_str->rec_bw_inv = 1.0f /
|
|
(INIT_BN_EST_WB + INIT_HDR_RATE_WB);
|
|
}
|
|
/* reset time-since-update counter */
|
|
bwest_str->last_reduction_ts = arr_ts;
|
|
}
|
|
else
|
|
//reset here?
|
|
{
|
|
bwest_str->last_reduction_ts = arr_ts + 3*FS;
|
|
bwest_str->last_update_ts = arr_ts;
|
|
bwest_str->num_pkts_rec = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bwest_str->last_reduction_ts = arr_ts + 3*FS;
|
|
bwest_str->last_update_ts = arr_ts;
|
|
bwest_str->num_pkts_rec = 0;
|
|
}
|
|
|
|
|
|
/* temporarily speed up adaptation if frame length has changed */
|
|
if ( frame_length != bwest_str->prev_frame_length )
|
|
{
|
|
bwest_str->count_tot_updates_rec = 10;
|
|
bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f *
|
|
1000.0f / (float)frame_length; /* bits/s */
|
|
|
|
bwest_str->rec_bw_inv = 1.0f /((float)bwest_str->rec_bw +
|
|
bwest_str->rec_header_rate);
|
|
}
|
|
|
|
////////////////////////
|
|
arr_ts_diff = (float)(arr_ts - bwest_str->prev_rec_arr_ts);
|
|
|
|
if (send_ts_diff > 0 )
|
|
{
|
|
late_diff = arr_ts_diff - send_ts_diff;
|
|
}
|
|
else
|
|
{
|
|
late_diff = arr_ts_diff - (float)(16 * frame_length);
|
|
}
|
|
|
|
if((late_diff > 0) && !bwest_str->inWaitLatePkts)
|
|
{
|
|
bwest_str->numConsecLatePkts++;
|
|
bwest_str->consecLatency += late_diff;
|
|
}
|
|
else
|
|
{
|
|
bwest_str->numConsecLatePkts = 0;
|
|
bwest_str->consecLatency = 0;
|
|
}
|
|
if(bwest_str->numConsecLatePkts > 50)
|
|
{
|
|
float latencyMs = bwest_str->consecLatency/(FS/1000);
|
|
float averageLatencyMs = latencyMs / bwest_str->numConsecLatePkts;
|
|
delay_correction_factor = frame_length / (frame_length + averageLatencyMs);
|
|
immediate_set = 1;
|
|
bwest_str->inWaitLatePkts = (WebRtc_Word16)((bwest_str->consecLatency/(FS/1000)) / 30);// + 150;
|
|
bwest_str->start_wait_period = arr_ts;
|
|
}
|
|
///////////////////////////////////////////////
|
|
|
|
|
|
|
|
/* update only if previous packet was not lost */
|
|
if ( rtp_number == bwest_str->prev_rec_rtp_number + 1 )
|
|
{
|
|
|
|
|
|
if (!(bwest_str->hsn_detect_snd && bwest_str->hsn_detect_rec))
|
|
{
|
|
if ((arr_ts_diff > (float)(16 * frame_length)))
|
|
{
|
|
//1/2 second
|
|
if ((late_diff > 8000.0f) && !bwest_str->in_wait_period)
|
|
{
|
|
delay_correction_factor = 0.7f;
|
|
bwest_str->in_wait_period = 55;
|
|
bwest_str->start_wait_period = arr_ts;
|
|
immediate_set = 1;
|
|
}
|
|
//320 ms
|
|
else if (late_diff > 5120.0f && !bwest_str->in_wait_period)
|
|
{
|
|
delay_correction_factor = 0.8f;
|
|
immediate_set = 1;
|
|
bwest_str->in_wait_period = 44;
|
|
bwest_str->start_wait_period = arr_ts;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ((bwest_str->prev_rec_rtp_rate > bwest_str->rec_bw_avg) &&
|
|
(rec_rtp_rate > bwest_str->rec_bw_avg) &&
|
|
!bwest_str->in_wait_period)
|
|
{
|
|
/* test if still in initiation period and increment counter */
|
|
if (bwest_str->count_tot_updates_rec++ > 99)
|
|
{
|
|
/* constant weight after initiation part */
|
|
weight = 0.01f;
|
|
}
|
|
else
|
|
{
|
|
/* weight decreases with number of updates */
|
|
weight = 1.0f / (float) bwest_str->count_tot_updates_rec;
|
|
}
|
|
/* Bottle Neck Estimation */
|
|
|
|
/* limit outliers */
|
|
/* if more than 25 ms too much */
|
|
if (arr_ts_diff > frame_length * FS/1000 + 400.0f)
|
|
{
|
|
// in samples, why 25ms??
|
|
arr_ts_diff = frame_length * FS/1000 + 400.0f;
|
|
}
|
|
if(arr_ts_diff < (frame_length * FS/1000) - 160.0f)
|
|
{
|
|
/* don't allow it to be less than frame rate - 10 ms */
|
|
arr_ts_diff = (float)frame_length * FS/1000 - 160.0f;
|
|
}
|
|
|
|
/* compute inverse receiving rate for last packet */
|
|
curr_bw_inv = arr_ts_diff / ((float)(pksize + HEADER_SIZE) *
|
|
8.0f * FS); // (180+35)*8*16000 = 27.5 Mbit....
|
|
|
|
|
|
if(curr_bw_inv <
|
|
(1.0f / (MAX_ISAC_BW + bwest_str->rec_header_rate)))
|
|
{
|
|
// don't allow inv rate to be larger than MAX
|
|
curr_bw_inv = (1.0f /
|
|
(MAX_ISAC_BW + bwest_str->rec_header_rate));
|
|
}
|
|
|
|
/* update bottle neck rate estimate */
|
|
bwest_str->rec_bw_inv = weight * curr_bw_inv +
|
|
(1.0f - weight) * bwest_str->rec_bw_inv;
|
|
|
|
/* reset time-since-update counter */
|
|
bwest_str->last_update_ts = arr_ts;
|
|
bwest_str->last_reduction_ts = arr_ts + 3 * FS;
|
|
bwest_str->num_pkts_rec = 0;
|
|
|
|
/* Jitter Estimation */
|
|
/* projected difference between arrival times */
|
|
t_diff_proj = ((float)(pksize + HEADER_SIZE) * 8.0f *
|
|
1000.0f) / bwest_str->rec_bw_avg;
|
|
|
|
|
|
// difference between projected and actual
|
|
// arrival time differences
|
|
arr_time_noise = (float)(arr_ts_diff*1000.0f/FS) -
|
|
t_diff_proj;
|
|
arr_time_noise_abs = (float) fabs( arr_time_noise );
|
|
|
|
/* long term averaged absolute jitter */
|
|
bwest_str->rec_jitter = weight * arr_time_noise_abs +
|
|
(1.0f - weight) * bwest_str->rec_jitter;
|
|
if (bwest_str->rec_jitter > 10.0f)
|
|
{
|
|
bwest_str->rec_jitter = 10.0f;
|
|
}
|
|
/* short term averaged absolute jitter */
|
|
bwest_str->rec_jitter_short_term_abs = 0.05f *
|
|
arr_time_noise_abs + 0.95f *
|
|
bwest_str->rec_jitter_short_term_abs;
|
|
|
|
/* short term averaged jitter */
|
|
bwest_str->rec_jitter_short_term = 0.05f * arr_time_noise +
|
|
0.95f * bwest_str->rec_jitter_short_term;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// reset time-since-update counter when
|
|
// receiving the first 9 packets
|
|
bwest_str->last_update_ts = arr_ts;
|
|
bwest_str->last_reduction_ts = arr_ts + 3*FS;
|
|
bwest_str->num_pkts_rec = 0;
|
|
|
|
bwest_str->count_tot_updates_rec++;
|
|
}
|
|
|
|
/* limit minimum bottle neck rate */
|
|
if (bwest_str->rec_bw_inv > 1.0f / ((float)MIN_ISAC_BW +
|
|
bwest_str->rec_header_rate))
|
|
{
|
|
bwest_str->rec_bw_inv = 1.0f / ((float)MIN_ISAC_BW +
|
|
bwest_str->rec_header_rate);
|
|
}
|
|
|
|
// limit maximum bitrate
|
|
if (bwest_str->rec_bw_inv < 1.0f / ((float)MAX_ISAC_BW +
|
|
bwest_str->rec_header_rate))
|
|
{
|
|
bwest_str->rec_bw_inv = 1.0f / ((float)MAX_ISAC_BW +
|
|
bwest_str->rec_header_rate);
|
|
}
|
|
|
|
/* store frame length */
|
|
bwest_str->prev_frame_length = frame_length;
|
|
|
|
/* store far-side transmission rate */
|
|
bwest_str->prev_rec_rtp_rate = rec_rtp_rate;
|
|
|
|
/* store far-side RTP time stamp */
|
|
bwest_str->prev_rec_rtp_number = rtp_number;
|
|
|
|
// Replace bwest_str->rec_max_delay by the new
|
|
// value (atomic operation)
|
|
bwest_str->rec_max_delay = 3.0f * bwest_str->rec_jitter;
|
|
|
|
/* store send and arrival time stamp */
|
|
bwest_str->prev_rec_arr_ts = arr_ts ;
|
|
bwest_str->prev_rec_send_ts = send_ts;
|
|
|
|
/* Replace bwest_str->rec_bw by the new value (atomic operation) */
|
|
bwest_str->rec_bw = (WebRtc_Word32)(1.0f / bwest_str->rec_bw_inv -
|
|
bwest_str->rec_header_rate);
|
|
|
|
if (immediate_set)
|
|
{
|
|
bwest_str->rec_bw = (WebRtc_Word32) (delay_correction_factor *
|
|
(float) bwest_str->rec_bw);
|
|
|
|
if (bwest_str->rec_bw < (WebRtc_Word32) MIN_ISAC_BW)
|
|
{
|
|
bwest_str->rec_bw = (WebRtc_Word32) MIN_ISAC_BW;
|
|
}
|
|
|
|
bwest_str->rec_bw_avg = bwest_str->rec_bw +
|
|
bwest_str->rec_header_rate;
|
|
|
|
bwest_str->rec_bw_avg_Q = (float) bwest_str->rec_bw;
|
|
|
|
bwest_str->rec_jitter_short_term = 0.0f;
|
|
|
|
bwest_str->rec_bw_inv = 1.0f / (bwest_str->rec_bw +
|
|
bwest_str->rec_header_rate);
|
|
|
|
bwest_str->count_tot_updates_rec = 1;
|
|
|
|
immediate_set = 0;
|
|
bwest_str->consecLatency = 0;
|
|
bwest_str->numConsecLatePkts = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* This function updates the send bottle neck rate */
|
|
/* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */
|
|
/* returns 0 if everything went fine, -1 otherwise */
|
|
WebRtc_Word16 WebRtcIsac_UpdateUplinkBwImpl(
|
|
BwEstimatorstr* bwest_str,
|
|
WebRtc_Word16 index,
|
|
enum IsacSamplingRate encoderSamplingFreq)
|
|
{
|
|
if((index < 0) || (index > 23))
|
|
{
|
|
return -ISAC_RANGE_ERROR_BW_ESTIMATOR;
|
|
}
|
|
|
|
/* UPDATE ESTIMATES FROM OTHER SIDE */
|
|
if(encoderSamplingFreq == kIsacWideband)
|
|
{
|
|
if(index > 11)
|
|
{
|
|
index -= 12;
|
|
/* compute the jitter estimate as decoded on the other side */
|
|
bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
|
|
0.1f * (float)MAX_ISAC_MD;
|
|
}
|
|
else
|
|
{
|
|
/* compute the jitter estimate as decoded on the other side */
|
|
bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
|
|
0.1f * (float)MIN_ISAC_MD;
|
|
}
|
|
|
|
/* compute the BN estimate as decoded on the other side */
|
|
bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg +
|
|
0.1f * kQRateTableWb[index];
|
|
}
|
|
else
|
|
{
|
|
/* compute the BN estimate as decoded on the other side */
|
|
bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg +
|
|
0.1f * kQRateTableSwb[index];
|
|
}
|
|
|
|
if (bwest_str->send_bw_avg > (float) 28000 && !bwest_str->hsn_detect_snd)
|
|
{
|
|
bwest_str->num_consec_snt_pkts_over_30k++;
|
|
|
|
if (bwest_str->num_consec_snt_pkts_over_30k >= 66)
|
|
{
|
|
//approx 2 seconds with 30ms frames
|
|
bwest_str->hsn_detect_snd = 1;
|
|
}
|
|
}
|
|
else if (!bwest_str->hsn_detect_snd)
|
|
{
|
|
bwest_str->num_consec_snt_pkts_over_30k = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// called when there is upper-band bit-stream to update jitter
|
|
// statistics.
|
|
WebRtc_Word16 WebRtcIsac_UpdateUplinkJitter(
|
|
BwEstimatorstr* bwest_str,
|
|
WebRtc_Word32 index)
|
|
{
|
|
if((index < 0) || (index > 23))
|
|
{
|
|
return -ISAC_RANGE_ERROR_BW_ESTIMATOR;
|
|
}
|
|
|
|
if(index > 0)
|
|
{
|
|
/* compute the jitter estimate as decoded on the other side */
|
|
bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
|
|
0.1f * (float)MAX_ISAC_MD;
|
|
}
|
|
else
|
|
{
|
|
/* compute the jitter estimate as decoded on the other side */
|
|
bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg +
|
|
0.1f * (float)MIN_ISAC_MD;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// Returns the bandwidth/jitter estimation code (integer 0...23)
|
|
// to put in the sending iSAC payload
|
|
WebRtc_UWord16
|
|
WebRtcIsac_GetDownlinkBwJitIndexImpl(
|
|
BwEstimatorstr* bwest_str,
|
|
WebRtc_Word16* bottleneckIndex,
|
|
WebRtc_Word16* jitterInfo,
|
|
enum IsacSamplingRate decoderSamplingFreq)
|
|
{
|
|
float MaxDelay;
|
|
//WebRtc_UWord16 MaxDelayBit;
|
|
|
|
float rate;
|
|
float r;
|
|
float e1, e2;
|
|
const float weight = 0.1f;
|
|
const float* ptrQuantizationTable;
|
|
WebRtc_Word16 addJitterInfo;
|
|
WebRtc_Word16 minInd;
|
|
WebRtc_Word16 maxInd;
|
|
WebRtc_Word16 midInd;
|
|
|
|
/* Get Max Delay Bit */
|
|
/* get unquantized max delay */
|
|
MaxDelay = (float)WebRtcIsac_GetDownlinkMaxDelay(bwest_str);
|
|
|
|
if ( ((1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight *
|
|
MAX_ISAC_MD - MaxDelay) > (MaxDelay - (1.f-weight) *
|
|
bwest_str->rec_max_delay_avg_Q - weight * MIN_ISAC_MD) )
|
|
{
|
|
jitterInfo[0] = 0;
|
|
/* update quantized average */
|
|
bwest_str->rec_max_delay_avg_Q =
|
|
(1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight *
|
|
(float)MIN_ISAC_MD;
|
|
}
|
|
else
|
|
{
|
|
jitterInfo[0] = 1;
|
|
/* update quantized average */
|
|
bwest_str->rec_max_delay_avg_Q =
|
|
(1.f-weight) * bwest_str->rec_max_delay_avg_Q + weight *
|
|
(float)MAX_ISAC_MD;
|
|
}
|
|
|
|
// Get unquantized rate.
|
|
rate = (float)WebRtcIsac_GetDownlinkBandwidth(bwest_str);
|
|
|
|
/* Get Rate Index */
|
|
if(decoderSamplingFreq == kIsacWideband)
|
|
{
|
|
ptrQuantizationTable = kQRateTableWb;
|
|
addJitterInfo = 1;
|
|
maxInd = 11;
|
|
}
|
|
else
|
|
{
|
|
ptrQuantizationTable = kQRateTableSwb;
|
|
addJitterInfo = 0;
|
|
maxInd = 23;
|
|
}
|
|
|
|
minInd = 0;
|
|
while(maxInd > minInd + 1)
|
|
{
|
|
midInd = (maxInd + minInd) >> 1;
|
|
if(rate > ptrQuantizationTable[midInd])
|
|
{
|
|
minInd = midInd;
|
|
}
|
|
else
|
|
{
|
|
maxInd = midInd;
|
|
}
|
|
}
|
|
// Chose the index which gives results an average which is closest
|
|
// to rate
|
|
r = (1 - weight) * bwest_str->rec_bw_avg_Q - rate;
|
|
e1 = weight * ptrQuantizationTable[minInd] + r;
|
|
e2 = weight * ptrQuantizationTable[maxInd] + r;
|
|
e1 = (e1 > 0)? e1:-e1;
|
|
e2 = (e2 > 0)? e2:-e2;
|
|
if(e1 < e2)
|
|
{
|
|
bottleneckIndex[0] = minInd;
|
|
}
|
|
else
|
|
{
|
|
bottleneckIndex[0] = maxInd;
|
|
}
|
|
|
|
bwest_str->rec_bw_avg_Q = (1 - weight) * bwest_str->rec_bw_avg_Q +
|
|
weight * ptrQuantizationTable[bottleneckIndex[0]];
|
|
bottleneckIndex[0] += jitterInfo[0] * 12 * addJitterInfo;
|
|
|
|
bwest_str->rec_bw_avg = (1 - weight) * bwest_str->rec_bw_avg + weight *
|
|
(rate + bwest_str->rec_header_rate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* get the bottle neck rate from far side to here, as estimated on this side */
|
|
WebRtc_Word32 WebRtcIsac_GetDownlinkBandwidth( const BwEstimatorstr *bwest_str)
|
|
{
|
|
WebRtc_Word32 rec_bw;
|
|
float jitter_sign;
|
|
float bw_adjust;
|
|
|
|
/* create a value between -1.0 and 1.0 indicating "average sign" of jitter */
|
|
jitter_sign = bwest_str->rec_jitter_short_term /
|
|
bwest_str->rec_jitter_short_term_abs;
|
|
|
|
/* adjust bw proportionally to negative average jitter sign */
|
|
bw_adjust = 1.0f - jitter_sign * (0.15f + 0.15f * jitter_sign * jitter_sign);
|
|
|
|
/* adjust Rate if jitter sign is mostly constant */
|
|
rec_bw = (WebRtc_Word32)(bwest_str->rec_bw * bw_adjust);
|
|
|
|
/* limit range of bottle neck rate */
|
|
if (rec_bw < MIN_ISAC_BW)
|
|
{
|
|
rec_bw = MIN_ISAC_BW;
|
|
}
|
|
else if (rec_bw > MAX_ISAC_BW)
|
|
{
|
|
rec_bw = MAX_ISAC_BW;
|
|
}
|
|
return rec_bw;
|
|
}
|
|
|
|
/* Returns the max delay (in ms) */
|
|
WebRtc_Word32
|
|
WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str)
|
|
{
|
|
WebRtc_Word32 rec_max_delay;
|
|
|
|
rec_max_delay = (WebRtc_Word32)(bwest_str->rec_max_delay);
|
|
|
|
/* limit range of jitter estimate */
|
|
if (rec_max_delay < MIN_ISAC_MD)
|
|
{
|
|
rec_max_delay = MIN_ISAC_MD;
|
|
}
|
|
else if (rec_max_delay > MAX_ISAC_MD)
|
|
{
|
|
rec_max_delay = MAX_ISAC_MD;
|
|
}
|
|
return rec_max_delay;
|
|
}
|
|
|
|
/* get the bottle neck rate from here to far side, as estimated by far side */
|
|
void
|
|
WebRtcIsac_GetUplinkBandwidth(
|
|
const BwEstimatorstr* bwest_str,
|
|
WebRtc_Word32* bitRate)
|
|
{
|
|
/* limit range of bottle neck rate */
|
|
if (bwest_str->send_bw_avg < MIN_ISAC_BW)
|
|
{
|
|
*bitRate = MIN_ISAC_BW;
|
|
}
|
|
else if (bwest_str->send_bw_avg > MAX_ISAC_BW)
|
|
{
|
|
*bitRate = MAX_ISAC_BW;
|
|
}
|
|
else
|
|
{
|
|
*bitRate = (WebRtc_Word32)(bwest_str->send_bw_avg);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Returns the max delay value from the other side in ms */
|
|
WebRtc_Word32
|
|
WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr *bwest_str)
|
|
{
|
|
WebRtc_Word32 send_max_delay;
|
|
|
|
send_max_delay = (WebRtc_Word32)(bwest_str->send_max_delay_avg);
|
|
|
|
/* limit range of jitter estimate */
|
|
if (send_max_delay < MIN_ISAC_MD)
|
|
{
|
|
send_max_delay = MIN_ISAC_MD;
|
|
}
|
|
else if (send_max_delay > MAX_ISAC_MD)
|
|
{
|
|
send_max_delay = MAX_ISAC_MD;
|
|
}
|
|
return send_max_delay;
|
|
}
|
|
|
|
|
|
/*
|
|
* update long-term average bitrate and amount of data in buffer
|
|
* returns minimum payload size (bytes)
|
|
*/
|
|
int WebRtcIsac_GetMinBytes(
|
|
RateModel* State,
|
|
int StreamSize, /* bytes in bitstream */
|
|
const int FrameSamples, /* samples per frame */
|
|
const double BottleNeck, /* bottle neck rate; excl headers (bps) */
|
|
const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */
|
|
enum ISACBandwidth bandwidth
|
|
/*,WebRtc_Word16 frequentLargePackets*/)
|
|
{
|
|
double MinRate = 0.0;
|
|
int MinBytes;
|
|
double TransmissionTime;
|
|
int burstInterval = BURST_INTERVAL;
|
|
|
|
// first 10 packets @ low rate, then INIT_BURST_LEN packets @
|
|
// fixed rate of INIT_RATE bps
|
|
if (State->InitCounter > 0)
|
|
{
|
|
if (State->InitCounter-- <= INIT_BURST_LEN)
|
|
{
|
|
if(bandwidth == isac8kHz)
|
|
{
|
|
MinRate = INIT_RATE_WB;
|
|
}
|
|
else
|
|
{
|
|
MinRate = INIT_RATE_SWB;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MinRate = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* handle burst */
|
|
if (State->BurstCounter)
|
|
{
|
|
if (State->StillBuffered < (1.0 - 1.0/BURST_LEN) * DelayBuildUp)
|
|
{
|
|
/* max bps derived from BottleNeck and DelayBuildUp values */
|
|
MinRate = (1.0 + (FS/1000) * DelayBuildUp /
|
|
(double)(BURST_LEN * FrameSamples)) * BottleNeck;
|
|
}
|
|
else
|
|
{
|
|
// max bps derived from StillBuffered and DelayBuildUp
|
|
// values
|
|
MinRate = (1.0 + (FS/1000) * (DelayBuildUp -
|
|
State->StillBuffered) / (double)FrameSamples) * BottleNeck;
|
|
if (MinRate < 1.04 * BottleNeck)
|
|
{
|
|
MinRate = 1.04 * BottleNeck;
|
|
}
|
|
}
|
|
State->BurstCounter--;
|
|
}
|
|
}
|
|
|
|
|
|
/* convert rate from bits/second to bytes/packet */
|
|
MinBytes = (int) (MinRate * FrameSamples / (8.0 * FS));
|
|
|
|
/* StreamSize will be adjusted if less than MinBytes */
|
|
if (StreamSize < MinBytes)
|
|
{
|
|
StreamSize = MinBytes;
|
|
}
|
|
|
|
/* keep track of when bottle neck was last exceeded by at least 1% */
|
|
if (StreamSize * 8.0 * FS / FrameSamples > 1.01 * BottleNeck) {
|
|
if (State->PrevExceed) {
|
|
/* bottle_neck exceded twice in a row, decrease ExceedAgo */
|
|
State->ExceedAgo -= /*BURST_INTERVAL*/ burstInterval / (BURST_LEN - 1);
|
|
if (State->ExceedAgo < 0)
|
|
State->ExceedAgo = 0;
|
|
}
|
|
else
|
|
{
|
|
State->ExceedAgo += (FrameSamples * 1000) / FS; /* ms */
|
|
State->PrevExceed = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
State->PrevExceed = 0;
|
|
State->ExceedAgo += (FrameSamples * 1000) / FS; /* ms */
|
|
}
|
|
|
|
/* set burst flag if bottle neck not exceeded for long time */
|
|
if ((State->ExceedAgo > burstInterval) &&
|
|
(State->BurstCounter == 0))
|
|
{
|
|
if (State->PrevExceed)
|
|
{
|
|
State->BurstCounter = BURST_LEN - 1;
|
|
}
|
|
else
|
|
{
|
|
State->BurstCounter = BURST_LEN;
|
|
}
|
|
}
|
|
|
|
|
|
/* Update buffer delay */
|
|
TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck; /* ms */
|
|
State->StillBuffered += TransmissionTime;
|
|
State->StillBuffered -= (FrameSamples * 1000) / FS; /* ms */
|
|
if (State->StillBuffered < 0.0)
|
|
{
|
|
State->StillBuffered = 0.0;
|
|
}
|
|
|
|
return MinBytes;
|
|
}
|
|
|
|
|
|
/*
|
|
* update long-term average bitrate and amount of data in buffer
|
|
*/
|
|
void WebRtcIsac_UpdateRateModel(
|
|
RateModel *State,
|
|
int StreamSize, /* bytes in bitstream */
|
|
const int FrameSamples, /* samples per frame */
|
|
const double BottleNeck) /* bottle neck rate; excl headers (bps) */
|
|
{
|
|
double TransmissionTime;
|
|
|
|
/* avoid the initial "high-rate" burst */
|
|
State->InitCounter = 0;
|
|
|
|
/* Update buffer delay */
|
|
TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck; /* ms */
|
|
State->StillBuffered += TransmissionTime;
|
|
State->StillBuffered -= (FrameSamples * 1000) / FS; /* ms */
|
|
if (State->StillBuffered < 0.0)
|
|
State->StillBuffered = 0.0;
|
|
|
|
}
|
|
|
|
|
|
void WebRtcIsac_InitRateModel(
|
|
RateModel *State)
|
|
{
|
|
State->PrevExceed = 0; /* boolean */
|
|
State->ExceedAgo = 0; /* ms */
|
|
State->BurstCounter = 0; /* packets */
|
|
State->InitCounter = INIT_BURST_LEN + 10; /* packets */
|
|
State->StillBuffered = 1.0; /* ms */
|
|
}
|
|
|
|
int WebRtcIsac_GetNewFrameLength(
|
|
double bottle_neck,
|
|
int current_framesamples)
|
|
{
|
|
int new_framesamples;
|
|
|
|
const int Thld_20_30 = 20000;
|
|
|
|
//const int Thld_30_20 = 30000;
|
|
const int Thld_30_20 = 1000000; // disable 20 ms frames
|
|
|
|
const int Thld_30_60 = 18000;
|
|
//const int Thld_30_60 = 0; // disable 60 ms frames
|
|
|
|
const int Thld_60_30 = 27000;
|
|
|
|
|
|
new_framesamples = current_framesamples;
|
|
|
|
/* find new framelength */
|
|
switch(current_framesamples) {
|
|
case 320:
|
|
if (bottle_neck < Thld_20_30)
|
|
new_framesamples = 480;
|
|
break;
|
|
case 480:
|
|
if (bottle_neck < Thld_30_60)
|
|
new_framesamples = 960;
|
|
else if (bottle_neck > Thld_30_20)
|
|
new_framesamples = 320;
|
|
break;
|
|
case 960:
|
|
if (bottle_neck >= Thld_60_30)
|
|
new_framesamples = 480;
|
|
break;
|
|
}
|
|
|
|
return new_framesamples;
|
|
}
|
|
|
|
double WebRtcIsac_GetSnr(
|
|
double bottle_neck,
|
|
int framesamples)
|
|
{
|
|
double s2nr;
|
|
|
|
const double a_20 = -30.0;
|
|
const double b_20 = 0.8;
|
|
const double c_20 = 0.0;
|
|
|
|
const double a_30 = -23.0;
|
|
const double b_30 = 0.48;
|
|
const double c_30 = 0.0;
|
|
|
|
const double a_60 = -23.0;
|
|
const double b_60 = 0.53;
|
|
const double c_60 = 0.0;
|
|
|
|
|
|
/* find new SNR value */
|
|
switch(framesamples) {
|
|
case 320:
|
|
s2nr = a_20 + b_20 * bottle_neck * 0.001 + c_20 * bottle_neck *
|
|
bottle_neck * 0.000001;
|
|
break;
|
|
case 480:
|
|
s2nr = a_30 + b_30 * bottle_neck * 0.001 + c_30 * bottle_neck *
|
|
bottle_neck * 0.000001;
|
|
break;
|
|
case 960:
|
|
s2nr = a_60 + b_60 * bottle_neck * 0.001 + c_60 * bottle_neck *
|
|
bottle_neck * 0.000001;
|
|
break;
|
|
default:
|
|
s2nr = 0;
|
|
}
|
|
|
|
return s2nr;
|
|
|
|
}
|