Commit f6734cf6 authored by xians's avatar xians Committed by Commit bot

Dynamically allocate more memory to AudioBlockFifo.

PulseAudio input can provide larger buffer size than what we ask for. This happens if the pulse internally increase its buffer size for various reasons. This patch handles it by dynamically allocate more memory to the FIFO.

BUG=411453
TEST=1. Have Jackd own a second (USB) microphone, and have
Pulseaudio connect to Jackd with the Pulseaudio Jack
module. There are many steps:
1a. Obtain a USB mic.
1b. Find out the ALSA name of the mic and of the default
audio.
1c. Create a file ~/.jackdrc containing:
/usr/bin/jackd -dalsa -d hw:<default audio device>
1d. Run "alsa_in -d hw:<USB mic name> &" (in background)
1e. Install pulseaudio-module-jack if not installed.
1f. Run "pactl load-module module-jack-source channels=2"
1g. Go into Pulseaudio and select Input, then Jack source.
1h. Install patchage, start it up and connect the USB mic
to the Pulseaudio JACK source.
2. Start up Chrome and begin a Hangout. Make sure that
Chrome is using the Default mic.
3. Have someone (or yourself on another computer) join the
Hangout and listen to you talk.

Review URL: https://codereview.chromium.org/557693003

Cr-Commit-Position: refs/heads/master@{#295713}
parent cf009964
......@@ -270,6 +270,15 @@ void PulseAudioInputStream::ReadData() {
break;
const int number_of_frames = length / params_.GetBytesPerFrame();
if (number_of_frames > fifo_.GetUnfilledFrames()) {
// Dynamically increase capacity to the FIFO to handle larger buffer got
// from Pulse.
const int increase_blocks_of_buffer = static_cast<int>(
(number_of_frames - fifo_.GetUnfilledFrames()) /
params_.frames_per_buffer()) + 1;
fifo_.IncreaseCapacity(increase_blocks_of_buffer);
}
fifo_.Push(data, number_of_frames, params_.bits_per_sample() / 8);
// Checks if we still have data.
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "media/base/audio_block_fifo.h"
#include "base/logging.h"
......@@ -9,17 +11,13 @@
namespace media {
AudioBlockFifo::AudioBlockFifo(int channels, int frames, int blocks)
: block_frames_(frames),
: channels_(channels),
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());
}
IncreaseCapacity(blocks);
}
AudioBlockFifo::~AudioBlockFifo() {}
......@@ -31,6 +29,7 @@ void AudioBlockFifo::Push(const void* source,
DCHECK_GT(frames, 0);
DCHECK_GT(bytes_per_sample, 0);
DCHECK_LT(available_blocks_, static_cast<int>(audio_blocks_.size()));
CHECK_LE(frames, GetUnfilledFrames());
const uint8* source_ptr = static_cast<const uint8*>(source);
int frames_to_push = frames;
......@@ -54,7 +53,7 @@ void AudioBlockFifo::Push(const void* source,
++available_blocks_;
}
source_ptr += push_frames * bytes_per_sample * current_block->channels();
source_ptr += push_frames * bytes_per_sample * channels_;
frames_to_push -= push_frames;
DCHECK_GE(frames_to_push, 0);
}
......@@ -86,4 +85,34 @@ int AudioBlockFifo::GetUnfilledFrames() const {
return unfilled_frames;
}
void AudioBlockFifo::IncreaseCapacity(int blocks) {
DCHECK_GT(blocks, 0);
// Create |blocks| of audio buses and insert them to the containers.
audio_blocks_.reserve(audio_blocks_.size() + blocks);
const int original_size = audio_blocks_.size();
for (int i = 0; i < blocks; ++i) {
audio_blocks_.push_back(
AudioBus::Create(channels_, block_frames_).release());
}
if (!original_size)
return;
std::rotate(audio_blocks_.begin() + read_block_,
audio_blocks_.begin() + original_size,
audio_blocks_.end());
// Update the write pointer if it is on top of the new inserted blocks.
if (write_block_ >= read_block_)
write_block_ += blocks;
// Update the read pointers correspondingly.
read_block_ += blocks;
DCHECK_LT(read_block_, static_cast<int>(audio_blocks_.size()));
DCHECK_LT(write_block_, static_cast<int>(audio_blocks_.size()));
}
} // namespace media
......@@ -45,10 +45,16 @@ class MEDIA_EXPORT AudioBlockFifo {
// Number of unfilled frames in the whole FIFO.
int GetUnfilledFrames() const;
// Dynamically increase |blocks| of memory to the FIFO.
void IncreaseCapacity(int blocks);
private:
// The actual FIFO is a vector of audio buses.
ScopedVector<AudioBus> audio_blocks_;
// Number of channels in AudioBus.
const int channels_;
// 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_;
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/time/time.h"
#include "media/audio/audio_power_monitor.h"
#include "media/base/audio_block_fifo.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -14,14 +16,9 @@ class AudioBlockFifoTest : public testing::Test {
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);
Push(fifo, frames_to_push, channels);
filled_frames += frames_to_push;
EXPECT_EQ(max_frames - filled_frames, fifo->GetUnfilledFrames());
EXPECT_EQ(static_cast<int>(filled_frames / block_frames),
......@@ -29,6 +26,28 @@ class AudioBlockFifoTest : public testing::Test {
}
}
void Push(AudioBlockFifo* fifo, int frames_to_push, int channels) {
DCHECK_LE(frames_to_push, fifo->GetUnfilledFrames());
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(), 1, data_byte_size);
fifo->Push(data.get(), frames_to_push, bytes_per_sample);
}
void ConsumeAndVerify(AudioBlockFifo* fifo, int expected_unfilled_frames,
int expected_available_blocks) {
const AudioBus* bus = fifo->Consume();
EXPECT_EQ(fifo->GetUnfilledFrames(), expected_unfilled_frames);
EXPECT_EQ(fifo->available_blocks(), expected_available_blocks);
// Verify the audio data is not 0.
for (int i = 0; i < bus->channels(); ++i) {
EXPECT_GT(bus->channel(i)[0], 0.0f);
EXPECT_GT(bus->channel(i)[bus->frames() - 1], 0.0f);
}
}
private:
DISALLOW_COPY_AND_ASSIGN(AudioBlockFifoTest);
};
......@@ -146,4 +165,62 @@ TEST_F(AudioBlockFifoTest, PushAndConsumeOneBlockFifo) {
EXPECT_TRUE(fifo.GetUnfilledFrames() == frames);
}
// Dynamically increase the capacity of FIFO and verify buffers are correct.
TEST_F(AudioBlockFifoTest, DynamicallyIncreaseCapacity) {
// Create a FIFO with default blocks of buffers.
const int channels = 2;
const int frames = 441;
const int default_blocks = 2;
AudioBlockFifo fifo(channels, frames, default_blocks);
Push(&fifo, frames, channels);
int expected_unfilled_frames = frames;
int expected_available_blocks = 1;
EXPECT_EQ(expected_unfilled_frames, fifo.GetUnfilledFrames());
EXPECT_EQ(expected_available_blocks, fifo.available_blocks());
// Increase the capacity dynamically for the first time.
const int new_blocks_1 = 3;
fifo.IncreaseCapacity(new_blocks_1);
expected_unfilled_frames += new_blocks_1 * frames;
EXPECT_EQ(fifo.GetUnfilledFrames(), expected_unfilled_frames);
EXPECT_EQ(fifo.available_blocks(), expected_available_blocks);
// Verify the previous buffer is not affected by the dynamic capacity
// increment.
expected_unfilled_frames += frames;
expected_available_blocks -= 1;
ConsumeAndVerify(&fifo, expected_unfilled_frames, expected_available_blocks);
// Fill another |new_blocks_1 + 0.5| blocks of data to the FIFO.
const int frames_to_push = static_cast<int>((new_blocks_1 + 0.5) * frames);
int max_frames = frames * (default_blocks + new_blocks_1);
Push(&fifo, frames_to_push, channels);
expected_unfilled_frames = max_frames - frames_to_push;
expected_available_blocks = new_blocks_1;
EXPECT_EQ(fifo.GetUnfilledFrames(), expected_unfilled_frames);
EXPECT_EQ(fifo.available_blocks(), expected_available_blocks);
// Increase the capacity dynamically for the second time.
const int new_blocks_2 = 2;
fifo.IncreaseCapacity(new_blocks_2);
max_frames += new_blocks_2 * frames;
expected_unfilled_frames += new_blocks_2 * frames;
EXPECT_EQ(fifo.GetUnfilledFrames(), expected_unfilled_frames);
EXPECT_EQ(fifo.available_blocks(), expected_available_blocks);
// Verify the previous buffers are not affected by the dynamic capacity
// increment.
while (fifo.available_blocks()) {
expected_unfilled_frames += frames;
expected_available_blocks -= 1;
ConsumeAndVerify(&fifo, expected_unfilled_frames,
expected_available_blocks);
}
// Fill up one block of buffer and consume it, FIFO should then be empty.
const int available_frames = max_frames - expected_unfilled_frames;
Push(&fifo, frames - available_frames, channels);
ConsumeAndVerify(&fifo, max_frames, 0);
}
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment