WebRTC源码之视频质量统计数据的数据结构分析
@TOC
</font>
<hr style=” border:solid; width:100px; height:1px;” color=#000000 size=1”>
WebRTC专题开嗨鸭 !!!一、 WebRTC 线程模型
2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用
二、 WebRTC媒体协商
1、WebRTC媒体协商之SDP中JsepSessionDescription类结构分析
三、 WebRTC 音频数据采集
四、 WebRTC 音频引擎(编解码和3A算法)
五、 WebRTC 视频数据采集
六、 WebRTC 视频引擎( 编解码)
七、 WebRTC 网络传输
2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解
八、 WebRTC服务质量(Qos)
3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传
九、 NetEQ
十、 Simulcast与SVC
前言
分析视频数据
video/send_statistics_proxy.h中进行数据统计 中基础数据结构支撑的
视频质量统计数据的数据结构分析
目录位置
src/video/stats_counter.h
src/video/stats_counter.cc
一、 工具类介绍 (定期计算的指标的最小值、平均值和最大值)
- 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