Bug 1890467 consider buffering level variation in more measurements when deciding whether to reduce desired buffering r=pehrsons

Instead of once a second, buffering level measurements are performed after
each output packet for which an input packet has recently arrived.

AudioDriftCorrection::RequestFrames() targets desired buffering at
13/10 * MeasuredSourceLatency() if actual buffering stays near desired.
20% less than this is 104/100 * MeasuredSourceLatency().  When input packets
are much larger than output packets, then when this much is buffered after an
input packet arrives, then the buffer was close to underruning before the
packet arrived.

Differential Revision: https://phabricator.services.mozilla.com/D215489
This commit is contained in:
Karl Tomlinson
2024-07-11 22:28:15 +00:00
parent aa1c4b60e0
commit 32feff8698
2 changed files with 35 additions and 29 deletions

View File

@@ -81,6 +81,23 @@ uint32_t DriftController::GetCorrectedSourceRate() const {
return std::lround(mCorrectedSourceRate);
}
int64_t DriftController::NearThreshold() const {
// mDesiredBuffering is divided by this to calculate a maximum error that
// would be considered "near" desired buffering. A denominator of 5
// corresponds to an error of +/- 20% of the desired buffering.
static constexpr uint32_t kNearDenominator = 5; // +/- 20%
// +/- 10ms band maximum half-width.
const media::TimeUnit nearCap = media::TimeUnit::FromSeconds(0.01);
// For the minimum desired buffering of 10ms we have a "near" error band
// of +/- 2ms (20%). This goes up to +/- 10ms (clamped) at most for when the
// desired buffering is 50 ms or higher. AudioDriftCorrection uses this
// threshold when deciding whether to reduce buffering.
return std::min(nearCap, mDesiredBuffering / kNearDenominator)
.ToTicksAtRate(mSourceRate);
}
void DriftController::UpdateClock(media::TimeUnit aSourceDuration,
media::TimeUnit aTargetDuration,
uint32_t aBufferedFrames,
@@ -176,6 +193,17 @@ void DriftController::UpdateClock(media::TimeUnit aSourceDuration,
mStage1Buffered = mAvgBufferedFramesEst;
}
uint32_t desiredBufferedFrames = mDesiredBuffering.ToTicksAtRate(mSourceRate);
int32_t error =
(CheckedInt32(aBufferedFrames) - desiredBufferedFrames).value();
if (std::abs(error) > NearThreshold()) {
// The error is outside a threshold boundary.
mDurationNearDesired = media::TimeUnit::Zero();
} else {
// The error is within the "near" threshold boundaries.
mDurationNearDesired += mTargetClock;
};
if (mTargetClock >= mAdjustmentInterval) {
// The adjustment interval has passed. Recalculate.
CalculateCorrection(aBufferedFrames, aBufferSize);
@@ -194,35 +222,9 @@ void DriftController::CalculateCorrection(uint32_t aBufferedFrames,
// Use nominal (not corrected) source rate when interpreting desired
// buffering so that the set point is independent of the control value.
uint32_t desiredBufferedFrames = mDesiredBuffering.ToTicksAtRate(mSourceRate);
int32_t error =
(CheckedInt32(aBufferedFrames) - desiredBufferedFrames).value();
float avgError = static_cast<float>(mAvgBufferedFramesEst) -
static_cast<float>(desiredBufferedFrames);
// mDesiredBuffering is divided by this to calculate a maximum error that
// would be considered "near" desired buffering. A denominator of 5
// corresponds to an error of +/- 20% of the desired buffering.
static constexpr uint32_t kNearDenominator = 5; // +/- 20%
// +/- 10ms band maximum half-width.
const media::TimeUnit nearCap = media::TimeUnit::FromSeconds(0.01);
// For the minimum desired buffering of 10ms we have a "near" error band
// of +/- 2ms (20%). This goes up to +/- 10ms (clamped) at most for when the
// desired buffering is 50 ms or higher. AudioDriftCorrection uses this
// threshold when deciding whether to reduce buffering.
const auto nearThreshold =
std::min(nearCap, mDesiredBuffering / kNearDenominator)
.ToTicksAtRate(mSourceRate);
if (std::abs(error) > nearThreshold) {
// The error is outside a threshold boundary.
mDurationNearDesired = media::TimeUnit::Zero();
} else {
// The error is within the "near" threshold boundaries.
mDurationNearDesired += mTargetClock;
};
// rateError is positive when pushing the buffering towards the desired level.
float rateError =
(mCorrectedSourceRate - steadyStateRate) * std::copysign(1.f, avgError);
@@ -258,8 +260,11 @@ void DriftController::CalculateCorrection(uint32_t aBufferedFrames,
"%.2fms), buffering: %.2fms, desired buffering: %.2fms",
mSourceRate, mTargetRate, cappedRate, mTargetRate,
cappedRate - mCorrectedSourceRate,
media::TimeUnit(error, mSourceRate).ToSeconds() * 1000.0,
media::TimeUnit(nearThreshold, mSourceRate).ToSeconds() * 1000.0,
media::TimeUnit(CheckedInt64(aBufferedFrames) - desiredBufferedFrames,
mSourceRate)
.ToSeconds() *
1000.0,
media::TimeUnit(NearThreshold(), mSourceRate).ToSeconds() * 1000.0,
media::TimeUnit(aBufferedFrames, mSourceRate).ToSeconds() * 1000.0,
mDesiredBuffering.ToSeconds() * 1000.0);
@@ -275,7 +280,7 @@ void DriftController::CalculateCorrection(uint32_t aBufferedFrames,
aBufferSize, mMeasuredSourceLatency.mean().ToTicksAtRate(mSourceRate),
mMeasuredTargetLatency.mean().ToTicksAtRate(mTargetRate),
mInputDurationAvg * mSourceRate, mOutputDurationAvg * mTargetRate,
mSourceRate, mTargetRate, steadyStateRate, nearThreshold, correctedRate,
mSourceRate, mTargetRate, steadyStateRate, NearThreshold(), correctedRate,
hysteresisCorrectedRate, std::lround(mCorrectedSourceRate));
// Reset the counters to prepare for the next period.

View File

@@ -97,6 +97,7 @@ class DriftController final {
uint32_t aBufferSize);
private:
int64_t NearThreshold() const;
// Adjust mCorrectedSourceRate for the current values of mDriftEstimate and
// mAvgBufferedFramesEst - mDesiredBuffering.ToTicksAtRate(mSourceRate).
//