WebRTC研究:rrt 时间 之 CallStats

在之前的文章:WebRTC研究:rrt 时间 之 再次处理以供重传等功能使用 中讲到,ModuleRtpRtcpImpl::Process() 会将 RRT 时间与当前时间一起存入 CallStats 对象的 reports_ 集合中,然后由 CallStats.Process() 计算出重传等应用中所使用到的 avg_rtt_ms_ 与 max_rtt_ms_,那么本篇文章将会介绍此部分计算逻辑。

一、创建线程 module_process_thread_ 和 CallStats 对象(用来追踪记录一个 Call 对象的统计数据),然后启动线程并将 CallStats 对象注册到线程中:

Call::Call(const Call::Config& config)
    : module_process_thread_(ProcessThread::Create("ModuleProcessThread")),
      call_stats_(new CallStats(clock_)),
      ...
      ...
      ...
{
  ...
  ...
  ...
  
  module_process_thread_->Start();
  module_process_thread_->RegisterModule(call_stats_.get());
  
  ...
  ...
  ...
}

二、将 CallStats 对象注册到线程 module_process_thread_ 的 list:modules_中:

void ProcessThreadImpl::RegisterModule(Module* module) 
{
  ...
  ...
  ...
  
  {
    // 将 CallStats 对象添加到集合 modules_
    rtc::CritScope lock(&lock_);
    modules_.push_back(ModuleCallback(module));
  }

  // 唤醒当前线程
  wake_up_->Set();
}

三、线程 run() 中会调用 Process(),Process() 负责依次检测各 module 的处理时间是否达到,是的话,则调用 module 的 Process() 进行处理。Process() 末尾,线程会进行 wait 状态,等待超时被唤醒:

bool ProcessThreadImpl::Process() 
{
  int64_t now = rtc::TimeMillis();

  /*
  当前线程在本次处理完毕后,进入 wait 状态的持续时长
  默认为 60s
  */
  int64_t next_checkpoint = now + (1000 * 60);

  {
    rtc::CritScope lock(&lock_);
    if (stop_)
      return false;
    for (ModuleCallback& m : modules_) 
	{
      // 初始化模块下次处理时间
      if (m.next_callback == 0)
        m.next_callback = GetNextCallbackTime(m.module, now);

	  // 判断模块处理时间是否到了
      if (m.next_callback <= now || m.next_callback == kCallProcessImmediately) 
	  {
        m.module->Process();

        // 计算模块下次处理时间
        int64_t new_now = rtc::TimeMillis();
        m.next_callback = GetNextCallbackTime(m.module, new_now);
      }

	  /*
	  若某模块下次处理时间在当前线程唤醒之前,则调整当前线程 wait 状态的持续时长
	  */
      if (m.next_callback < next_checkpoint)
        next_checkpoint = m.next_callback;
    }

    ...
    ...
    ...
  }

  /* 计算当前线程 wait 状态的持续时长 */
  int64_t time_to_wait = next_checkpoint - rtc::TimeMillis();
  if (time_to_wait > 0)
    wake_up_->Wait(static_cast<unsigned long>(time_to_wait)); /* 进入 wait 状态*/

  return true;
}

四、对于 CallStats 对象,Process() 下次调用时间为 1s 减去本次调用耗时:

int64_t CallStats::TimeUntilNextProcess() 
{
  /*
  last_process_time_:上次进入 Process() 的时间
  kUpdateIntervalMs = 1000

  理论上是,在本次调用 Process() 之后,需等待 1s,然后再次执行 Process()
  last_process_time_ + kUpdateIntervalMs,就是下一次准备开始调用 Process() 的理论时间,
  但是由于调用 CallStats.Process() 本身存在耗时,因此实际间隔时间需要减掉这部分耗时
  */
  return last_process_time_ + kUpdateIntervalMs - clock_->TimeInMilliseconds();
}

五、CallStats 对象 Process() 会根据 list:reports_ 中的 rrt 计算出最新的 avg_rtt_ms_ 与 max_rtt_ms_,新 avg_rtt 由 旧 avg_rtt(70%) 和 reports_ 中 rrt 平均值(30%) 的加权和构成:

void CallStats::Process() 
{
  rtc::CritScope cs(&crit_);
  int64_t now = clock_->TimeInMilliseconds();

  /* 本次处理时间还未到,直接返回 */
  if (now < last_process_time_ + kUpdateIntervalMs)
    return;

  /* 记录本次处理时间 */
  last_process_time_ = now;

  /*
  剔除 list:reports_ 中太久的数据,判断标准:
  当前时间 距离 rrt 添加到 list 中的时间 超过 1500 ms
  */
  RemoveOldReports(now, &reports_);

  // 获取 list:reports_ 中 最大的 rrt 时间
  max_rtt_ms_ = GetMaxRttMs(&reports_);

  /*
  计算最新的 avg_rtt_ms_,
  最新 avg_rtt_ms_ 为 当前 avg_rtt_ms_ 与 list:reports_ 中 rrt平均值的加权和,权重分别为 70%、30%
  */
  UpdateAvgRttMs(&reports_, &avg_rtt_ms_);

  // 将 avg_rtt_ms_ 、max_rtt_ms_ 更新到所有 observers
  if (max_rtt_ms_ >= 0) 
  {
    RTC_DCHECK_GE(avg_rtt_ms_, 0);

    for (std::list<CallStatsObserver*>::iterator it = observers_.begin(); it != observers_.end(); ++it) 
	{
      (*it)->OnRttUpdate(avg_rtt_ms_, max_rtt_ms_);
    }

    // 累加历代所有 avg_rtt_ms_ 及 个数
    sum_avg_rtt_ms_ += avg_rtt_ms_;
    ++num_avg_rtt_;
  }
}

剔除 reports_ 中距离当前时间超过 1500ms 的 rrt 节点:

void RemoveOldReports(int64_t now, std::list<CallStats::RttTime>* reports) 
{
  const int64_t kRttTimeoutMs = 1500;
  while (!reports->empty() && (now - reports->front().time) > kRttTimeoutMs) 
  {
    reports->pop_front();
  }
}

计算最新的 max_rtt_ms_:

int64_t GetMaxRttMs(std::list<CallStats::RttTime>* reports) 
{
  if (reports->empty())
    return -1;

  int64_t max_rtt_ms = 0;
  for (const CallStats::RttTime& rtt_time : *reports)
    max_rtt_ms = std::max(rtt_time.rtt, max_rtt_ms);
  return max_rtt_ms;
}

计算 reports_ 中 rrt 时间的平均值:

int64_t GetAvgRttMs(std::list<CallStats::RttTime>* reports) 
{
  if (reports->empty()) 
  {
    return -1;
  }

  int64_t sum = 0;
  for (std::list<CallStats::RttTime>::const_iterator it = reports->begin(); it != reports->end(); ++it) 
  {
    sum += it->rtt;
  }

  return sum / reports->size();
}

计算最新的 avg_rtt_ms_:

void UpdateAvgRttMs(std::list<CallStats::RttTime>* reports, int64_t* avg_rtt) 
{
  /* 获取 list:reports_ 中 rrt 时间的平均值 */
  int64_t cur_rtt_ms = GetAvgRttMs(reports);

  // list:reports_ 为空,获取 rrt 平均值失败
  if (cur_rtt_ms == -1) 
  {
    // Reset.
    *avg_rtt = -1;
    return;
  }

  if (*avg_rtt == -1) 
  {
    // 第一次获取 avg_rtt,直接初始化 avg_rtt,
    *avg_rtt = cur_rtt_ms;
    return;
  }

  /* 新 avg_rtt 由 旧 avg_rtt(70%) 和 本次计算的平均值(30%) 的加权和构成  */ 
  *avg_rtt = *avg_rtt * (1.0f - kWeightFactor) + cur_rtt_ms * kWeightFactor;
}

你可能感兴趣的:(webrtc)