WebRTC源码之视频质量统计数据的数据结构分析

视频质量(Qos)、数据统计

Posted by chensong on 2022-07-26 23::50::33

WebRTC源码之视频质量统计数据的数据结构分析

@TOC

</font>

<hr style=” border:solid; width:100px; height:1px;” color=#000000 size=1”>

WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

1、WebRTC中线程模型和常见线程模型介绍

2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒体协商

1、WebRTC媒体协商之SDP中JsepSessionDescription类结构分析

三、 WebRTC 音频数据采集

1、WebRTC源码之音频设备播放流程源码分析

2、WebRTC源码之音频设备的录制流程源码分析

四、 WebRTC 音频引擎(编解码和3A算法)

五、 WebRTC 视频数据采集

六、 WebRTC 视频引擎( 编解码)

七、 WebRTC 网络传输

1、WebRTC的ICE之STUN协议

2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解

八、 WebRTC服务质量(Qos)

1、WebRTC中RTCP协议详解

2、WebRTC中RTP协议详解

3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

4、WebRTC源码之视频质量统计数据的数据结构分析

九、 NetEQ

十、 Simulcast与SVC

前言

分析视频数据

video/send_statistics_proxy.h中进行数据统计 中基础数据结构支撑的

视频质量统计数据的数据结构分析

目录位置

src/video/stats_counter.h
src/video/stats_counter.cc
1. 平均计数器 2. 最大计数器 3. 百分比计数器 4. 千分比计数器 5. 速率计数器 6. 速率Acc计数器

一、 工具类介绍 (定期计算的指标的最小值、平均值和最大值)

  • AggregatedStats 定期计算的指标的最小值、平均值和最大值
  • AggregatedCounter 定期计算的指标的最小值、平均值和最大值
  • Samples 间隔内保存收集的样本

1、AggregatedStats

//聚合统计
struct AggregatedStats 
{
  std::string ToString() const;
  std::string ToStringWithMultiplier(int multiplier) const;

  int64_t num_samples = 0; // 次数叠加值
  int min = -1;
  int max = -1;
  int average = -1;
  // TODO(asapersson): Consider adding median/percentiles.
};

2、AggregatedCounter

//持有定期计算的指标
//
class AggregatedCounter {
 public:
  AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
  ~AggregatedCounter() {}

  void Add(int sample) {
    last_sample_ = sample;
    sum_samples_ += sample;
    ++stats_.num_samples;
    if (stats_.num_samples == 1) {
      stats_.min = sample;
      stats_.max = sample;
    }
    stats_.min = std::min(sample, stats_.min);
    stats_.max = std::max(sample, stats_.max);
  }

  AggregatedStats ComputeStats() {
    Compute();
    return stats_;
  }

  bool Empty() const { return stats_.num_samples == 0; }

  int last_sample() const { return last_sample_; }

 private:
  void Compute() {
    if (stats_.num_samples == 0) {
      return;
    }
    // 平均值  公式 = ( 每个数据的叠加值  + 次数的平均数)/ 次数
    stats_.average =
        (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
  }
  int last_sample_;        //记录上一次的数据值
  int64_t sum_samples_;    // 每个数据的叠加值
  AggregatedStats stats_;  // 记录次数 和 min、avg、max
};

3、 Samples 间隔内保存收集的样本

// 类在进程间隔内保存收集的样本
class Samples {
 public:
  Samples() : total_count_(0) {}
  ~Samples() {}

  void Add(int sample, uint32_t stream_id) 
  {
    samples_[stream_id].Add(sample);
    ++total_count_;
  }

  void Set(int64_t sample, uint32_t stream_id) 
  {
    samples_[stream_id].Set(sample);
	//改变总数据的后 为什么还要修改 总次数呢???
	++total_count_;
  }
  void SetLast(int64_t sample, uint32_t stream_id) 
  {
    samples_[stream_id].SetLast(sample);
  }
  int64_t GetLast(uint32_t stream_id) { return samples_[stream_id].GetLast(); }

  int64_t Count() const { return total_count_; }
  bool Empty() const { return total_count_ == 0; }

  int64_t Sum() const 
  {
    int64_t sum = 0;
    for (const auto& it : samples_)
    {
       sum += it.second.sum_;
	}
    return sum;
  }

  int Max() const 
  {
    int max = std::numeric_limits<int>::min();
    for (const auto& it : samples_)
    {
      max = std::max(it.second.max_, max);
	}
    return max;
  }

  void Reset() {
    for (auto& it : samples_) 
	{
      it.second.Reset();
    }
    total_count_ = 0;
  }

  // 这个接口意义是什么???
  int64_t Diff() const 
  {
    int64_t sum_diff = 0;
    int count = 0;
    for (const auto& it : samples_) 
	{
      if (it.second.count_ > 0) 
	  {
        int64_t diff = it.second.sum_ - it.second.last_sum_;
        if (diff >= 0) 
		{
          sum_diff += diff;
          ++count;
        }
      }
    }
    return (count > 0) ? sum_diff : -1;
  }

 private:
  struct Stats {
    void Add(int sample)
	{
      sum_ += sample;
      ++count_;
      max_ = std::max(sample, max_);
    }
    void Set(int64_t sample)
	{
      sum_ = sample;
      ++count_;
    }
    void SetLast(int64_t sample) { last_sum_ = sample; }
    int64_t GetLast() const { return last_sum_; }
    void Reset() {
      if (count_ > 0) {
        last_sum_ = sum_;
      }
      sum_ = 0;
      count_ = 0;
      max_ = std::numeric_limits<int>::min();
    }

    int max_ = std::numeric_limits<int>::min();
    int64_t count_ = 0;
    int64_t sum_ = 0;
    int64_t last_sum_ = 0;
  };

  int64_t total_count_;
  std::map<uint32_t, Stats> samples_;  //key : 视频流的id 和统计的数据 Gathered samples mapped by stream id.
};

二、 视频数据统计信息

1、StatsCounter 视频统计中基类

// Classes which periodically computes a metric.
//
// During a period, |kProcessIntervalMs|, different metrics can be computed e.g:
// - |AvgCounter|: average of samples
// - |PercentCounter|: percentage of samples
// - |PermilleCounter|: permille of samples
//
// Each periodic metric can be either:
// - reported to an |observer| each period
// - aggregated during the call (e.g. min, max, average)
//
//                 periodically computed
//                    GetMetric()            GetMetric()   => AggregatedStats
//                        ^                      ^            (e.g. min/max/avg)
//                        |                      |
// |   *    *  *       *  |  **    *   * *     * | ...
// |<- process interval ->|
//
// (*) - samples
//
//
// Example usage:
//
// AvgCounter counter(&clock, nullptr);
// counter.Add(5);
// counter.Add(1);
// counter.Add(6);   // process interval passed -> GetMetric() avg:4
// counter.Add(7);
// counter.Add(3);   // process interval passed -> GetMetric() avg:5
// counter.Add(10);
// counter.Add(20);  // process interval passed -> GetMetric() avg:15
// AggregatedStats stats = counter.GetStats();
// stats: {min:4, max:15, avg:8}
//

// Note: StatsCounter takes ownership of |observer|.

class StatsCounter {
 public:
  virtual ~StatsCounter();

  // Gets metric within an interval. Returns true on success false otherwise.
  // 1. 获取间隔内的度量。成功时返回true,否则返回false。
  virtual bool GetMetric(int* metric) const = 0;

  // Gets the value to use for an interval without samples.
  // 2. 获取用于不带样本的间隔的值
  virtual int GetValueForEmptyInterval() const = 0;

  // Gets aggregated stats (i.e. aggregate of periodically computed metrics).
  // 3. 获取聚合的统计信息(即定期计算的指标的聚合) [min、avg、max]
  AggregatedStats GetStats();

  // Reports metrics for elapsed intervals to AggregatedCounter and GetStats.
  // 4. 向AggregatedCounter和GetStats报告已用时间间隔的指标
  AggregatedStats ProcessAndGetStats();

  // Reports metrics for elapsed intervals to AggregatedCounter and pauses stats
  // (i.e. empty intervals will be discarded until next sample is added).
  // 5. 向AggregatedCounter报告已用时间间隔的指标,并暂停统计数据
  // (即,在添加下一个样本之前,将丢弃空间隔)。
  void ProcessAndPause();

  // As above with a minimum pause time. Added samples within this interval will
  // not resume the stats (i.e. stop the pause).
  // 6. 如上所述,暂停时间最短。在此间隔内添加的样本将
  // 不恢复统计(即停止暂停)。   暂停毫秒数
  void ProcessAndPauseForDuration(int64_t min_pause_time_ms);

  // Reports metrics for elapsed intervals to AggregatedCounter and stops pause.
  // 7. 向AggregatedCounter报告已用时间间隔的指标,并停止暂停。
  void ProcessAndStopPause();

  // Checks if a sample has been added (i.e. Add or Set called).
  // 8. 检查是否已添加样本(即添加或设置调用
  bool HasSample() const;

 protected:
  StatsCounter(Clock* clock,
               int64_t process_intervals_ms,
               bool include_empty_intervals,
               StatsCounterObserver* observer);

  void Add(int sample);
  void Set(int64_t sample, uint32_t stream_id);
  void SetLast(int64_t sample, uint32_t stream_id);

  const bool include_empty_intervals_;
  const int64_t process_intervals_ms_;
  const std::unique_ptr<AggregatedCounter> aggregated_counter_;
  const std::unique_ptr<Samples> samples_;

 private:
  bool TimeToProcess(int* num_elapsed_intervals);
  void TryProcess();
  void ReportMetricToAggregatedCounter(int value, int num_values_to_add) const;
  bool IncludeEmptyIntervals() const;
  void Resume();
  void ResumeIfMinTimePassed();

  Clock* const clock_;
  const std::unique_ptr<StatsCounterObserver> observer_;
  int64_t last_process_time_ms_; // 记录数据 - 毫秒数
  bool paused_;
  int64_t pause_time_ms_;
  int64_t min_pause_time_ms_;
};

// StatsCounter class.
StatsCounter::StatsCounter(Clock* clock,
                           int64_t process_intervals_ms,
                           bool include_empty_intervals,
                           StatsCounterObserver* observer)
    : include_empty_intervals_(include_empty_intervals),
      process_intervals_ms_(process_intervals_ms),
      aggregated_counter_(new AggregatedCounter()),
      samples_(new Samples()),
      clock_(clock),
      observer_(observer),
      last_process_time_ms_(-1),
      paused_(false),
      pause_time_ms_(-1),
      min_pause_time_ms_(0) {
  RTC_DCHECK_GT(process_intervals_ms_, 0);
}

StatsCounter::~StatsCounter() {}

AggregatedStats StatsCounter::GetStats() {
  return aggregated_counter_->ComputeStats();
}

AggregatedStats StatsCounter::ProcessAndGetStats() {
  if (HasSample()) {
    TryProcess();
  }
  return aggregated_counter_->ComputeStats();
}

void StatsCounter::ProcessAndPauseForDuration(int64_t min_pause_time_ms) {
  ProcessAndPause();
  min_pause_time_ms_ = min_pause_time_ms;
}

void StatsCounter::ProcessAndPause() {
  if (HasSample()) 
  {
    TryProcess();
  }
  paused_ = true;
  pause_time_ms_ = clock_->TimeInMilliseconds();
}

void StatsCounter::ProcessAndStopPause() 
{
  if (HasSample()) 
  {
    TryProcess();
  }
  Resume();
}

bool StatsCounter::HasSample() const 
{
  return last_process_time_ms_ != -1;
}

bool StatsCounter::TimeToProcess(int* elapsed_intervals) 
{
  int64_t now = clock_->TimeInMilliseconds();
  if (last_process_time_ms_ == -1) 
  {
    last_process_time_ms_ = now;
  }

  int64_t diff_ms = now - last_process_time_ms_;
  if (diff_ms < process_intervals_ms_) 
  {
    return false;
  }

  // Advance number of complete |process_intervals_ms_| that have passed.
  // 时间间隔中毫秒数 调整差值 [多减去的毫秒数 添加上]
  int64_t num_intervals = diff_ms / process_intervals_ms_;
  last_process_time_ms_ += num_intervals * process_intervals_ms_;

  *elapsed_intervals = num_intervals;
  return true;
}

void StatsCounter::Add(int sample) 
{
  TryProcess();
  samples_->Add(sample, kStreamId0);
  ResumeIfMinTimePassed();
}

void StatsCounter::Set(int64_t sample, uint32_t stream_id) 
{
  if (paused_ && sample == samples_->GetLast(stream_id)) 
  {
    // Do not add same sample while paused (will reset pause).
    return;
  }
  TryProcess();
  samples_->Set(sample, stream_id);
  ResumeIfMinTimePassed();
}

void StatsCounter::SetLast(int64_t sample, uint32_t stream_id) {
  RTC_DCHECK(!HasSample()) << "Should be set before first sample is added.";
  samples_->SetLast(sample, stream_id);
}

// Reports periodically computed metric.
void StatsCounter::ReportMetricToAggregatedCounter(
    int value,
    int num_values_to_add) const 
{
  for (int i = 0; i < num_values_to_add; ++i) 
  {
    aggregated_counter_->Add(value);
    if (observer_) 
	{
      observer_->OnMetricUpdated(value);
    }
  }
}

void StatsCounter::TryProcess() 
{
  int elapsed_intervals;
  if (!TimeToProcess(&elapsed_intervals)) 
  {
    return;
  }

  // Get and report periodically computed metric.
  int metric;
  if (GetMetric(&metric)) {
    ReportMetricToAggregatedCounter(metric, 1);
  }

  // Report value for elapsed intervals without samples.
  if (IncludeEmptyIntervals()) {
    // If there are no samples, all elapsed intervals are empty (otherwise one
    // interval contains sample(s), discard this interval).
    int empty_intervals =
        samples_->Empty() ? elapsed_intervals : (elapsed_intervals - 1);
    ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
                                    empty_intervals);
  }

  // Reset samples for elapsed interval.
  samples_->Reset();
}

bool StatsCounter::IncludeEmptyIntervals() const {
  return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
}
void StatsCounter::ResumeIfMinTimePassed() {
  if (paused_ && (clock_->TimeInMilliseconds() - pause_time_ms_) >= min_pause_time_ms_) 
  {
    Resume();
  }
}

void StatsCounter::Resume() {
  paused_ = false;
  min_pause_time_ms_ = 0;
}

2、AvgCounter 平均计数器


// AvgCounter: average of samples
//
//           | *      *      *      | *           *       | ...
//           | Add(5) Add(1) Add(6) | Add(5)      Add(5)  |
// GetMetric | (5 + 1 + 6) / 3      | (5 + 5) / 2         |
//
// |include_empty_intervals|: If set, intervals without samples will be included
//                            in the stats. The value for an interval is
//                            determined by GetValueForEmptyInterval().
//
class AvgCounter : public StatsCounter {
 public:
  AvgCounter(Clock* clock,
             StatsCounterObserver* observer,
             bool include_empty_intervals);
  ~AvgCounter() override {}

  void Add(int sample);

 private:
  bool GetMetric(int* metric) const override;

  // Returns the last computed metric (i.e. from GetMetric).
  int GetValueForEmptyInterval() const override;

  RTC_DISALLOW_COPY_AND_ASSIGN(AvgCounter);
};

// StatsCounter sub-classes.
AvgCounter::AvgCounter(Clock* clock,
                       StatsCounterObserver* observer,
                       bool include_empty_intervals)
    : StatsCounter(clock,
                   kDefaultProcessIntervalMs,
                   include_empty_intervals,
                   observer) {}

void AvgCounter::Add(int sample) {
  StatsCounter::Add(sample);
}

bool AvgCounter::GetMetric(int* metric) const {
  int64_t count = samples_->Count();
  if (count == 0) {
    return false;
  }
  //  [count / 2] : 次数的平均值
  *metric = (samples_->Sum() + count / 2) / count;
  return true;
}

int AvgCounter::GetValueForEmptyInterval() const {
  return aggregated_counter_->last_sample();
}

3、MaxCounter

// MaxCounter: maximum of samples
//
//           | *      *      *      | *           *       | ...
//           | Add(5) Add(1) Add(6) | Add(5)      Add(5)  |
// GetMetric | max: (5, 1, 6)       | max: (5, 5)         |
//
class MaxCounter : public StatsCounter {
 public:
  MaxCounter(Clock* clock,
             StatsCounterObserver* observer,
             int64_t process_intervals_ms);
  ~MaxCounter() override {}

  void Add(int sample);

 private:
  bool GetMetric(int* metric) const override;
  int GetValueForEmptyInterval() const override;

  RTC_DISALLOW_COPY_AND_ASSIGN(MaxCounter);
};


MaxCounter::MaxCounter(Clock* clock,
                       StatsCounterObserver* observer,
                       int64_t process_intervals_ms)
    : StatsCounter(clock,
                   process_intervals_ms,
                   false,  // |include_empty_intervals|
                   observer) {}

void MaxCounter::Add(int sample) {
  StatsCounter::Add(sample);
}

bool MaxCounter::GetMetric(int* metric) const {
  if (samples_->Empty())
    return false;

  *metric = samples_->Max();
  return true;
}

int MaxCounter::GetValueForEmptyInterval() const {
  RTC_NOTREACHED();
  return 0;
}

4、PercentCounter 百分比计数器

// PercentCounter: percentage of samples
//
//           | *      *      *      | *           *       | ...
//           | Add(T) Add(F) Add(T) | Add(F)      Add(T)  |
// GetMetric | 100 * 2 / 3          | 100 * 1 / 2         |
//  百分比
class PercentCounter : public StatsCounter {
 public:
  PercentCounter(Clock* clock, StatsCounterObserver* observer);
  ~PercentCounter() override {}

  void Add(bool sample);

 private:
  bool GetMetric(int* metric) const override;
  int GetValueForEmptyInterval() const override;

  RTC_DISALLOW_COPY_AND_ASSIGN(PercentCounter);
};

PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
    : StatsCounter(clock,
                   kDefaultProcessIntervalMs,
                   false,  // |include_empty_intervals|
                   observer) {}

void PercentCounter::Add(bool sample) {
  StatsCounter::Add(sample ? 1 : 0);
}

bool PercentCounter::GetMetric(int* metric) const {
  int64_t count = samples_->Count();
  if (count == 0)
    return false;

  *metric = (samples_->Sum() * 100 + count / 2) / count;
  return true;
}

int PercentCounter::GetValueForEmptyInterval() const {
  RTC_NOTREACHED();
  return 0;
}

5、PermilleCounter 千分比计数器

// PermilleCounter: permille of samples
//
//           | *      *      *      | *         *         | ...
//           | Add(T) Add(F) Add(T) | Add(F)    Add(T)    |
// GetMetric | 1000 *  2 / 3        | 1000 * 1 / 2        |
//
class PermilleCounter : public StatsCounter {
 public:
  PermilleCounter(Clock* clock, StatsCounterObserver* observer);
  ~PermilleCounter() override {}

  void Add(bool sample);

 private:
  bool GetMetric(int* metric) const override;
  int GetValueForEmptyInterval() const override;

  RTC_DISALLOW_COPY_AND_ASSIGN(PermilleCounter);
};


PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
    : StatsCounter(clock,
                   kDefaultProcessIntervalMs,
                   false,  // |include_empty_intervals|
                   observer) {}

void PermilleCounter::Add(bool sample) {
  StatsCounter::Add(sample ? 1 : 0);
}

bool PermilleCounter::GetMetric(int* metric) const {
  int64_t count = samples_->Count();
  if (count == 0)
    return false;

  *metric = (samples_->Sum() * 1000 + count / 2) / count;
  return true;
}

int PermilleCounter::GetValueForEmptyInterval() const {
  RTC_NOTREACHED();
  return 0;
}

6、RateCounter 速率计数器

// RateCounter: units per second
//
//           | *      *      *      | *           *       | ...
//           | Add(5) Add(1) Add(6) | Add(5)      Add(5)  |
//           |<------ 2 sec ------->|                     |
// GetMetric | (5 + 1 + 6) / 2      | (5 + 5) / 2         |
//
// |include_empty_intervals|: If set, intervals without samples will be included
//                            in the stats. The value for an interval is
//                            determined by GetValueForEmptyInterval().
//include_empty_intervals: 如果设置,将包括没有样本的间隔
//在统计中。间隔的值为
//由GetValueForEmptyInterval()确定。
//
class RateCounter : public StatsCounter {
 public:
  RateCounter(Clock* clock,
              StatsCounterObserver* observer,
              bool include_empty_intervals);
  ~RateCounter() override {}

  void Add(int sample);

 private:
  bool GetMetric(int* metric) const override;
  int GetValueForEmptyInterval() const override;  // Returns zero.

  RTC_DISALLOW_COPY_AND_ASSIGN(RateCounter);
};

7、RateAccCounter 速率Acc计数器


// RateAccCounter: units per second (used for counters)
//
//           | *      *      *      | *         *         | ...
//           | Set(5) Set(6) Set(8) | Set(11)   Set(13)   |
//           |<------ 2 sec ------->|                     |
// GetMetric | (8 - 0) / 2          | (13 - 8) / 2        |
//
// |include_empty_intervals|: If set, intervals without samples will be included
//                            in the stats. The value for an interval is
//                            determined by GetValueForEmptyInterval().
//
class RateAccCounter : public StatsCounter {
 public:
  RateAccCounter(Clock* clock,
                 StatsCounterObserver* observer,
                 bool include_empty_intervals);
  ~RateAccCounter() override {}

  void Set(int64_t sample, uint32_t stream_id);

  // Sets the value for previous interval.
  // To be used if a value other than zero is initially required.
  void SetLast(int64_t sample, uint32_t stream_id);

 private:
  bool GetMetric(int* metric) const override;
  int GetValueForEmptyInterval() const override;  // Returns zero.

  RTC_DISALLOW_COPY_AND_ASSIGN(RateAccCounter);
};
RateCounter::RateCounter(Clock* clock,
                         StatsCounterObserver* observer,
                         bool include_empty_intervals)
    : StatsCounter(clock,
                   kDefaultProcessIntervalMs /*2000*/,
                   include_empty_intervals,
                   observer) {}

void RateCounter::Add(int sample) {
  StatsCounter::Add(sample);
}

bool RateCounter::GetMetric(int* metric) const {
  if (samples_->Empty())
    return false;

  *metric = (samples_->Sum() * 1000 + process_intervals_ms_ / 2) /
            process_intervals_ms_;
  return true;
}

int RateCounter::GetValueForEmptyInterval() const {
  return 0;
}

总结:

WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc