diff options
Diffstat (limited to 'dom/media/AudioPacketizer.h')
-rw-r--r-- | dom/media/AudioPacketizer.h | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/dom/media/AudioPacketizer.h b/dom/media/AudioPacketizer.h new file mode 100644 index 0000000000..8df04c0c5c --- /dev/null +++ b/dom/media/AudioPacketizer.h @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef AudioPacketizer_h_ +#define AudioPacketizer_h_ + +#include <mozilla/PodOperations.h> +#include <mozilla/Assertions.h> +#include <mozilla/UniquePtr.h> +#include <AudioSampleFormat.h> + +// Enable this to warn when `Output` has been called but not enough data was +// buffered. +// #define LOG_PACKETIZER_UNDERRUN + +namespace mozilla { +/** + * This class takes arbitrary input data, and returns packets of a specific + * size. In the process, it can convert audio samples from 16bit integers to + * float (or vice-versa). + * + * Input and output, as well as length units in the public interface are + * interleaved frames. + * + * Allocations of output buffer can be performed by this class. Buffers can + * simply be delete-d. This is because packets are intended to be sent off to + * non-gecko code using normal pointers/length pairs + * + * Alternatively, consumers can pass in a buffer in which the output is copied. + * The buffer needs to be large enough to store a packet worth of audio. + * + * The implementation uses a circular buffer using absolute virtual indices. + */ +template <typename InputType, typename OutputType> +class AudioPacketizer { + public: + AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels) + : mPacketSize(aPacketSize), + mChannels(aChannels), + mReadIndex(0), + mWriteIndex(0), + // Start off with a single packet + mStorage(new InputType[aPacketSize * aChannels]), + mLength(aPacketSize * aChannels) { + MOZ_ASSERT(aPacketSize > 0 && aChannels > 0, + "The packet size and the number of channel should be strictly " + "positive"); + } + + void Input(const InputType* aFrames, uint32_t aFrameCount) { + uint32_t inputSamples = aFrameCount * mChannels; + // Need to grow the storage. This should rarely happen, if at all, once the + // array has the right size. + if (inputSamples > EmptySlots()) { + // Calls to Input and Output are roughtly interleaved + // (Input,Output,Input,Output, etc.), or balanced + // (Input,Input,Input,Output,Output,Output), so we update the buffer to + // the exact right size in order to not waste space. + uint32_t newLength = AvailableSamples() + inputSamples; + uint32_t toCopy = AvailableSamples(); + UniquePtr<InputType[]> oldStorage = std::move(mStorage); + mStorage = mozilla::MakeUnique<InputType[]>(newLength); + // Copy the old data at the beginning of the new storage. + if (WriteIndex() >= ReadIndex()) { + PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(), + AvailableSamples()); + } else { + uint32_t firstPartLength = mLength - ReadIndex(); + uint32_t secondPartLength = AvailableSamples() - firstPartLength; + PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(), + firstPartLength); + PodCopy(mStorage.get() + firstPartLength, oldStorage.get(), + secondPartLength); + } + mWriteIndex = toCopy; + mReadIndex = 0; + mLength = newLength; + } + + if (WriteIndex() + inputSamples <= mLength) { + PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels); + } else { + uint32_t firstPartLength = mLength - WriteIndex(); + uint32_t secondPartLength = inputSamples - firstPartLength; + PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength); + PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength); + } + + mWriteIndex += inputSamples; + } + + OutputType* Output() { + uint32_t samplesNeeded = mPacketSize * mChannels; + OutputType* out = new OutputType[samplesNeeded]; + + Output(out); + + return out; + } + + void Output(OutputType* aOutputBuffer) { + uint32_t samplesNeeded = mPacketSize * mChannels; + + // Under-run. Pad the end of the buffer with silence. + if (AvailableSamples() < samplesNeeded) { +#ifdef LOG_PACKETIZER_UNDERRUN + char buf[256]; + snprintf(buf, 256, + "AudioPacketizer %p underrun: available: %u, needed: %u\n", this, + AvailableSamples(), samplesNeeded); + NS_WARNING(buf); +#endif + uint32_t zeros = samplesNeeded - AvailableSamples(); + PodZero(aOutputBuffer + AvailableSamples(), zeros); + samplesNeeded -= zeros; + } + if (ReadIndex() + samplesNeeded <= mLength) { + ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(), + aOutputBuffer, samplesNeeded); + } else { + uint32_t firstPartLength = mLength - ReadIndex(); + uint32_t secondPartLength = samplesNeeded - firstPartLength; + ConvertAudioSamples<InputType, OutputType>( + mStorage.get() + ReadIndex(), aOutputBuffer, firstPartLength); + ConvertAudioSamples<InputType, OutputType>( + mStorage.get(), aOutputBuffer + firstPartLength, secondPartLength); + } + mReadIndex += samplesNeeded; + } + + void Clear() { + mReadIndex = 0; + mWriteIndex = 0; + } + + uint32_t PacketsAvailable() const { + return AvailableSamples() / mChannels / mPacketSize; + } + + uint32_t FramesAvailable() const { return AvailableSamples() / mChannels; } + + bool Empty() const { return mWriteIndex == mReadIndex; } + + bool Full() const { return mWriteIndex - mReadIndex == mLength; } + + // Size of one packet of audio, in frames + const uint32_t mPacketSize; + // Number of channels of the stream flowing through this packetizer + const uint32_t mChannels; + + private: + uint32_t ReadIndex() const { return mReadIndex % mLength; } + + uint32_t WriteIndex() const { return mWriteIndex % mLength; } + + uint32_t AvailableSamples() const { return mWriteIndex - mReadIndex; } + + uint32_t EmptySlots() const { return mLength - AvailableSamples(); } + + // Two virtual index into the buffer: the read position and the write + // position. + uint64_t mReadIndex; + uint64_t mWriteIndex; + // Storage for the samples + mozilla::UniquePtr<InputType[]> mStorage; + // Length of the buffer, in samples + uint32_t mLength; +}; + +} // namespace mozilla + +#endif // AudioPacketizer_h_ |