Mediasoup之RateCalculator(流量统计)
一、Mediasoup之RateCalculator(流量统计)
mediasoup中使用RateCalculator记录每个毫秒数比特流的数据的,
二、 分析RateCalculator类的方法
大致的思想是: 滑动窗口总时间和滑动窗口个数, 就可以得到每个具体滑动窗口占有毫秒, 就是每个窗口比特流 总和 除以总窗口的数量就是大致的比特流
class RateCalculator
{
public:
static constexpr size_t DefaultWindowSize{ 1000u }; // 默认滑动窗口占有毫秒数
static constexpr float DefaultBpsScale{ 8000.0f };// 比特流
static constexpr uint16_t DefaultWindowItems{ 100u };// 滑动窗口有多少个
public:
RateCalculator(
size_t windowSizeMs = DefaultWindowSize,
float scale = DefaultBpsScale,
uint16_t windowItems = DefaultWindowItems)
: windowSizeMs(windowSizeMs), scale(scale), windowItems(windowItems)
{
this->itemSizeMs = std::max(windowSizeMs / windowItems, static_cast<size_t>(1));
Reset();
}
void Update(size_t size, uint64_t nowMs);
uint32_t GetRate(uint64_t nowMs);
size_t GetBytes() const
{
return this->bytes;
}
private:
void RemoveOldData(uint64_t nowMs);
void Reset()
{
this->buffer.reset(new BufferItem[this->windowItems]);
this->newestItemStartTime = 0u;
this->newestItemIndex = -1;
this->oldestItemStartTime = 0u;
this->oldestItemIndex = -1;
this->totalCount = 0u;
this->lastRate = 0u;
this->lastTime = 0u;
}
private:
struct BufferItem
{
size_t count{ 0u };
uint64_t time{ 0u };
};
private:
// Window Size (in milliseconds).
size_t windowSizeMs{ DefaultWindowSize }; // 滑动窗口的大小毫秒数
// Scale in which the rate is represented.
float scale{ DefaultBpsScale }; // 表示比率的刻度。
// Window Size (number of items).
uint16_t windowItems{ DefaultWindowItems }; // 滑动窗口的数组个数
// Item Size (in milliseconds), calculated as: windowSizeMs / windowItems.
size_t itemSizeMs{ 0u }; // 每个滑动窗口占有毫秒数
// Buffer to keep data.
std::unique_ptr<BufferItem[]> buffer; // 滑动窗口的数组记录数据集
// Time (in milliseconds) for last item in the time window.
uint64_t newestItemStartTime{ 0u }; // 当前最新窗口的时间毫秒数
// Index for the last item in the time window.
int32_t newestItemIndex{ -1 }; // 最新的窗口的下标
// Time (in milliseconds) for oldest item in the time window.
uint64_t oldestItemStartTime{ 0u }; // 总是记录滑动窗口中最小毫秒数
// Index for the oldest item in the time window.
int32_t oldestItemIndex{ -1 }; // 最老的下标
// Total count in the time window.
size_t totalCount{ 0u }; // 数据同和当前
// Total bytes transmitted.
size_t bytes{ 0u };
// Last value calculated by GetRate().
uint32_t lastRate{ 0u };
// Last time GetRate() was called.
uint64_t lastTime{ 0u };
};
void RateCalculator::Update(size_t size, uint64_t nowMs)
{
MS_TRACE();
// Ignore too old data. Should never happen.
if (nowMs < this->oldestItemStartTime)
{
return;
}
// Increase bytes.
this->bytes += size;
RemoveOldData(nowMs);
// If the elapsed time from the newest item start time is greater than the
// item size (in milliseconds), increase the item index.
if (this->newestItemIndex < 0 || nowMs - this->newestItemStartTime >= this->itemSizeMs)
{
this->newestItemIndex++;
this->newestItemStartTime = nowMs;
if (this->newestItemIndex >= this->windowItems)
{
this->newestItemIndex = 0;
}
// Newest index overlaps with the oldest one, remove it.
if (this->newestItemIndex == this->oldestItemIndex && this->oldestItemIndex != -1)
{
MS_WARN_TAG(
info,
"calculation buffer full, windowSizeMs:%zu ms windowItems:%" PRIu16,
this->windowSizeMs,
this->windowItems);
BufferItem& oldestItem = buffer[this->oldestItemIndex];
this->totalCount -= oldestItem.count;
oldestItem.count = 0u;
oldestItem.time = 0u;
if (++this->oldestItemIndex >= this->windowItems)
{
this->oldestItemIndex = 0;
}
}
// Set the newest item.
BufferItem& item = buffer[this->newestItemIndex];
item.count = size;
item.time = nowMs;
}
else
{
// Update the newest item.
BufferItem& item = buffer[this->newestItemIndex];
item.count += size;
}
// Set the oldest item index and time, if not set.
if (this->oldestItemIndex < 0)
{
this->oldestItemIndex = this->newestItemIndex;
this->oldestItemStartTime = nowMs;
}
this->totalCount += size;
// Reset lastRate and lastTime so GetRate() will calculate rate again even
// if called with same now in the same loop iteration.
this->lastRate = 0;
this->lastTime = 0;
}
uint32_t RateCalculator::GetRate(uint64_t nowMs)
{
MS_TRACE();
if (nowMs == this->lastTime)
{
return this->lastRate;
}
RemoveOldData(nowMs);
// 比特流 / 总的滑动窗口毫秒数
float scale = this->scale / this->windowSizeMs;
this->lastTime = nowMs;
// 具体就是滑动窗口比特流的公式 = 总滑动窗口中有的数据的总和 * 8000 / windowsizems ;
// 原来我以为有什么特殊公式呢!!!
this->lastRate = static_cast<uint32_t>(std::trunc(this->totalCount * scale + 0.5f));
//trunc()函数是cmath标头的库函数,用于将值四舍五入(截断)为零,它接受一个数字并返回其大小不大于给定数字的最近整数值。
return this->lastRate;
}
inline void RateCalculator::RemoveOldData(uint64_t nowMs)
{
MS_TRACE();
// No item set.
if (this->newestItemIndex < 0 || this->oldestItemIndex < 0)
{
return;
}
// 1. 这个值是根据 当前毫秒数减去全部的滑动窗口时间来计算的
uint64_t newoldestTime = nowMs - this->windowSizeMs;
// 2. 判断是否小于滑动窗口中最小的时间毫秒数 , 如果小于就不需要处理哈(滑动窗口中没有计算该值哈)
// Oldest item already removed.
if (newoldestTime <= this->oldestItemStartTime)
{
return;
}
// 说明上次发送数据时间长太长, 超出滑动窗口的时间长 需要重置滑动窗口
// A whole window size time has elapsed since last entry. Reset the buffer.
if (newoldestTime > this->newestItemStartTime)
{
Reset();
return;
}
// 移除超出滑动窗口时间的数据 count
while (this->oldestItemStartTime < newoldestTime)
{
BufferItem& oldestItem = buffer[this->oldestItemIndex];
this->totalCount -= oldestItem.count;
oldestItem.count = 0u;
oldestItem.time = 0u;
if (++this->oldestItemIndex >= this->windowItems)
{
this->oldestItemIndex = 0;
}
const BufferItem& newOldestItem = buffer[this->oldestItemIndex];
// 总是记录滑动窗口中最小毫秒数
this->oldestItemStartTime = newOldestItem.time;
}
}
三、 比特流的公式
公式 = 总滑动窗口中有的数据的总和 * 8000 / windowsizems ;
对应的代码
// 比特流 / 总的滑动窗口毫秒数
float scale = this->scale / this->windowSizeMs;
this->lastTime = nowMs;
// 具体就是滑动窗口比特流的公式 = 总滑动窗口中有的数据的总和 * 8000 / windowsizems ;
this->lastRate = static_cast<uint32_t>(std::trunc(this->totalCount * scale + 0.5f));
总结:
在Mediasoup中RateCalculator也没有啥大的作用, 作为流量统计数据分析
源码地址:https://github.com/chensongpoixs/cmediasoup/blob/master/src/RTC/RateCalculator.cpp