diff options
Diffstat (limited to 'dom/media/AudioPacketizer.h')
-rw-r--r-- | dom/media/AudioPacketizer.h | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/dom/media/AudioPacketizer.h b/dom/media/AudioPacketizer.h new file mode 100644 index 000000000..b0452cdf0 --- /dev/null +++ b/dom/media/AudioPacketizer.h @@ -0,0 +1,196 @@ +/* -*- 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 = mozilla::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; + } + + uint32_t PacketsAvailable() const { + return AvailableSamples() / mChannels / mPacketSize; + } + + bool Empty() const { + return mWriteIndex == mReadIndex; + } + + bool Full() const { + return mWriteIndex - mReadIndex == mLength; + } + + uint32_t PacketSize() const { + return mPacketSize; + } + + uint32_t Channels() const { + return 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(); + } + + // Size of one packet of audio, in frames + uint32_t mPacketSize; + // Number of channels of the stream flowing through this packetizer + uint32_t mChannels; + // 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; +}; + +} // mozilla + +#endif // AudioPacketizer_h_ |