michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #if !defined(AudioCompactor_h) michael@0: #define AudioCompactor_h michael@0: michael@0: #include "MediaQueue.h" michael@0: #include "MediaData.h" michael@0: #include "VideoUtils.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: class AudioCompactor michael@0: { michael@0: public: michael@0: AudioCompactor(MediaQueue& aQueue) michael@0: : mQueue(aQueue) michael@0: { } michael@0: michael@0: // Push audio data into the underlying queue with minimal heap allocation michael@0: // slop. This method is responsible for allocating AudioDataValue[] buffers. michael@0: // The caller must provide a functor to copy the data into the buffers. The michael@0: // functor must provide the following signature: michael@0: // michael@0: // uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples); michael@0: // michael@0: // The functor must copy as many complete frames as possible to the provided michael@0: // buffer given its length (in AudioDataValue elements). The number of frames michael@0: // copied must be returned. This copy functor must support being called michael@0: // multiple times in order to copy the audio data fully. The copy functor michael@0: // must copy full frames as partial frames will be ignored. michael@0: template michael@0: bool Push(int64_t aOffset, int64_t aTime, int32_t aSampleRate, michael@0: uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc) michael@0: { michael@0: // If we are losing more than a reasonable amount to padding, try to chunk michael@0: // the data. michael@0: size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR; michael@0: michael@0: while (aFrames > 0) { michael@0: uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop); michael@0: nsAutoArrayPtr buffer(new AudioDataValue[samples]); michael@0: michael@0: // Copy audio data to buffer using caller-provided functor. michael@0: uint32_t framesCopied = aCopyFunc(buffer, samples); michael@0: michael@0: NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames"); michael@0: michael@0: CheckedInt64 duration = FramesToUsecs(framesCopied, aSampleRate); michael@0: if (!duration.isValid()) { michael@0: return false; michael@0: } michael@0: michael@0: mQueue.Push(new AudioData(aOffset, michael@0: aTime, michael@0: duration.value(), michael@0: framesCopied, michael@0: buffer.forget(), michael@0: aChannels)); michael@0: michael@0: // Remove the frames we just pushed into the queue and loop if there is michael@0: // more to be done. michael@0: aTime += duration.value(); michael@0: aFrames -= framesCopied; michael@0: michael@0: // NOTE: No need to update aOffset as its only an approximation anyway. michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Copy functor suitable for copying audio samples already in the michael@0: // AudioDataValue format/layout expected by AudioStream on this platform. michael@0: class NativeCopy michael@0: { michael@0: public: michael@0: NativeCopy(const uint8_t* aSource, size_t aSourceBytes, michael@0: uint32_t aChannels) michael@0: : mSource(aSource) michael@0: , mSourceBytes(aSourceBytes) michael@0: , mChannels(aChannels) michael@0: , mNextByte(0) michael@0: { } michael@0: michael@0: uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples); michael@0: michael@0: private: michael@0: const uint8_t* const mSource; michael@0: const size_t mSourceBytes; michael@0: const uint32_t mChannels; michael@0: size_t mNextByte; michael@0: }; michael@0: michael@0: // Allow 12.5% slop before chunking kicks in. Public so that the gtest can michael@0: // access it. michael@0: static const size_t MAX_SLOP_DIVISOR = 8; michael@0: michael@0: private: michael@0: // Compute the number of AudioDataValue samples that will be fit the most michael@0: // frames while keeping heap allocation slop less than the given threshold. michael@0: static uint32_t michael@0: GetChunkSamples(uint32_t aFrames, uint32_t aChannels, size_t aMaxSlop); michael@0: michael@0: static size_t BytesPerFrame(uint32_t aChannels) michael@0: { michael@0: return sizeof(AudioDataValue) * aChannels; michael@0: } michael@0: michael@0: static size_t AudioDataSize(uint32_t aFrames, uint32_t aChannels) michael@0: { michael@0: return aFrames * BytesPerFrame(aChannels); michael@0: } michael@0: michael@0: MediaQueue &mQueue; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif // AudioCompactor_h