Created
July 21, 2023 07:42
-
-
Save IMelker/16f26747fda4c21ef56eb5aad501d2ea to your computer and use it in GitHub Desktop.
Bayesian bitrate estimator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cmath> | |
#define INITIAL_WINDOW_MS 500 | |
#define NONINITIAL_WINDOWS_MS 150 | |
#define UNCERTAINTY_SCALE 10.0f | |
#define UNCERTAINTY_SYMMETRY_CAP 0.0f | |
#define ESTIMATE_FLOOR_KBPS 0.0f | |
class BitrateEstimator { | |
public: | |
BitrateEstimator(); | |
~BitrateEstimator() = default; | |
void update(int64_t nowMs, int dataSize); | |
float bitrate() const; | |
private: | |
float updateWindow(int64_t nowMs, int bytes, int rateWindowMs); | |
int sum = 0; | |
int64_t currentWindowMs= 0; | |
int64_t prevTimeMs = -1; | |
float bitrateEstimateKbps = -1.0f; | |
float bitrateEstimateVar = 50.0f; | |
}; | |
BitrateEstimator::BitrateEstimator() { | |
// A higher bitrate-estimate variance value allows the bitrate to change fast for the next few samples. | |
bitrateEstimateVar += 200; | |
} | |
void BitrateEstimator::update(int64_t nowMs, int dataSize) { | |
int rateWindowMs = NONINITIAL_WINDOWS_MS; | |
// We use a larger window at the beginning to get a more stable sample. | |
if (bitrateEstimateKbps < 0.f) | |
rateWindowMs = INITIAL_WINDOW_MS; | |
float bitrateSampleKbps = updateWindow(nowMs, dataSize, rateWindowMs); | |
if (bitrateSampleKbps < 0.0f) | |
return; | |
if (bitrateEstimateKbps < 0.0f) { | |
// First sample to initialize the estimate. | |
bitrateEstimateKbps = bitrateSampleKbps; | |
return; | |
} | |
// Define the sample uncertainty as a function of how far away it is from the current estimate. | |
// With low values of UNCERTAINTY_SYMMETRY_CAP we add more uncertainty to increases than to decreases. | |
// For higher values we approach symmetry. | |
float sampleUncertainty = UNCERTAINTY_SCALE * std::abs(bitrateEstimateKbps - bitrateSampleKbps) / | |
(bitrateEstimateKbps + std::min(bitrateSampleKbps, UNCERTAINTY_SYMMETRY_CAP)); | |
float sampleVar = sampleUncertainty * sampleUncertainty; | |
// Update a bayesian estimate of the rate, weighting it lower if the sample uncertainty is large. | |
// The bitrate estimate uncertainty is increased with each update to model that the bitrate changes over time. | |
float predBitrateEstimateVar = bitrateEstimateVar + 5.f; | |
bitrateEstimateKbps = (sampleVar * bitrateEstimateKbps + predBitrateEstimateVar * bitrateSampleKbps) / | |
(sampleVar + predBitrateEstimateVar); | |
bitrateEstimateKbps = std::max(bitrateEstimateKbps, ESTIMATE_FLOOR_KBPS); | |
bitrateEstimateVar = sampleVar * predBitrateEstimateVar / | |
(sampleVar + predBitrateEstimateVar); | |
} | |
float BitrateEstimator::updateWindow(int64_t nowMs, int bytes, int rateWindowMs) { | |
// Reset if time moves backwards. | |
if (nowMs < prevTimeMs) { | |
prevTimeMs = -1; | |
sum = 0; | |
currentWindowMs = 0; | |
} | |
if (prevTimeMs >= 0) { | |
currentWindowMs += nowMs - prevTimeMs; | |
// Reset if nothing has been received for more than a full window. | |
if (nowMs - prevTimeMs > rateWindowMs) { | |
sum = 0; | |
currentWindowMs %= rateWindowMs; | |
} | |
} | |
prevTimeMs = nowMs; | |
float bitrateSample = -1.0f; | |
if (currentWindowMs >= rateWindowMs) { | |
bitrateSample = 8.0f * sum / static_cast<float>(rateWindowMs); | |
currentWindowMs -= rateWindowMs; | |
sum = 0; | |
} | |
sum += bytes; | |
return bitrateSample; | |
} | |
float BitrateEstimator::bitrate() const { | |
return std::max(bitrateEstimateKbps, ESTIMATE_FLOOR_KBPS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment