diff options
author | xians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-16 21:38:18 +0000 |
---|---|---|
committer | xians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-16 21:38:18 +0000 |
commit | 43a63273ded47c2c086c738f98599a61aa72fef9 (patch) | |
tree | c2db0c84f76e1a51074645c3bd880b66fd916c97 /media | |
parent | 3df4a8560e995a350304770bc2314e07713fefaf (diff) | |
download | chromium_src-43a63273ded47c2c086c738f98599a61aa72fef9.zip chromium_src-43a63273ded47c2c086c738f98599a61aa72fef9.tar.gz chromium_src-43a63273ded47c2c086c738f98599a61aa72fef9.tar.bz2 |
Add a block based Audio FIFO.
This new AudioBlockFifo() is made for the input code to avoid copying audio data during Push() and Consume().
Contrast to the existing AudioFifo, which requires a AudioBus* as input param for its Push() and Consume() methods to copy the data from/to the FIFO, this new AudioBlockFifo keeps blocks of AudioBus, it accepts interleaved data as input for its Push() method, and its Consume() method return an AudioBus for consumption. So the copy operations in this AudioBlockFifo() is 1 versus 3 in AudioFifo().
NOTRY=true
BUG=393199
TEST=media_unittests --gtest_filter="*AudioBlockFifo*"
Review URL: https://codereview.chromium.org/389623002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283518 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/audio_block_fifo.cc | 83 | ||||
-rw-r--r-- | media/base/audio_block_fifo.h | 70 | ||||
-rw-r--r-- | media/base/audio_block_fifo_unittest.cc | 149 | ||||
-rw-r--r-- | media/media.gyp | 5 |
4 files changed, 306 insertions, 1 deletions
diff --git a/media/base/audio_block_fifo.cc b/media/base/audio_block_fifo.cc new file mode 100644 index 0000000..b8cecfa1 --- /dev/null +++ b/media/base/audio_block_fifo.cc @@ -0,0 +1,83 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/audio_block_fifo.h" + +#include "base/logging.h" + +namespace media { + +AudioBlockFifo::AudioBlockFifo(int channels, int frames, int blocks) + : block_frames_(frames), + write_block_(0), + read_block_(0), + available_blocks_(0), + write_pos_(0) { + // Create |blocks| of audio buses and push them to the containers. + audio_blocks_.reserve(blocks); + for (int i = 0; i < blocks; ++i) { + scoped_ptr<AudioBus> audio_bus = AudioBus::Create(channels, frames); + audio_blocks_.push_back(audio_bus.release()); + } +} + +AudioBlockFifo::~AudioBlockFifo() {} + +void AudioBlockFifo::Push(const void* source, + int frames, + int bytes_per_sample) { + DCHECK(source); + DCHECK_GT(frames, 0); + DCHECK_GT(bytes_per_sample, 0); + DCHECK_LT(available_blocks_, static_cast<int>(audio_blocks_.size())); + + const uint8* source_ptr = static_cast<const uint8*>(source); + int frames_to_push = frames; + while (frames_to_push) { + // Get the current write block. + AudioBus* current_block = audio_blocks_[write_block_]; + + // Figure out what segment sizes we need when adding the new content to + // the FIFO. + const int push_frames = + std::min(block_frames_ - write_pos_, frames_to_push); + + // Deinterleave the content to the FIFO and update the |write_pos_|. + current_block->FromInterleaved(source_ptr, push_frames, bytes_per_sample); + write_pos_ = (write_pos_ + push_frames) % block_frames_; + if (!write_pos_) { + // The current block is completely filled, increment |write_block_| and + // |available_blocks_|. + write_block_ = (write_block_ + 1) % audio_blocks_.size(); + ++available_blocks_; + } + + source_ptr += push_frames * bytes_per_sample * current_block->channels(); + frames_to_push -= push_frames; + } +} + +const AudioBus* AudioBlockFifo::Consume() { + DCHECK(available_blocks_); + AudioBus* audio_bus = audio_blocks_[read_block_]; + read_block_ = (read_block_ + 1) % audio_blocks_.size(); + --available_blocks_; + return audio_bus; +} + +void AudioBlockFifo::Clear() { + write_pos_ = 0; + write_block_ = 0; + read_block_ = 0; + available_blocks_ = 0; +} + +int AudioBlockFifo::GetUnfilledFrames() const { + const int unfilled_frames = + (audio_blocks_.size() - available_blocks_) * block_frames_ - write_pos_; + DCHECK_GE(unfilled_frames, 0); + return unfilled_frames; +} + +} // namespace media diff --git a/media/base/audio_block_fifo.h b/media/base/audio_block_fifo.h new file mode 100644 index 0000000..d3d5a8a --- /dev/null +++ b/media/base/audio_block_fifo.h @@ -0,0 +1,70 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_AUDIO_BLOCK_FIFO_H_ +#define MEDIA_BASE_AUDIO_BLOCK_FIFO_H_ + +#include "base/memory/scoped_vector.h" +#include "media/base/audio_bus.h" +#include "media/base/media_export.h" + +namespace media { + +// First-in first-out container for AudioBus elements. +// The FIFO is composed of blocks of AudioBus elements, it accepts interleaved +// data as input and will deinterleave it into the FIFO, and it only allows +// consuming a whole block of AudioBus element. +// This class is thread-unsafe. +class MEDIA_EXPORT AudioBlockFifo { + public: + // Creates a new AudioBlockFifo and allocates |blocks| memory, each block + // of memory can store |channels| of length |frames| data. + AudioBlockFifo(int channels, int frames, int blocks); + virtual ~AudioBlockFifo(); + + // Pushes interleaved audio data from |source| to the FIFO. + // The method will deinterleave the data into a audio bus. + // Push() will crash if the allocated space is insufficient. + void Push(const void* source, int frames, int bytes_per_sample); + + // Consumes a block of audio from the FIFO. Returns an AudioBus which + // contains the consumed audio data to avoid copying. + // Consume() will crash if the FIFO does not contain a block of data. + const AudioBus* Consume(); + + // Empties the FIFO without deallocating any memory. + void Clear(); + + // Number of available block of memory ready to be consumed in the FIFO. + int available_blocks() const { return available_blocks_; } + + // Number of unfilled frames in the whole FIFO. + int GetUnfilledFrames() const; + + private: + // The actual FIFO is a vector of audio buses. + ScopedVector<AudioBus> audio_blocks_; + + // Maximum number of frames of data one block of memory can contain. + // This value is set by |frames| in the constructor. + const int block_frames_; + + // Used to keep track which block of memory to be written. + int write_block_; + + // Used to keep track which block of memory to be consumed. + int read_block_; + + // Number of available blocks of memory to be consumed. + int available_blocks_; + + // Current write position in the current written block. + int write_pos_; + + DISALLOW_COPY_AND_ASSIGN(AudioBlockFifo); +}; + +} // namespace media + +#endif // MEDIA_BASE_AUDIO_BLOCK_FIFO_H_ diff --git a/media/base/audio_block_fifo_unittest.cc b/media/base/audio_block_fifo_unittest.cc new file mode 100644 index 0000000..8e8b5e0 --- /dev/null +++ b/media/base/audio_block_fifo_unittest.cc @@ -0,0 +1,149 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/audio_block_fifo.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class AudioBlockFifoTest : public testing::Test { + public: + AudioBlockFifoTest() {} + virtual ~AudioBlockFifoTest() {} + + void PushAndVerify(AudioBlockFifo* fifo, int frames_to_push, + int channels, int block_frames, int max_frames) { + const int bytes_per_sample = 2; + const int data_byte_size = bytes_per_sample * channels * frames_to_push; + scoped_ptr<uint8[]> data(new uint8[data_byte_size]); + memset(data.get(), 0, data_byte_size); + + for (int filled_frames = max_frames - fifo->GetUnfilledFrames(); + filled_frames + frames_to_push <= max_frames;) { + fifo->Push(data.get(), frames_to_push, bytes_per_sample); + filled_frames += frames_to_push; + EXPECT_EQ(max_frames - filled_frames, fifo->GetUnfilledFrames()); + EXPECT_EQ(static_cast<int>(filled_frames / block_frames), + fifo->available_blocks()); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(AudioBlockFifoTest); +}; + +// Verify that construction works as intended. +TEST_F(AudioBlockFifoTest, Construct) { + const int channels = 6; + const int frames = 128; + const int blocks = 4; + AudioBlockFifo fifo(channels, frames, blocks); + EXPECT_EQ(0, fifo.available_blocks()); + EXPECT_EQ(frames * blocks, fifo.GetUnfilledFrames()); +} + +// Pushes audio bus objects to/from a FIFO up to different degrees. +TEST_F(AudioBlockFifoTest, Push) { + const int channels = 2; + const int frames = 128; + const int blocks = 2; + AudioBlockFifo fifo(channels, frames, blocks); + + // Push frames / 2 of data until FIFO is full. + PushAndVerify(&fifo, frames / 2, channels, frames, frames * blocks); + fifo.Clear(); + + // Push frames of data until FIFO is full. + PushAndVerify(&fifo, frames, channels, frames, frames * blocks); + fifo.Clear(); + + // Push 1.5 * frames of data. + PushAndVerify(&fifo, frames * 1.5, channels, frames, frames * blocks); + fifo.Clear(); +} + +// Perform a sequence of Push/Consume calls to different degrees, and verify +// things are correct. +TEST_F(AudioBlockFifoTest, PushAndConsume) { + const int channels = 2; + const int frames = 441; + const int blocks = 4; + AudioBlockFifo fifo(channels, frames, blocks); + PushAndVerify(&fifo, frames, channels, frames, frames * blocks); + EXPECT_TRUE(fifo.GetUnfilledFrames() == 0); + EXPECT_TRUE(fifo.available_blocks() == blocks); + + // Consume 1 block of data. + const AudioBus* bus = fifo.Consume(); + EXPECT_TRUE(channels == bus->channels()); + EXPECT_TRUE(frames == bus->frames()); + EXPECT_TRUE(fifo.available_blocks() == (blocks - 1)); + EXPECT_TRUE(fifo.GetUnfilledFrames() == frames); + + // Fill it up again. + PushAndVerify(&fifo, frames, channels, frames, frames * blocks); + EXPECT_TRUE(fifo.GetUnfilledFrames() == 0); + EXPECT_TRUE(fifo.available_blocks() == blocks); + + // Consume all blocks of data. + for (int i = 1; i <= blocks; ++i) { + const AudioBus* bus = fifo.Consume(); + EXPECT_TRUE(channels == bus->channels()); + EXPECT_TRUE(frames == bus->frames()); + EXPECT_TRUE(fifo.GetUnfilledFrames() == frames * i); + EXPECT_TRUE(fifo.available_blocks() == (blocks - i)); + } + EXPECT_TRUE(fifo.GetUnfilledFrames() == frames * blocks); + EXPECT_TRUE(fifo.available_blocks() == 0); + + fifo.Clear(); + int new_push_frames = 128; + // Change the input frame and try to fill up the FIFO. + PushAndVerify(&fifo, new_push_frames, channels, frames, + frames * blocks); + EXPECT_TRUE(fifo.GetUnfilledFrames() != 0); + EXPECT_TRUE(fifo.available_blocks() == blocks -1); + + // Consume all the existing filled blocks of data. + while (fifo.available_blocks()) { + const AudioBus* bus = fifo.Consume(); + EXPECT_TRUE(channels == bus->channels()); + EXPECT_TRUE(frames == bus->frames()); + } + + // Since one block of FIFO has not been completely filled up, there should + // be remaining frames. + const int number_of_push = + static_cast<int>(frames * blocks / new_push_frames); + const int remain_frames = frames * blocks - fifo.GetUnfilledFrames(); + EXPECT_EQ(number_of_push * new_push_frames - frames * (blocks - 1), + remain_frames); + + // Completely fill up the buffer again. + new_push_frames = frames * blocks - remain_frames; + PushAndVerify(&fifo, new_push_frames, channels, frames, + frames * blocks); + EXPECT_TRUE(fifo.GetUnfilledFrames() == 0); + EXPECT_TRUE(fifo.available_blocks() == blocks); +} + +// Perform a sequence of Push/Consume calls to a 1 block FIFO. +TEST_F(AudioBlockFifoTest, PushAndConsumeOneBlockFifo) { + static const int channels = 2; + static const int frames = 441; + static const int blocks = 1; + AudioBlockFifo fifo(channels, frames, blocks); + PushAndVerify(&fifo, frames, channels, frames, frames * blocks); + EXPECT_TRUE(fifo.GetUnfilledFrames() == 0); + EXPECT_TRUE(fifo.available_blocks() == blocks); + + // Consume 1 block of data. + const AudioBus* bus = fifo.Consume(); + EXPECT_TRUE(channels == bus->channels()); + EXPECT_TRUE(frames == bus->frames()); + EXPECT_TRUE(fifo.available_blocks() == 0); + EXPECT_TRUE(fifo.GetUnfilledFrames() == frames); +} + +} // namespace media diff --git a/media/media.gyp b/media/media.gyp index fdccc42..f1e81dd 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -206,13 +206,15 @@ 'base/android/media_player_manager.h', 'base/android/media_resource_getter.cc', 'base/android/media_resource_getter.h', + 'base/audio_block_fifo.cc', + 'base/audio_block_fifo.h', 'base/audio_buffer.cc', 'base/audio_buffer.h', 'base/audio_buffer_queue.cc', 'base/audio_buffer_queue.h', - 'base/audio_capturer_source.h', 'base/audio_buffer_converter.cc', 'base/audio_buffer_converter.h', + 'base/audio_capturer_source.h', 'base/audio_converter.cc', 'base/audio_converter.h', 'base/audio_decoder.cc', @@ -1061,6 +1063,7 @@ 'base/android/media_codec_bridge_unittest.cc', 'base/android/media_drm_bridge_unittest.cc', 'base/android/media_source_player_unittest.cc', + 'base/audio_block_fifo_unittest.cc', 'base/audio_buffer_converter_unittest.cc', 'base/audio_buffer_unittest.cc', 'base/audio_buffer_queue_unittest.cc', |