提交 0e6e53f1 authored 作者: Dragos Oancea's avatar Dragos Oancea 提交者: Anthony Minessale

FS-8644: OPUS_SET_BITRATE(), codec control and estimators for packet loss and…

FS-8644: OPUS_SET_BITRATE(), codec control and estimators for packet loss and RTT (with Kalman filters) to detect a slow or congested link.
Feature enabled with "adjust-bitrate" in opus.conf.xml - it's a feedback loop with incoming RTCP.
上级 bbe5ee08
......@@ -286,6 +286,7 @@ library_include_HEADERS = \
src/include/switch_utils.h \
src/include/switch_rtp.h \
src/include/switch_jitterbuffer.h \
src/include/switch_estimators.h \
src/include/switch_rtcp_frame.h \
src/include/switch_stun.h \
src/include/switch_nat.h \
......@@ -351,6 +352,7 @@ libfreeswitch_la_SOURCES = \
src/switch_regex.c \
src/switch_rtp.c \
src/switch_jitterbuffer.c \
src/switch_estimators.c \
src/switch_ivr_bridge.c \
src/switch_ivr_originate.c \
src/switch_ivr_async.c \
......
......@@ -144,6 +144,7 @@
#include "switch_core_media.h"
#include "switch_core_video.h"
#include "switch_jitterbuffer.h"
#include "switch_estimators.h"
#include <libteletone.h>
......
......@@ -322,6 +322,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_codec_control(switch_core_sess
switch_codec_control_type_t *rtype,
void **ret_data);
SWITCH_DECLARE(switch_bool_t) switch_core_media_codec_get_cap(switch_core_session_t *session,
switch_media_type_t mtype,
switch_codec_flag_t flag);
#define switch_core_media_gen_key_frame(_session) switch_core_media_codec_control(_session, SWITCH_MEDIA_TYPE_VIDEO, SWITCH_IO_WRITE, \
SCC_VIDEO_GEN_KEYFRAME, SCCT_NONE, NULL, SCCT_NONE, NULL, NULL, NULL) \
......
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Dragos Oancea <droancea@yahoo.com>
*
* switch_estimators.h -- Estimators for Packet Loss, Jitter, RTT , etc
*
*/
#ifndef SWITCH_ESTIMATORS_H
#define SWITCH_ESTIMATORS_H
#include <switch.h>
SWITCH_BEGIN_EXTERN_C
struct kalman_estimator_s {
/* initial values for the Kalman filter */
float val_estimate_last ;
float P_last ;
/* the noise in the system:
The amount of noise in your measurements and the state-transitions
(e.g. the standard deviation of the signal noise, and how 'wrong' your simplified model
of the state-transitions are) => These are Q and R matrices */
float Q ; /* the process noise covariance matrix */
float R ; /* the measurement noise covariance matrix */
float K; /* P_temp * H^T * (H* P_temp * H^T + R)^-1 */
float P; /* the Kalman gain (calculated) */
float val_estimate; /* x_temp_est + K * (z_measured - H * x_temp_est) */
float val_measured; /* the 'noisy' value we measured */
};
struct cusum_kalman_detector_s {
/* initial values for the CUSUM Kalman filter */
float val_estimate_last;
float val_desired_last;
float P_last;
float K_last;
float delta;
float measurement_noise_e;
float variance_Re;
float measurement_noise_v;
float variance_Rv;
float g_last;
/*constants per model*/
float epsilon;
float h;
/* for calculating variance */
float last_average;
float last_q;
float N; /*how many samples we have so far (eg: how many RTCP we received, granted that we can calculate RTT for each one of them)*/
};
typedef struct kalman_estimator_s kalman_estimator_t;
typedef struct cusum_kalman_detector_s cusum_kalman_detector_t;
SWITCH_DECLARE(void) switch_kalman_init(kalman_estimator_t *est, float Q, float R);
SWITCH_DECLARE(switch_bool_t) switch_kalman_cusum_init(cusum_kalman_detector_t *detect_change, float epsilon,float h);
SWITCH_DECLARE(switch_bool_t) switch_kalman_estimate(kalman_estimator_t * est, float measurement, int system_model);
SWITCH_DECLARE (switch_bool_t) switch_kalman_cusum_detect_change(cusum_kalman_detector_t * detector, float measurement, float rtt_avg);
SWITCH_DECLARE(switch_bool_t) switch_kalman_is_slow_link(kalman_estimator_t * est_loss, kalman_estimator_t * est_rtt);
SWITCH_END_EXTERN_C
#endif
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/
......@@ -755,6 +755,8 @@ typedef enum {
SWITCH_ZRTP_FLAG_SECURE_MITM_RECV,
SWITCH_RTP_FLAG_DEBUG_RTP_READ,
SWITCH_RTP_FLAG_DEBUG_RTP_WRITE,
SWITCH_RTP_FLAG_ESTIMATORS,
SWITCH_RTP_FLAG_ADJ_BITRATE_CAP,
SWITCH_RTP_FLAG_VIDEO,
SWITCH_RTP_FLAG_ENABLE_RTCP,
SWITCH_RTP_FLAG_RTCP_MUX,
......@@ -1631,6 +1633,7 @@ typedef enum {
SWITCH_CODEC_FLAG_AAL2 = (1 << 6),
SWITCH_CODEC_FLAG_PASSTHROUGH = (1 << 7),
SWITCH_CODEC_FLAG_READY = (1 << 8),
SWITCH_CODEC_FLAG_HAS_ADJ_BITRATE = (1 << 14),
SWITCH_CODEC_FLAG_HAS_PLC = (1 << 15),
SWITCH_CODEC_FLAG_VIDEO_PATCHING = (1 << 16)
} switch_codec_flag_enum_t;
......@@ -2268,6 +2271,7 @@ typedef enum {
SCC_VIDEO_BANDWIDTH,
SCC_VIDEO_RESET,
SCC_AUDIO_PACKET_LOSS,
SCC_AUDIO_ADJUST_BITRATE,
SCC_DEBUG,
SCC_CODEC_SPECIFIC
} switch_codec_control_command_t;
......@@ -2586,6 +2590,12 @@ typedef struct switch_waitlist_s {
struct switch_jb_s;
typedef struct switch_jb_s switch_jb_t;
//struct kalman_estimator_s;
//typedef struct kalman_estimator_s kalman_estimator_t;
//struct cusum_kalman_detector_s;
//typedef struct cusum_kalman_detector_s cusum_kalman_detector_t;
struct switch_img_txt_handle_s;
typedef struct switch_img_txt_handle_s switch_img_txt_handle_t;
......
......@@ -34,6 +34,11 @@
#include "switch.h"
#include "opus.h"
#define SWITCH_OPUS_MIN_BITRATE 6000
#define SWITCH_OPUS_MAX_BITRATE 510000
#define SWITCH_OPUS_MIN_FEC_BITRATE 12400
SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load);
SWITCH_MODULE_DEFINITION(mod_opus, mod_opus_load, NULL, NULL);
......@@ -91,6 +96,15 @@ struct dec_stats {
};
typedef struct dec_stats dec_stats_t;
struct codec_control_state {
int keep_fec;
opus_int32 current_bitrate;
opus_int32 wanted_bitrate;
uint32_t increase_step;
uint32_t decrease_step;
};
typedef struct codec_control_state codec_control_state_t;
struct opus_context {
OpusEncoder *encoder_object;
OpusDecoder *decoder_object;
......@@ -103,6 +117,7 @@ struct opus_context {
int look_check;
int look_ts;
dec_stats_t decoder_stats;
codec_control_state_t control_state;
};
struct {
......@@ -116,6 +131,7 @@ struct {
int asymmetric_samplerates;
int keep_fec;
int fec_decode;
int adjust_bitrate;
int debuginfo;
uint32_t use_jb_lookahead;
switch_mutex_t *mutex;
......@@ -253,7 +269,7 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt
if (!strcasecmp(data, "maxaveragebitrate")) {
codec_settings->maxaveragebitrate = atoi(arg);
if (codec_settings->maxaveragebitrate < 6000 || codec_settings->maxaveragebitrate > 510000) {
if (codec_settings->maxaveragebitrate < SWITCH_OPUS_MIN_BITRATE || codec_settings->maxaveragebitrate > SWITCH_OPUS_MAX_BITRATE) {
codec_settings->maxaveragebitrate = 0; /* values outside the range between 6000 and 510000 SHOULD be ignored */
}
}
......@@ -600,6 +616,7 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(fec_bitrate));
/* will override the maxaveragebitrate set in opus.conf.xml */
opus_codec_settings.maxaveragebitrate = fec_bitrate;
context->control_state.keep_fec = opus_prefs.keep_fec ;
}
}
}
......@@ -607,6 +624,10 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag
if (opus_codec_settings.usedtx) {
opus_encoder_ctl(context->encoder_object, OPUS_SET_DTX(opus_codec_settings.usedtx));
}
if (opus_prefs.adjust_bitrate) {
switch_set_flag(codec, SWITCH_CODEC_FLAG_HAS_ADJ_BITRATE);
}
}
if (decoding) {
......@@ -972,9 +993,11 @@ static switch_status_t opus_load_config(switch_bool_t reload)
opus_prefs.keep_fec = atoi(val);
} else if (!strcasecmp(key, "advertise-useinbandfec")) { /*decoder, has meaning only for FMTP: useinbandfec=1 by default */
opus_prefs.fec_decode = atoi(val);
} else if (!strcasecmp(key, "adjust-bitrate")) { /* encoder, this setting will make the encoder adjust its bitrate based on a feedback loop (RTCP). This is not "VBR".*/
opus_prefs.adjust_bitrate = atoi(val);
} else if (!strcasecmp(key, "maxaveragebitrate")) {
opus_prefs.maxaveragebitrate = atoi(val);
if (opus_prefs.maxaveragebitrate < 6000 || opus_prefs.maxaveragebitrate > 510000) {
if (opus_prefs.maxaveragebitrate < SWITCH_OPUS_MIN_BITRATE || opus_prefs.maxaveragebitrate > SWITCH_OPUS_MAX_BITRATE) {
opus_prefs.maxaveragebitrate = 0; /* values outside the range between 6000 and 510000 SHOULD be ignored */
}
} else if (!strcasecmp(key, "maxplaybackrate")) {
......@@ -1132,13 +1155,68 @@ static switch_status_t switch_opus_control(switch_codec_t *codec,
}
if (globals.debug || context->debug) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus Adjusting packet loss percent from %d%% to %d%%!\n",
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: Adjusting packet loss percent from %d%% to %d%%!\n",
context->old_plpct, plpct);
}
}
context->old_plpct = plpct;
}
break;
case SCC_AUDIO_ADJUST_BITRATE:
{
const char *cmd = (const char *)cmd_data;
if (!zstr(cmd)) {
opus_int32 current_bitrate=context->control_state.current_bitrate;
if (!strcasecmp(cmd, "increase")) {
/* https://wiki.xiph.org/OpusFAQ
"[...]Opus scales from about 6 to 512 kb/s, in increments of 0.4 kb/s (one byte with 20 ms frames).
Opus can have more than 1200 possible bitrates[...]" */
int br_step = context->control_state.increase_step?context->control_state.increase_step:400;
opus_encoder_ctl(context->encoder_object, OPUS_GET_BITRATE(&current_bitrate));
if (opus_prefs.maxaveragebitrate > current_bitrate) {
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(current_bitrate+br_step));
if ((context->control_state.keep_fec) && (current_bitrate > SWITCH_OPUS_MIN_FEC_BITRATE)) {
opus_prefs.keep_fec = 1; /* enable back FEC if it was disabled by SCC_AUDIO_ADJUST_BITRATE, we have enough network bandwidth now */
}
if (globals.debug || context->debug) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: Adjusting bitrate to %d (increase)\n", current_bitrate+br_step);
}
}
} else if (!strcasecmp(cmd, "decrease")) {
int br_step = context->control_state.decrease_step?context->control_state.decrease_step:400;
opus_encoder_ctl(context->encoder_object, OPUS_GET_BITRATE(&current_bitrate));
if (current_bitrate > SWITCH_OPUS_MIN_BITRATE) {
if ((context->control_state.keep_fec) && (current_bitrate < SWITCH_OPUS_MIN_FEC_BITRATE)) {
opus_prefs.keep_fec = 0; /* no point to try to keep FEC enabled anymore, we're low on network bandwidth (that's why we ended up here) */
}
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(current_bitrate-br_step));
if (globals.debug || context->debug) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: Adjusting bitrate to %d (decrease)\n", current_bitrate-br_step);
}
}
} else if (!strcasecmp(cmd, "default")) {
/*restore default bitrate */
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(opus_prefs.maxaveragebitrate));
if (context->control_state.keep_fec) {
opus_prefs.keep_fec = 1; /* enable back FEC, we have enough network bandwidth now */
}
if (globals.debug || context->debug) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: Adjusting bitrate to %d (configured maxaveragebitrate)\n", opus_prefs.maxaveragebitrate);
}
} else {
/* set Opus minimum bitrate */
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(SWITCH_OPUS_MIN_BITRATE));
if (context->control_state.keep_fec) {
opus_prefs.keep_fec = 0; /* do not enforce FEC anymore, we're low on network bandwidth */
}
if (globals.debug || context->debug) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opus encoder: Adjusting bitrate to %d (minimum)\n", SWITCH_OPUS_MIN_BITRATE);
}
}
}
}
break;
default:
break;
}
......
......@@ -11322,6 +11322,35 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_codec_control(switch_core_sess
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_bool_t) switch_core_media_codec_get_cap(switch_core_session_t *session,
switch_media_type_t mtype,
switch_codec_flag_t flag) {
switch_rtp_engine_t *engine = NULL;
switch_media_handle_t *smh = NULL;
switch_codec_t *codec = NULL;
switch_assert(session);
if (!(smh = session->media_handle)) {
return SWITCH_FALSE;
}
if (!(engine = &smh->engines[mtype])) {
return SWITCH_FALSE;
}
codec = &engine->write_codec;
if (!switch_core_codec_ready(codec)) {
return SWITCH_FALSE;
}
if (switch_test_flag(codec, flag)){
return SWITCH_TRUE;
}
return SWITCH_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_core_session_write_encoded_video_frame(switch_core_session_t *session,
switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
......
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Dragos Oancea <droancea@yahoo.com>
*
* switch_estimators.c -- Estimators and Detectors (try to read into the future: packet loss, jitter, RTT, etc)
*
*/
#include <switch_estimators.h>
#include <switch.h>
#ifndef _MSC_VER
#include <switch_private.h>
#endif
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#undef PACKAGE_BUGREPORT
#undef VERSION
#undef PACKAGE
#undef inline
#include <datatypes.h>
#include <switch_types.h>
#define KALMAN_SYSTEM_MODELS 3 /*loss, jitter, rtt*/
#define EST_LOSS 0
#define EST_JITTER 1
#define EST_RTT 2
/* This function initializes the Kalman System Model
*
* xk+1 = A*xk + wk
* zk = H*xk + vk
* xk = state variable (must exist in physical world - measurable )
* zk = measurment
* wk,vk - white noise
* A = state trasition matrix , (n x n ) matrix
* H = state-to-measurment matrix , ( n x n ) matrix
* Noise covariance:
* Q: Covariance matrix of wk, ( n x n ) diagonal matrix
* R: Covariance matrix of vk , ( m x m ) diagonal matrix
* R: if you want to be affected less by the measurement and get the estimate with less variation, increase R
* Q: if you want to be affected more by the measurement and get the estimate with more variation, decrease Q
*
* (Phil Kim book)
*
*/
void switch_kalman_init(kalman_estimator_t *est, float Q, float R)
{
est -> val_estimate_last = 0 ;
est -> P_last = 0;
est -> Q = Q; /*accuracy of system model */ /* SYSTEM MODEL: TO BE DEDUCTED */
est -> R = R; /*accuracy of measurement*/ /* SYSTEM MODEL: TO BE DEDUCTED */
est -> K = 0;
est -> val_estimate = 0 ;
est -> val_measured = 0 ; // [0-100 %] or [0-5000] or [0-2sec]
}
/*
CUSUM Kalman functions to detect sudden change over a predefined thereshold.
y(t) = sampled RTT
x(t)= desired RTT
Model:
x(t+1) = x(t) + delta(t)*v(t)
y(t) = x(t) + e(t)
Noisy characteristic of RTT captured by measurment noise e(t) with variance Re.
The step changes in the desired RTT x(t) is modeled as the process noise v(t)
with variance Rv and the discrete variable delta(t) .
If a change occurs at time t, then delta(t) = 1 otherwise delta(t) = 0.
avg(x(t)) = avg(x(t-1)) + K(t)(y(t) - avg(x(t-1)))
K(t) = P(t-1)/(P(t-1) + Re))
P(t) = (1-K(t))P(t-1) + delta(t-1)* Rv
e(t) = y(t) - avg(x(t))
g(t) = max(g(t-1) + e(t) - epsilon,0)
if g(t) > 0 then
delta(t) = 1 // alarm
g(t) = 0
else
delta(t) = 0
endif
constants:
epsilon = 0.005
h = 0.05
*/
switch_bool_t switch_kalman_cusum_init (cusum_kalman_detector_t *detect_change, float epsilon, float h)
{
cusum_kalman_detector_t *detector_change = detect_change;
if (epsilon < 0 || h < 0) {
return FALSE;
}
detector_change -> val_estimate_last = 0;
detector_change -> val_desired_last = 0;
detector_change -> P_last = 0;
detector_change -> K_last = 0;
detector_change -> delta = 0;
detector_change -> measurement_noise_e = 0;
detector_change -> variance_Re = 0;
detector_change -> measurement_noise_v = 0;
detector_change -> variance_Rv = 0;
detector_change -> g_last = 0;
/*per system model*/
detector_change -> epsilon = epsilon;
detector_change -> h = h;
/*variance*/
detector_change -> last_average = 0;
detector_change -> last_q = 0;
detector_change -> N = 0;
return TRUE;
}
switch_bool_t switch_kalman_cusum_detect_change(cusum_kalman_detector_t * detector, float measurement, float avg)
{
float K=0;
float P=0;
float g=0;
float desired_val;
float current_average;
float current_q;
float sample_variance_Re = 0;
/*variance*/
detector->N++;
current_average = detector->last_average + (measurement - detector->last_average)/detector->N ;
if (avg > current_average) {
current_average = avg;
}
current_q = detector-> last_q + (measurement - detector->last_average) * (measurement - current_average);
if (detector->N != 0)
sample_variance_Re = sqrt(current_q/detector->N);
detector->variance_Re = sample_variance_Re;
detector->variance_Rv = sample_variance_Re;
if (sample_variance_Re != 0) {
K = detector->P_last / (detector->P_last + detector->variance_Re);
desired_val = detector->val_desired_last + K * (measurement - detector->variance_Re);
P = (1 - K) * detector->P_last + detector->delta * detector->variance_Rv;
detector->measurement_noise_e = measurement - desired_val;
g = detector->g_last + detector->measurement_noise_e - detector->epsilon;
if (g > detector->h) {
detector->delta = 1;
g = 0;
} else {
detector->delta = 0;
}
/* update last vals for calculating variance */
detector->last_average = current_average;
/* update lasts (cusum)*/
detector -> g_last = g;
detector -> P_last = P;
detector -> val_desired_last = desired_val;
}
if (detector->delta == 1) {
return TRUE;
}
return FALSE;
}
/* Kalman filter abstract ( measure and estimate 1 single value per system model )
* Given the measurment and the system model together with the current state ,
* the function puts an estimate in the estimator struct */
switch_bool_t switch_kalman_estimate (kalman_estimator_t * est, float measurement, int system_model)
{
/*system model can be about: loss, jitter, rtt*/
float val_estimate;
float val_temp_est = est->val_estimate_last;
float P_temp = est->P_last + est->Q;
if (system_model >= KALMAN_SYSTEM_MODELS) {
return SWITCH_FALSE ;
}
/*sanitize input a little bit, just in case */
if (system_model == EST_LOSS ) {
if ((measurement > 100) && (measurement < 0)) {
return SWITCH_FALSE ;
}
}
if (system_model == EST_JITTER) {
if ((measurement > 10000) && (measurement < 0)) {
return SWITCH_FALSE;
}
}
if (system_model == EST_RTT) {
if ((measurement > 2 ) && (measurement < 0)) {
return SWITCH_FALSE;
}
}
/* calculate the Kalman gain */
est->K = P_temp * (1.0/(P_temp + est->R));
/* real life measurement */
est->val_measured = measurement ;
val_estimate = val_temp_est + est->K * (est->val_measured - val_temp_est);
est->P = (1 - est->K) * P_temp;
/*update lasts*/
est->P_last = est->P;
/* save the estimated value (future) */
est->val_estimate_last = val_estimate;
return SWITCH_TRUE;
}
switch_bool_t switch_kalman_is_slow_link(kalman_estimator_t * est_loss, kalman_estimator_t * est_rtt)
{
float thresh_packet_loss = 5; /* % */
float thresh_rtt = 0.8 ; /*seconds*/
if ((est_loss->val_estimate_last > thresh_packet_loss) &&
(est_rtt->val_estimate_last > thresh_rtt )) {
return SWITCH_TRUE;
}
return SWITCH_FALSE;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论