diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /dom/media/driftcontrol/DynamicResampler.h | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/driftcontrol/DynamicResampler.h')
-rw-r--r-- | dom/media/driftcontrol/DynamicResampler.h | 148 |
1 files changed, 67 insertions, 81 deletions
diff --git a/dom/media/driftcontrol/DynamicResampler.h b/dom/media/driftcontrol/DynamicResampler.h index c1b9000aa0..1f601c898b 100644 --- a/dom/media/driftcontrol/DynamicResampler.h +++ b/dom/media/driftcontrol/DynamicResampler.h @@ -27,15 +27,13 @@ const uint32_t STEREO = 2; * to allow the requested to be resampled and returned. * * Input data buffering makes use of the AudioRingBuffer. The capacity of the - * buffer is initially 100ms of float audio and it is pre-allocated at the - * constructor. Should the input data grow beyond that, the input buffer is - * re-allocated on the fly. In addition to that, due to special feature of + * buffer is initially 100ms of audio and it is pre-allocated during + * SetSampleFormat(). Should the input data grow beyond that, the input buffer + * is re-allocated on the fly. In addition to that, due to special feature of * AudioRingBuffer, no extra copies take place when the input data is fed to the * resampler. * - * The sample format must be set before using any method. If the provided sample - * format is of type short the pre-allocated capacity of the input buffer - * becomes 200ms of short audio. + * The sample format must be set before using any method. * * The DynamicResampler is not thread-safe, so all the methods appart from the * constructor must be called on the same thread. @@ -47,16 +45,15 @@ class DynamicResampler final { * The channel count will be set to stereo. Memory allocation will take * place. The input buffer is non-interleaved. */ - DynamicResampler( - uint32_t aInRate, uint32_t aOutRate, - media::TimeUnit aPreBufferDuration = media::TimeUnit::Zero()); + DynamicResampler(uint32_t aInRate, uint32_t aOutRate, + uint32_t aInputPreBufferFrameCount = 0); ~DynamicResampler(); /** * Set the sample format type to float or short. */ void SetSampleFormat(AudioSampleFormat aFormat); - uint32_t GetOutRate() const { return mOutRate; } + uint32_t GetInRate() const { return mInRate; } uint32_t GetChannels() const { return mChannels; } /** @@ -81,16 +78,16 @@ class DynamicResampler final { /** * Prepends existing input data with a silent pre-buffer if not already done. - * Data will be prepended so that after resampling aOutFrames worth of output - * data, the buffering level will be as close as possible to - * mPreBufferDuration, which is the desired buffering level. + * Data will be prepended so that after resampling aDuration of data, + * the buffering level will be as close as possible to + * mInputPreBufferFrameCount, which is the desired buffering level. */ void EnsurePreBuffer(media::TimeUnit aDuration); /** - * Set the duration that should be used for pre-buffering. + * Set the number of frames that should be used for input pre-buffering. */ - void SetPreBufferDuration(media::TimeUnit aDuration); + void SetInputPreBufferFrameCount(uint32_t aInputPreBufferFrameCount); /* * Resample as much frames as needed from the internal input buffer to the @@ -114,14 +111,14 @@ class DynamicResampler final { /** * Update the output rate or/and the channel count. If a value is not updated - * compared to the current one nothing happens. Changing the `aOutRate` + * compared to the current one nothing happens. Changing the `aInRate` * results in recalculation in the resampler. Changing `aChannels` results in * the reallocation of the internal input buffer with the exception of * changes between mono to stereo and vice versa where no reallocation takes * place. A stereo internal input buffer is always maintained even if the * sound is mono. */ - void UpdateResampler(uint32_t aOutRate, uint32_t aChannels); + void UpdateResampler(uint32_t aInRate, uint32_t aChannels); private: template <typename T> @@ -174,24 +171,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 +201,7 @@ class DynamicResampler final { ((CheckedUint32(totalOutFramesNeeded) * mInRate + mOutRate - 1) / mOutRate) .value(); - mInternalInBuffer[aChannelIndex].WriteSilence(totalInFramesNeeded); - resample(); + resample(nullptr, totalInFramesNeeded); } mIsPreBufferSet = false; return true; @@ -219,33 +215,14 @@ class DynamicResampler final { MOZ_ASSERT(mChannels); MOZ_ASSERT(aChannelIndex < mChannels); MOZ_ASSERT(aChannelIndex < mInternalInBuffer.Length()); - EnsureInputBufferDuration(media::TimeUnit( - CheckedInt64(mInternalInBuffer[aChannelIndex].AvailableRead()) + - aInFrames, - mInRate)); + EnsureInputBufferSizeInFrames( + mInternalInBuffer[aChannelIndex].AvailableRead() + aInFrames); mInternalInBuffer[aChannelIndex].Write(Span(aInBuffer, aInFrames)); } void WarmUpResampler(bool aSkipLatency); - media::TimeUnit CalculateInputBufferDuration() const { - // Pre-allocate something big, twice the pre-buffer, or at least 100ms. - return std::max(mPreBufferDuration * 2, media::TimeUnit::FromSeconds(0.1)); - } - - bool EnsureInputBufferDuration(media::TimeUnit aDuration) { - if (aDuration <= mSetBufferDuration) { - // Buffer size is sufficient. - return true; - } - - // 5 second cap. - const media::TimeUnit cap = media::TimeUnit::FromSeconds(5); - if (mSetBufferDuration == cap) { - // Already at the cap. - return false; - } - + bool EnsureInputBufferSizeInFrames(uint32_t aSizeInFrames) { uint32_t sampleSize = 0; if (mSampleFormat == AUDIO_FORMAT_FLOAT32) { sampleSize = sizeof(float); @@ -258,53 +235,62 @@ class DynamicResampler final { return true; } + uint32_t sizeInFrames = InFramesBufferSize(); + if (aSizeInFrames <= sizeInFrames) { + // Buffer size is sufficient. + return true; // no reallocation necessary + } + + // 5 second cap. + const uint32_t cap = 5 * mInRate; + if (sizeInFrames >= cap) { + // Already at the cap. + return false; + } + // As a backoff strategy, at least double the previous size. - media::TimeUnit duration = mSetBufferDuration * 2; + sizeInFrames *= 2; - if (aDuration > duration) { + if (aSizeInFrames > sizeInFrames) { // A larger buffer than the normal backoff strategy provides is needed, or - // this is the first time setting the buffer size. Round up to the nearest - // 100ms, some jitter is expected. - duration = aDuration.ToBase<media::TimeUnit::CeilingPolicy>(10); + // this is the first time setting the buffer size. Add another 50ms, as + // some jitter is expected. + sizeInFrames = aSizeInFrames + mInRate / 20; } - duration = std::min(cap, duration); + // mInputPreBufferFrameCount is an indication of the desired average + // buffering. Provide for at least twice this. + sizeInFrames = std::max(sizeInFrames, mInputPreBufferFrameCount * 2); + + sizeInFrames = std::min(cap, sizeInFrames); bool success = true; for (auto& b : mInternalInBuffer) { - success = success && - b.SetLengthBytes(sampleSize * duration.ToTicksAtRate(mInRate)); + success = success && b.EnsureLengthBytes(sampleSize * sizeInFrames); } if (success) { // All buffers have the new size. - mSetBufferDuration = duration; return true; } - const uint32_t sizeInFrames = - static_cast<uint32_t>(mSetBufferDuration.ToTicksAtRate(mInRate)); // Allocating an input buffer failed. We stick with the old buffer size. NS_WARNING(nsPrintfCString("Failed to allocate a buffer of %u bytes (%u " "frames). Expect glitches.", sampleSize * sizeInFrames, sizeInFrames) .get()); - for (auto& b : mInternalInBuffer) { - MOZ_ALWAYS_TRUE(b.SetLengthBytes(sampleSize * sizeInFrames)); - } return false; } public: - const uint32_t mInRate; + const uint32_t mOutRate; private: bool mIsPreBufferSet = false; bool mIsWarmingUp = false; - media::TimeUnit mPreBufferDuration; - media::TimeUnit mSetBufferDuration = media::TimeUnit::Zero(); + uint32_t mInputPreBufferFrameCount; uint32_t mChannels = 0; - uint32_t mOutRate; + uint32_t mInRate; AutoTArray<AudioRingBuffer, STEREO> mInternalInBuffer; @@ -324,16 +310,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] = {}; |