WebRTC QoS方法十三.2(Jitter延时的计算)

诗林  金牌会员 | 2024-11-4 09:11:13 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 581|帖子 581|积分 1743

一、背景先容

一些报文在网络传输中,会存在丢包重传和延时的情况。渲染时需要进行适当缓存,等候丢失被重传的报文或者正在路上传输的报文。
jitter延时计算是确认需要缓存的时间
另外,在检测到帧有重传情况时,也可适当在渲染时间内增加RTT延时时间,等候丢失重传的报文
二、jitter实现原理

JitterDelay由两部分耽误造成:传输大帧引起的耽误和网络噪声引起的耽误。计算公式如下:

其中:
estimate[0]:信道传输速率的倒数
MaxFrameSize:表示自会话开始以来所收到的最大帧size
AvgFrameSize:表示平均帧巨细,排除keyframe等超大帧
kNoiseStdDevs:       表示噪声系数2.33
var_noise_ms2_:     表示噪声方差
kNoiseStdDevOffset:  表示噪声扣除常数30 
实现函数:
JitterEstimator::CalculateEstimate

1、传输大帧引起的耽误

传输大帧引起的耽误



这个公式的原理是:[milliseconds] = [1 / bytes per millisecond] * [bytes] 
实现函数:
  1. double FrameDelayVariationKalmanFilter::GetFrameDelayVariationEstimateSizeBased(
  2.     double frame_size_variation_bytes) const {
  3.   // Unit: [1 / bytes per millisecond] * [bytes] = [milliseconds].
  4.   return estimate_[0] * frame_size_variation_bytes;
  5. }
复制代码
filtered_max_frame_size_bytes

=  std::max<double>(kPsi * max_frame_size_bytes_, frame_size.bytes());
constexpr double kPsi = 0.9999;
filtered_avg_frame_size_bytes

是每一帧的加权平均值,但是需要排除key frame这种超大帧

estimate_[0]参数计算
利用一个简化卡尔曼滤波算法,在处理帧耽误变革(frame_delay_variation_ms)的估计,考虑了帧巨细变革(frame_size_variation_bytes)和最大帧巨细(max_frame_size_bytes)作为输入参数。
  1. void FrameDelayVariationKalmanFilter::PredictAndUpdate(
  2.     double frame_delay_variation_ms,
  3.     double frame_size_variation_bytes,
  4.     double max_frame_size_bytes,
  5.     double var_noise) {
  6.   // Sanity checks.
  7.   if (max_frame_size_bytes < 1) {
  8.     return;
  9.   }
  10.   if (var_noise <= 0.0) {
  11.     return;
  12.   }
  13.   // This member function follows the data flow in
  14.   // https://en.wikipedia.org/wiki/Kalman_filter#Details.
  15.   // 1) Estimate prediction: `x = F*x`.
  16.   // For this model, there is no need to explicitly predict the estimate, since
  17.   // the state transition matrix is the identity.
  18.   // 2) Estimate covariance prediction: `P = F*P*F' + Q`.
  19.   // Again, since the state transition matrix is the identity, this update
  20.   // is performed by simply adding the process noise covariance.
  21.   estimate_cov_[0][0] += process_noise_cov_diag_[0];
  22.   estimate_cov_[1][1] += process_noise_cov_diag_[1];
  23.   // 3) Innovation: `y = z - H*x`.
  24.   // This is the part of the measurement that cannot be explained by the current
  25.   // estimate.
  26.   double innovation =
  27.       frame_delay_variation_ms -
  28.       GetFrameDelayVariationEstimateTotal(frame_size_variation_bytes);
  29.   // 4) Innovation variance: `s = H*P*H' + r`.
  30.   double estim_cov_times_obs[2];
  31.   estim_cov_times_obs[0] =
  32.       estimate_cov_[0][0] * frame_size_variation_bytes + estimate_cov_[0][1];
  33.   estim_cov_times_obs[1] =
  34.       estimate_cov_[1][0] * frame_size_variation_bytes + estimate_cov_[1][1];
  35.   double observation_noise_stddev =
  36.       (300.0 * exp(-fabs(frame_size_variation_bytes) /
  37.                    (1e0 * max_frame_size_bytes)) +
  38.        1) *
  39.       sqrt(var_noise);
  40.   if (observation_noise_stddev < 1.0) {
  41.     observation_noise_stddev = 1.0;
  42.   }
  43.   // TODO(brandtr): Shouldn't we add observation_noise_stddev^2 here? Otherwise,
  44.   // the dimensional analysis fails.
  45.   double innovation_var = frame_size_variation_bytes * estim_cov_times_obs[0] +
  46.                           estim_cov_times_obs[1] + observation_noise_stddev;
  47.   if ((innovation_var < 1e-9 && innovation_var >= 0) ||
  48.       (innovation_var > -1e-9 && innovation_var <= 0)) {
  49.     RTC_DCHECK_NOTREACHED();
  50.     return;
  51.   }
  52.   // 5) Optimal Kalman gain: `K = P*H'/s`.
  53.   // How much to trust the model vs. how much to trust the measurement.
  54.   double kalman_gain[2];
  55.   kalman_gain[0] = estim_cov_times_obs[0] / innovation_var;
  56.   kalman_gain[1] = estim_cov_times_obs[1] / innovation_var;
  57.   // 6) Estimate update: `x = x + K*y`.
  58.   // Optimally weight the new information in the innovation and add it to the
  59.   // old estimate.
  60.   estimate_[0] += kalman_gain[0] * innovation;
  61.   estimate_[1] += kalman_gain[1] * innovation;
  62.   // (This clamping is not part of the linear Kalman filter.)
  63.   if (estimate_[0] < kMaxBandwidth) {
  64.     estimate_[0] = kMaxBandwidth;
  65.   }
  66.   // 7) Estimate covariance update: `P = (I - K*H)*P`
  67.   double t00 = estimate_cov_[0][0];
  68.   double t01 = estimate_cov_[0][1];
  69.   estimate_cov_[0][0] =
  70.       (1 - kalman_gain[0] * frame_size_variation_bytes) * t00 -
  71.       kalman_gain[0] * estimate_cov_[1][0];
  72.   estimate_cov_[0][1] =
  73.       (1 - kalman_gain[0] * frame_size_variation_bytes) * t01 -
  74.       kalman_gain[0] * estimate_cov_[1][1];
  75.   estimate_cov_[1][0] = estimate_cov_[1][0] * (1 - kalman_gain[1]) -
  76.                         kalman_gain[1] * frame_size_variation_bytes * t00;
  77.   estimate_cov_[1][1] = estimate_cov_[1][1] * (1 - kalman_gain[1]) -
  78.                         kalman_gain[1] * frame_size_variation_bytes * t01;
  79.   // Covariance matrix, must be positive semi-definite.
  80.   RTC_DCHECK(estimate_cov_[0][0] + estimate_cov_[1][1] >= 0 &&
  81.              estimate_cov_[0][0] * estimate_cov_[1][1] -
  82.                      estimate_cov_[0][1] * estimate_cov_[1][0] >=
  83.                  0 &&
  84.              estimate_cov_[0][0] >= 0);
  85. }
复制代码
2、网络噪声引起的耽误

网络噪声引起的耽误


constexpr double kNoiseStdDevs = 2.33; //噪声系数
constexpr double kNoiseStdDevOffset = 30.0;//噪声扣除常数
var_noise_ms2_ //噪声方差
实现函数:

噪声方差var_noise_ms2计算

var_noise_ms2 = alpha * var_noise_ms2_ + 
                (1 - alpha) *(d_dT - avg_noise_ms_) *(d_dT - avg_noise_ms_);
实现函数:JitterEstimator::EstimateRandomJitter

其中:
d_dT = 实际FrameDelay - 评估FrameDelay

             在JitterEstimator::UpdateEstimate函数实现
             

实际FrameDelay = (两帧之间实际接收gap - 两帧之间实际发送gap)

             在InterFrameDelayVariationCalculator::Calculate函数实现
  1. absl::optional<TimeDelta> InterFrameDelayVariationCalculator::Calculate(
  2.     uint32_t rtp_timestamp,
  3.     Timestamp now) {
  4.   int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp);
  5.   if (!prev_wall_clock_) {
  6.     prev_wall_clock_ = now;
  7.     prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;
  8.     // Inter-frame delay variation is undefined for a single frame.
  9.     // TODO(brandtr): Should this return absl::nullopt instead?
  10.     return TimeDelta::Zero();
  11.   }
  12.   // Account for reordering in jitter variance estimate in the future?
  13.   // Note that this also captures incomplete frames which are grabbed for
  14.   // decoding after a later frame has been complete, i.e. real packet losses.
  15.   uint32_t cropped_prev = static_cast<uint32_t>(prev_rtp_timestamp_unwrapped_);
  16.   if (rtp_timestamp_unwrapped < prev_rtp_timestamp_unwrapped_ ||
  17.       !IsNewerTimestamp(rtp_timestamp, cropped_prev)) {
  18.     return absl::nullopt;
  19.   }
  20.   // Compute the compensated timestamp difference.
  21.   TimeDelta delta_wall = now - *prev_wall_clock_;
  22.   int64_t d_rtp_ticks = rtp_timestamp_unwrapped - prev_rtp_timestamp_unwrapped_;
  23.   TimeDelta delta_rtp = d_rtp_ticks / k90kHz;
  24.   // The inter-frame delay variation is the second order difference between the
  25.   // RTP and wall clocks of the two frames, or in other words, the first order
  26.   // difference between `delta_rtp` and `delta_wall`.
  27.   TimeDelta inter_frame_delay_variation = delta_wall - delta_rtp;
  28.   prev_wall_clock_ = now;
  29.   prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;
  30.   return inter_frame_delay_variation;
  31. }
复制代码
评估FrameDelay =  estimate[0] * (FrameSize – PreFrameSize) + estimate[1]

评估FrameDelay实现函数:
  1. double FrameDelayVariationKalmanFilter::GetFrameDelayVariationEstimateTotal(
  2.     double frame_size_variation_bytes) const {
  3.   double frame_transmission_delay_ms =
  4.       GetFrameDelayVariationEstimateSizeBased(frame_size_variation_bytes);
  5.   double link_queuing_delay_ms = estimate_[1];
  6.   return frame_transmission_delay_ms + link_queuing_delay_ms;
  7. }
复制代码
3、jitter延时更新流程


三、RTT延时计算 

VideoStreamBufferController::OnFrameReady函数,在判断帧有重传情况时,还会根据实际情况,在渲染帧时间里面增加RTT值。

JitterEstimator::GetJitterEstimate根据实际配置,可以在渲染时间中适当增加一定比例的RTT延时值。 
 
四、参考

WebRTC视频接收缓冲区基于KalmanFilter的耽误模型 - 简书在WebRTC的视频处理流水线中,接收端缓冲区JitterBuffer是关键的构成部分:它负责RTP数据包乱序重排和组帧,RTP丢包重传,请求重传关键帧,估算缓冲区耽误等功能...
https://www.jianshu.com/p/bb34995c549a

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表