mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1891885 don't use mInternalInBuffer to drain resampler r=pehrsons
as this sometimes left unconsumed samples in the buffer, which interfered with the timing of the buffer size reset after an underrun. Differential Revision: https://phabricator.services.mozilla.com/D207659
This commit is contained in:
parent
465b642e9e
commit
607d2bdc0f
@ -107,13 +107,23 @@ class WavDumper {
|
||||
if (!mFile) {
|
||||
return;
|
||||
}
|
||||
WriteDumpFileHelper(aBuffer, aSamples);
|
||||
if (aBuffer) {
|
||||
WriteDumpFileHelper(aBuffer, aSamples);
|
||||
} else {
|
||||
constexpr size_t blockSize = 128;
|
||||
T block[blockSize] = {};
|
||||
for (size_t remaining = aSamples; remaining;) {
|
||||
size_t toWrite = std::min(remaining, blockSize);
|
||||
fwrite(block, sizeof(T), toWrite, mFile);
|
||||
remaining -= toWrite;
|
||||
}
|
||||
}
|
||||
fflush(mFile);
|
||||
}
|
||||
|
||||
private:
|
||||
void WriteDumpFileHelper(const int16_t* aInput, size_t aSamples) {
|
||||
mozilla::Unused << fwrite(aInput, sizeof(int16_t), aSamples, mFile);
|
||||
fflush(mFile);
|
||||
}
|
||||
|
||||
void WriteDumpFileHelper(const float* aInput, size_t aSamples) {
|
||||
@ -127,7 +137,6 @@ class WavDumper {
|
||||
MOZ_ASSERT(rv);
|
||||
}
|
||||
mozilla::Unused << fwrite(buf.Elements(), buf.Length(), 1, mFile);
|
||||
fflush(mFile);
|
||||
}
|
||||
|
||||
FILE* mFile = nullptr;
|
||||
|
@ -47,7 +47,9 @@ void DynamicResampler::EnsurePreBuffer(media::TimeUnit aDuration) {
|
||||
if (buffered.IsZero()) {
|
||||
// Wait for the first input segment before deciding how much to pre-buffer.
|
||||
// If it is large it indicates high-latency, and the buffer would have to
|
||||
// handle that.
|
||||
// handle that. This also means that the pre-buffer is not set up just
|
||||
// before a large input segment would extend the buffering beyond the
|
||||
// desired level.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,7 +95,6 @@ void DynamicResampler::ResampleInternal(const float* aInBuffer,
|
||||
MOZ_ASSERT(mInRate);
|
||||
MOZ_ASSERT(mOutRate);
|
||||
|
||||
MOZ_ASSERT(aInBuffer);
|
||||
MOZ_ASSERT(aInFrames);
|
||||
MOZ_ASSERT(*aInFrames > 0);
|
||||
MOZ_ASSERT(aOutBuffer);
|
||||
@ -125,7 +126,6 @@ void DynamicResampler::ResampleInternal(const int16_t* aInBuffer,
|
||||
MOZ_ASSERT(mInRate);
|
||||
MOZ_ASSERT(mOutRate);
|
||||
|
||||
MOZ_ASSERT(aInBuffer);
|
||||
MOZ_ASSERT(aInFrames);
|
||||
MOZ_ASSERT(*aInFrames > 0);
|
||||
MOZ_ASSERT(aOutBuffer);
|
||||
|
@ -174,24 +174,24 @@ class DynamicResampler final {
|
||||
}
|
||||
|
||||
uint32_t totalOutFramesNeeded = aOutFrames;
|
||||
auto resample = [&] {
|
||||
mInternalInBuffer[aChannelIndex].ReadNoCopy(
|
||||
[&](const Span<const T>& aInBuffer) -> uint32_t {
|
||||
if (!totalOutFramesNeeded) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t outFramesResampled = totalOutFramesNeeded;
|
||||
uint32_t inFrames = aInBuffer.Length();
|
||||
ResampleInternal(aInBuffer.data(), &inFrames, aOutBuffer,
|
||||
&outFramesResampled, aChannelIndex);
|
||||
aOutBuffer += outFramesResampled;
|
||||
totalOutFramesNeeded -= outFramesResampled;
|
||||
mInputTail[aChannelIndex].StoreTail<T>(aInBuffer.To(inFrames));
|
||||
return inFrames;
|
||||
});
|
||||
auto resample = [&](const T* aInBuffer, uint32_t aInLength) -> uint32_t {
|
||||
uint32_t outFramesResampled = totalOutFramesNeeded;
|
||||
uint32_t inFrames = aInLength;
|
||||
ResampleInternal(aInBuffer, &inFrames, aOutBuffer, &outFramesResampled,
|
||||
aChannelIndex);
|
||||
aOutBuffer += outFramesResampled;
|
||||
totalOutFramesNeeded -= outFramesResampled;
|
||||
mInputTail[aChannelIndex].StoreTail<T>(aInBuffer, inFrames);
|
||||
return inFrames;
|
||||
};
|
||||
|
||||
resample();
|
||||
mInternalInBuffer[aChannelIndex].ReadNoCopy(
|
||||
[&](const Span<const T>& aInBuffer) -> uint32_t {
|
||||
if (!totalOutFramesNeeded) {
|
||||
return 0;
|
||||
}
|
||||
return resample(aInBuffer.Elements(), aInBuffer.Length());
|
||||
});
|
||||
|
||||
if (totalOutFramesNeeded == 0) {
|
||||
return false;
|
||||
@ -204,8 +204,7 @@ class DynamicResampler final {
|
||||
((CheckedUint32(totalOutFramesNeeded) * mInRate + mOutRate - 1) /
|
||||
mOutRate)
|
||||
.value();
|
||||
mInternalInBuffer[aChannelIndex].WriteSilence(totalInFramesNeeded);
|
||||
resample();
|
||||
resample(nullptr, totalInFramesNeeded);
|
||||
}
|
||||
mIsPreBufferSet = false;
|
||||
return true;
|
||||
@ -324,16 +323,16 @@ class DynamicResampler final {
|
||||
}
|
||||
template <typename T>
|
||||
void StoreTail(const T* aInBuffer, uint32_t aInFrames) {
|
||||
if (aInFrames >= MAXSIZE) {
|
||||
PodCopy(Buffer<T>(), aInBuffer + aInFrames - MAXSIZE, MAXSIZE);
|
||||
mSize = MAXSIZE;
|
||||
const T* inBuffer = aInBuffer;
|
||||
mSize = std::min(aInFrames, MAXSIZE);
|
||||
if (inBuffer) {
|
||||
PodCopy(Buffer<T>(), inBuffer + aInFrames - mSize, mSize);
|
||||
} else {
|
||||
PodCopy(Buffer<T>(), aInBuffer, aInFrames);
|
||||
mSize = aInFrames;
|
||||
std::fill_n(Buffer<T>(), mSize, static_cast<T>(0));
|
||||
}
|
||||
}
|
||||
uint32_t Length() { return mSize; }
|
||||
static const uint32_t MAXSIZE = 20;
|
||||
static constexpr uint32_t MAXSIZE = 20;
|
||||
|
||||
private:
|
||||
float mBuffer[MAXSIZE] = {};
|
||||
|
@ -459,29 +459,46 @@ TEST(TestAudioDriftCorrection, DriftStepResponseUnderrunHighLatencyInput)
|
||||
constexpr uint32_t iterations = 200;
|
||||
const PrincipalHandle testPrincipal =
|
||||
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
|
||||
uint32_t inputRate = nominalRate * 1005 / 1000; // 0.5% drift
|
||||
uint32_t inputInterval = inputRate;
|
||||
uint32_t inputRate1 = nominalRate * 1005 / 1000; // 0.5% drift
|
||||
uint32_t inputInterval1 = inputRate1;
|
||||
AudioGenerator<AudioDataValue> tone(1, nominalRate, 440);
|
||||
AudioDriftCorrection ad(nominalRate, nominalRate, testPrincipal);
|
||||
for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
|
||||
AudioSegment inSegment;
|
||||
if (i > 0 && i % interval == 0) {
|
||||
tone.Generate(inSegment, inputInterval);
|
||||
tone.Generate(inSegment, inputInterval1);
|
||||
}
|
||||
ad.RequestFrames(inSegment, interval / 100);
|
||||
}
|
||||
|
||||
inputRate = nominalRate * 995 / 1000; // -0.5% drift
|
||||
inputInterval = inputRate;
|
||||
uint32_t inputRate2 = nominalRate * 995 / 1000; // -0.5% drift
|
||||
uint32_t inputInterval2 = inputRate2;
|
||||
for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
|
||||
AudioSegment inSegment;
|
||||
// The first segment is skipped to cause an underrun.
|
||||
if (i > 0 && i % interval == 0) {
|
||||
tone.Generate(inSegment, inputInterval);
|
||||
tone.Generate(inSegment, inputInterval2);
|
||||
}
|
||||
ad.RequestFrames(inSegment, interval / 100);
|
||||
if (i >= interval / 10 && i < interval) {
|
||||
// While the DynamicResampler has not set its pre-buffer after the
|
||||
// underrun, InFramesBuffered() reports the pre-buffer size.
|
||||
// The initial desired buffer and pre-buffer size was
|
||||
// inputInterval1 * 11 / 10 to accomodate the large input block size.
|
||||
// This was doubled when the underrun occurred.
|
||||
EXPECT_EQ(ad.CurrentBuffering(), inputInterval1 * 11 / 10 * 2)
|
||||
<< "for i=" << i;
|
||||
} else if (i == interval) {
|
||||
// After the pre-buffer was set and used to generate the first output
|
||||
// block, the actual number of frames buffered almost matches the
|
||||
// pre-buffer size, with some rounding from output to input frame count
|
||||
// conversion.
|
||||
EXPECT_EQ(ad.CurrentBuffering(), inputInterval1 * 11 / 10 * 2 - 1)
|
||||
<< "after first input after underrun";
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(ad.BufferSize(), 220800U);
|
||||
EXPECT_EQ(ad.BufferSize(), 110400U);
|
||||
EXPECT_EQ(ad.NumUnderruns(), 1u);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user