summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-14 23:16:00 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-14 23:16:00 +0000
commit01c41dc19a7706176785b426dccf97f4e24e7270 (patch)
tree804bf8e62563b94a435e6b5098855c604561339b /media/audio
parent2e03945a0cb0d34b784e985af3fc05f6ad59ceff (diff)
downloadchromium_src-01c41dc19a7706176785b426dccf97f4e24e7270.zip
chromium_src-01c41dc19a7706176785b426dccf97f4e24e7270.tar.gz
chromium_src-01c41dc19a7706176785b426dccf97f4e24e7270.tar.bz2
Removed AlsaPcmOutputStrem::Packet. Fixed bug in AlsaPcmOutputStream::ScheduleNextWrite which would cause high CPU consumption.
BUG=28654 TEST=Audio still works on Linux Review URL: http://codereview.chromium.org/2008010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47333 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/linux/alsa_output.cc194
-rw-r--r--media/audio/linux/alsa_output.h34
-rw-r--r--media/audio/linux/alsa_output_unittest.cc169
-rw-r--r--media/audio/linux/alsa_wrapper.cc5
-rw-r--r--media/audio/linux/alsa_wrapper.h2
5 files changed, 217 insertions, 187 deletions
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc
index 8212413..3ce6644 100644
--- a/media/audio/linux/alsa_output.cc
+++ b/media/audio/linux/alsa_output.cc
@@ -84,6 +84,8 @@
#include "media/audio/audio_util.h"
#include "media/audio/linux/alsa_wrapper.h"
#include "media/audio/linux/audio_manager_linux.h"
+#include "media/base/data_buffer.h"
+#include "media/base/seekable_buffer.h"
// Amount of time to wait if we've exhausted the data source. This is to avoid
// busy looping.
@@ -376,7 +378,8 @@ void AlsaPcmOutputStream::OpenTask(uint32 packet_size) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
// Initialize the configuration variables.
- frames_per_packet_ = packet_size / bytes_per_frame_;
+ packet_size_ = packet_size;
+ frames_per_packet_ = packet_size_ / bytes_per_frame_;
// Try to open the device.
micros_per_packet_ =
@@ -397,9 +400,22 @@ void AlsaPcmOutputStream::OpenTask(uint32 packet_size) {
if (playback_handle_ == NULL) {
stop_stream_ = true;
} else {
- packet_.reset(new Packet(packet_size));
- if (should_downmix_) {
- bytes_per_output_frame_ = 2 * bytes_per_sample_;
+ bytes_per_output_frame_ = should_downmix_ ? 2 * bytes_per_sample_ :
+ bytes_per_frame_;
+ uint32 output_packet_size = frames_per_packet_ * bytes_per_output_frame_;
+ buffer_.reset(new media::SeekableBuffer(0, output_packet_size));
+
+ // Get alsa buffer size.
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+ int error = wrapper_->PcmGetParams(playback_handle_, &buffer_size,
+ &period_size);
+ if (error < 0) {
+ LOG(ERROR) << "Failed to get playback buffer size from ALSA: "
+ << wrapper_->StrError(error);
+ alsa_buffer_frames_ = frames_per_packet_;
+ } else {
+ alsa_buffer_frames_ = buffer_size;
}
}
}
@@ -431,17 +447,7 @@ void AlsaPcmOutputStream::StartTask() {
return;
}
- // Do a best-effort pre-roll to fill the buffer. Use integer rounding to find
- // the maximum number of full packets that can fit into the buffer.
- //
- // TODO(ajwong): Handle EAGAIN.
- const uint32 num_preroll = latency_micros_ / micros_per_packet_;
- for (uint32 i = 0; i < num_preroll; ++i) {
- BufferPacket(packet_.get());
- WritePacket(packet_.get());
- }
-
- ScheduleNextWrite(packet_.get());
+ ScheduleNextWrite();
}
void AlsaPcmOutputStream::CloseTask() {
@@ -456,67 +462,51 @@ void AlsaPcmOutputStream::CloseTask() {
playback_handle_ = NULL;
// Release the buffer.
- packet_.reset();
+ buffer_.reset();
// Signal anything that might already be scheduled to stop.
stop_stream_ = true;
}
-void AlsaPcmOutputStream::BufferPacket(Packet* packet) {
+void AlsaPcmOutputStream::BufferPacket() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
// If stopped, simulate a 0-lengthed packet.
if (stop_stream_) {
- packet->used = packet->size = 0;
+ buffer_->Clear();
return;
}
- // Request more data if we don't have any cached.
- if (packet->used >= packet->size) {
+ // Request more data if we have capacity.
+ if (buffer_->forward_capacity() > buffer_->forward_bytes()) {
// Before making a request to source for data. We need to determine the
// delay (in bytes) for the requested data to be played.
- snd_pcm_sframes_t delay = 0;
-
- // Don't query ALSA's delay if we have underrun since it'll be jammed at
- // some non-zero value and potentially even negative!
- if (wrapper_->PcmState(playback_handle_) != SND_PCM_STATE_XRUN) {
- int error = wrapper_->PcmDelay(playback_handle_, &delay);
- if (error >= 0) {
- // Convert frames to bytes, but watch out for those negatives!
- delay = (delay < 0 ? 0 : delay) * bytes_per_output_frame_;
- } else {
- // Assume a delay of zero and attempt to recover the device.
- delay = 0;
- error = wrapper_->PcmRecover(playback_handle_,
- error,
- kPcmRecoverIsSilent);
- if (error < 0) {
- LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
- }
- }
- }
+ snd_pcm_sframes_t delay = buffer_->forward_bytes() * bytes_per_frame_ /
+ bytes_per_output_frame_ + GetCurrentDelay() * bytes_per_output_frame_;
- packet->used = 0;
- packet->size = shared_data_.OnMoreData(this, packet->buffer.get(),
- packet->capacity, delay);
- CHECK(packet->size <= packet->capacity) << "Data source overran buffer.";
+ media::DataBuffer* packet = new media::DataBuffer(packet_size_);
+ size_t packet_size =
+ shared_data_.OnMoreData(this, packet->GetWritableData(),
+ packet->GetBufferSize(), delay);
+ CHECK(packet_size <= packet->GetBufferSize()) <<
+ "Data source overran buffer.";
// This should not happen, but incase it does, drop any trailing bytes
// that aren't large enough to make a frame. Without this, packet writing
// may stall because the last few bytes in the packet may never get used by
// WritePacket.
- DCHECK(packet->size % bytes_per_frame_ == 0);
- packet->size = (packet->size / bytes_per_frame_) * bytes_per_frame_;
+ DCHECK(packet_size % bytes_per_frame_ == 0);
+ packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_;
if (should_downmix_) {
- if (media::FoldChannels(packet->buffer.get(),
- packet->size,
+ if (media::FoldChannels(packet->GetWritableData(),
+ packet_size,
channels_,
bytes_per_sample_,
shared_data_.volume())) {
// Adjust packet size for downmix.
- packet->size =
- packet->size / bytes_per_frame_ * bytes_per_output_frame_;
+ packet_size =
+ packet_size / bytes_per_frame_ * bytes_per_output_frame_;
} else {
LOG(ERROR) << "Folding failed";
}
@@ -526,59 +516,60 @@ void AlsaPcmOutputStream::BufferPacket(Packet* packet) {
// Handle channel order for 5.0 audio.
if (channels_ == 5) {
if (bytes_per_sample_ == 1) {
- Swizzle50Layout(reinterpret_cast<uint8*>(packet->buffer.get()),
- packet->size);
+ Swizzle50Layout(packet->GetWritableData(), packet_size);
} else if (bytes_per_sample_ == 2) {
- Swizzle50Layout(reinterpret_cast<int16*>(packet->buffer.get()),
- packet->size);
+ Swizzle50Layout(packet->GetWritableData(), packet_size);
} else if (bytes_per_sample_ == 4) {
- Swizzle50Layout(reinterpret_cast<int32*>(packet->buffer.get()),
- packet->size);
+ Swizzle50Layout(packet->GetWritableData(), packet_size);
}
}
// Handle channel order for 5.1 audio.
if (channels_ == 6) {
if (bytes_per_sample_ == 1) {
- Swizzle51Layout(reinterpret_cast<uint8*>(packet->buffer.get()),
- packet->size);
+ Swizzle51Layout(packet->GetWritableData(), packet_size);
} else if (bytes_per_sample_ == 2) {
- Swizzle51Layout(reinterpret_cast<int16*>(packet->buffer.get()),
- packet->size);
+ Swizzle51Layout(packet->GetWritableData(), packet_size);
} else if (bytes_per_sample_ == 4) {
- Swizzle51Layout(reinterpret_cast<int32*>(packet->buffer.get()),
- packet->size);
+ Swizzle51Layout(packet->GetWritableData(), packet_size);
}
}
- media::AdjustVolume(packet->buffer.get(),
- packet->size,
+ media::AdjustVolume(packet->GetWritableData(),
+ packet_size,
channels_,
bytes_per_sample_,
shared_data_.volume());
+
+ packet->SetDataSize(packet_size);
+
+ // Add the packet to the buffer.
+ buffer_->Append(packet);
}
}
}
-void AlsaPcmOutputStream::WritePacket(Packet* packet) {
+void AlsaPcmOutputStream::WritePacket() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
- CHECK(packet->size % bytes_per_output_frame_ == 0);
-
// If the device is in error, just eat the bytes.
if (stop_stream_) {
- packet->used = packet->size;
+ buffer_->Clear();
return;
}
- if (packet->used < packet->size) {
- char* buffer_pos = packet->buffer.get() + packet->used;
- snd_pcm_sframes_t frames = FramesInPacket(*packet, bytes_per_output_frame_);
+ CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u);
+
+ const uint8* buffer_data;
+ size_t buffer_size;
+ if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) {
+ buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_);
+ snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_;
DCHECK_GT(frames, 0);
snd_pcm_sframes_t frames_written =
- wrapper_->PcmWritei(playback_handle_, buffer_pos, frames);
+ wrapper_->PcmWritei(playback_handle_, buffer_data, frames);
if (frames_written < 0) {
// Attempt once to immediately recover from EINTR,
// EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket
@@ -599,7 +590,14 @@ void AlsaPcmOutputStream::WritePacket(Packet* packet) {
stop_stream_ = true;
}
} else {
- packet->used += frames_written * bytes_per_output_frame_;
+ if (frames_written > frames) {
+ LOG(WARNING)
+ << "snd_pcm_writei() has written more frame that we asked.";
+ frames_written = frames;
+ }
+
+ // Seek forward in the buffer after we've written some data to ALSA.
+ buffer_->Seek(frames_written * bytes_per_output_frame_);
}
}
}
@@ -611,25 +609,22 @@ void AlsaPcmOutputStream::WriteTask() {
return;
}
- BufferPacket(packet_.get());
- WritePacket(packet_.get());
+ BufferPacket();
+ WritePacket();
- ScheduleNextWrite(packet_.get());
+ ScheduleNextWrite();
}
-void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) {
+void AlsaPcmOutputStream::ScheduleNextWrite() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
if (stop_stream_) {
return;
}
- // Calculate when we should have enough buffer for another packet of data.
- // Make sure to take into consideration down-mixing.
- uint32 frames_leftover =
- FramesInPacket(*current_packet, bytes_per_output_frame_);
- uint32 frames_avail_wanted =
- (frames_leftover > 0) ? frames_leftover : frames_per_packet_;
+ // Next write is scheduled for the moment when half of the buffer is
+ // available.
+ uint32 frames_avail_wanted = alsa_buffer_frames_ / 2;
uint32 available_frames = GetAvailableFrames();
uint32 next_fill_time_ms = 0;
@@ -649,13 +644,10 @@ void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) {
}
// Avoid busy looping if the data source is exhausted.
- if (current_packet->size == 0) {
+ if (buffer_->forward_bytes() == 0) {
next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds);
}
- // Wake up sooner than should be necessary to avoid stutter.
- next_fill_time_ms /= 2; // TODO(fbarchard): Remove this hack.
-
// Only schedule more reads/writes if we are still in the playing state.
if (shared_data_.state() == kIsPlaying) {
if (next_fill_time_ms == 0) {
@@ -673,11 +665,6 @@ void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) {
}
}
-uint32 AlsaPcmOutputStream::FramesInPacket(const Packet& packet,
- uint32 bytes_per_frame) {
- return (packet.size - packet.used) / bytes_per_frame;
-}
-
uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) {
return frames * base::Time::kMicrosecondsPerSecond / sample_rate;
}
@@ -805,6 +792,29 @@ snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() {
return available_frames;
}
+snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() {
+ snd_pcm_sframes_t delay = 0;
+
+ // Don't query ALSA's delay if we have underrun since it'll be jammed at
+ // some non-zero value and potentially even negative!
+ if (wrapper_->PcmState(playback_handle_) != SND_PCM_STATE_XRUN) {
+ int error = wrapper_->PcmDelay(playback_handle_, &delay);
+ if (error < 0) {
+ // Assume a delay of zero and attempt to recover the device.
+ delay = 0;
+ error = wrapper_->PcmRecover(playback_handle_,
+ error,
+ kPcmRecoverIsSilent);
+ if (error < 0) {
+ LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
+ }
+ }
+ if (delay < 0)
+ delay = 0;
+ }
+ return delay;
+}
+
snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) {
// For auto-selection:
// 1) Attempt to open a device that best matches the number of channels
diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h
index 192186b..da77cf2 100644
--- a/media/audio/linux/alsa_output.h
+++ b/media/audio/linux/alsa_output.h
@@ -39,6 +39,10 @@
#include "media/audio/audio_output.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
+namespace media {
+class SeekableBuffer;
+}; // namespace media
+
class AlsaWrapper;
class AudioManagerLinux;
@@ -94,7 +98,7 @@ class AlsaPcmOutputStream :
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_Negative);
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_StopStream);
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_Underrun);
- FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket);
+ FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer);
FRIEND_TEST(AlsaPcmOutputStreamTest, ConstructedState);
FRIEND_TEST(AlsaPcmOutputStreamTest, LatencyFloor);
FRIEND_TEST(AlsaPcmOutputStreamTest, OpenClose);
@@ -110,22 +114,6 @@ class AlsaPcmOutputStream :
virtual ~AlsaPcmOutputStream();
- // In-memory buffer to hold sound samples before pushing to the sound device.
- // TODO(ajwong): There are now about 3 buffer/packet implementations. Factor
- // them out.
- struct Packet {
- explicit Packet(uint32 new_capacity)
- : capacity(new_capacity),
- size(0),
- used(0),
- buffer(new char[capacity]) {
- }
- uint32 capacity;
- uint32 size;
- uint32 used;
- scoped_array<char> buffer;
- };
-
// Flags indicating the state of the stream.
enum InternalState {
kInError = 0,
@@ -144,13 +132,12 @@ class AlsaPcmOutputStream :
// Functions to get another packet from the data source and write it into the
// ALSA device.
- void BufferPacket(Packet* packet);
- void WritePacket(Packet* packet);
+ void BufferPacket();
+ void WritePacket();
void WriteTask();
- void ScheduleNextWrite(Packet* current_packet);
+ void ScheduleNextWrite();
// Utility functions for talking with the ALSA API.
- static uint32 FramesInPacket(const Packet& packet, uint32 bytes_per_frame);
static uint32 FramesToMicros(uint32 frames, uint32 sample_rate);
static uint32 FramesToMillis(uint32 frames, uint32 sample_rate);
std::string FindDeviceForChannels(uint32 channels);
@@ -159,6 +146,7 @@ class AlsaPcmOutputStream :
uint32 latency);
bool CloseDevice(snd_pcm_t* handle);
snd_pcm_sframes_t GetAvailableFrames();
+ snd_pcm_sframes_t GetCurrentDelay();
// Attempts to find the best matching linux audio device for the given number
// of channels. This function will set |device_name_| and |should_downmix_|.
@@ -227,8 +215,10 @@ class AlsaPcmOutputStream :
std::string device_name_;
bool should_downmix_;
uint32 latency_micros_;
+ uint32 packet_size_;
uint32 micros_per_packet_;
uint32 bytes_per_output_frame_;
+ uint32 alsa_buffer_frames_;
// Flag indicating the code should stop reading from the data source or
// writing to the ALSA device. This is set because the device has entered
@@ -246,7 +236,7 @@ class AlsaPcmOutputStream :
// Handle to the actual PCM playback device.
snd_pcm_t* playback_handle_;
- scoped_ptr<Packet> packet_;
+ scoped_ptr<media::SeekableBuffer> buffer_;
uint32 frames_per_packet_;
// Used to check which message loop is allowed to call the public APIs.
diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc
index 26237ca..546bd88 100644
--- a/media/audio/linux/alsa_output_unittest.cc
+++ b/media/audio/linux/alsa_output_unittest.cc
@@ -8,6 +8,8 @@
#include "media/audio/linux/alsa_output.h"
#include "media/audio/linux/alsa_wrapper.h"
#include "media/audio/linux/audio_manager_linux.h"
+#include "media/base/data_buffer.h"
+#include "media/base/seekable_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -46,6 +48,9 @@ class MockAlsaWrapper : public AlsaWrapper {
snd_pcm_access_t access, unsigned int channels,
unsigned int rate, int soft_resample,
unsigned int latency));
+ MOCK_METHOD3(PcmGetParams, int(snd_pcm_t* handle,
+ snd_pcm_uframes_t* buffer_size,
+ snd_pcm_uframes_t* period_size));
MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle));
MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t(snd_pcm_t* handle));
MOCK_METHOD1(PcmState, snd_pcm_state_t(snd_pcm_t* handle));
@@ -76,10 +81,8 @@ class MockAudioManagerLinux : public AudioManagerLinux {
class AlsaPcmOutputStreamTest : public testing::Test {
protected:
- AlsaPcmOutputStreamTest()
- : packet_(kTestPacketSize + 1) {
+ AlsaPcmOutputStreamTest() {
test_stream_ = CreateStreamWithChannels(kTestChannels);
- packet_.size = kTestPacketSize;
}
virtual ~AlsaPcmOutputStreamTest() {
@@ -107,6 +110,15 @@ class AlsaPcmOutputStreamTest : public testing::Test {
return strdup("Output");
}
+ // Helper function to initialize |test_stream_->buffer_|. Must be called
+ // in all tests that use buffer_ without opening the stream.
+ void InitBuffer() {
+ packet_ = new media::DataBuffer(kTestPacketSize);
+ packet_->SetDataSize(kTestPacketSize);
+ test_stream_->buffer_.reset(new media::SeekableBuffer(0, kTestPacketSize));
+ test_stream_->buffer_->Append(packet_.get());
+ }
+
static const int kTestChannels;
static const int kTestSampleRate;
static const int kTestBitsPerSample;
@@ -132,7 +144,7 @@ class AlsaPcmOutputStreamTest : public testing::Test {
StrictMock<MockAudioManagerLinux> mock_manager_;
MessageLoop message_loop_;
scoped_refptr<AlsaPcmOutputStream> test_stream_;
- AlsaPcmOutputStream::Packet packet_;
+ scoped_refptr<media::DataBuffer> packet_;
private:
DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStreamTest);
@@ -224,6 +236,10 @@ TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) {
PcmSetParams(_, _, _, _, _, _,
AlsaPcmOutputStream::kMinLatencyMicros))
.WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kTestPacketSize),
+ SetArgumentPointee<2>(kTestPacketSize / 2),
+ Return(0)));
ASSERT_TRUE(test_stream_->Open(kMinLatencyPacketSize));
message_loop_.RunAllPending();
@@ -255,6 +271,10 @@ TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) {
EXPECT_CALL(mock_alsa_wrapper_,
PcmSetParams(_, _, _, _, _, _, expected_micros))
.WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kTestPacketSize),
+ SetArgumentPointee<2>(kTestPacketSize / 2),
+ Return(0)));
ASSERT_TRUE(test_stream_->Open(kOverMinLatencyPacketSize));
message_loop_.RunAllPending();
@@ -294,6 +314,10 @@ TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
1,
expected_micros))
.WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(kFakeHandle, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kTestPacketSize),
+ SetArgumentPointee<2>(kTestPacketSize/2),
+ Return(0)));
// Open the stream.
ASSERT_TRUE(test_stream_->Open(kTestPacketSize));
@@ -303,7 +327,7 @@ TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
test_stream_->shared_data_.state());
EXPECT_EQ(kFakeHandle, test_stream_->playback_handle_);
EXPECT_EQ(kTestFramesPerPacket, test_stream_->frames_per_packet_);
- EXPECT_TRUE(test_stream_->packet_.get());
+ EXPECT_TRUE(test_stream_->buffer_.get());
EXPECT_FALSE(test_stream_->stop_stream_);
// Now close it and test that everything was released.
@@ -316,7 +340,7 @@ TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
message_loop_.RunAllPending();
EXPECT_TRUE(test_stream_->playback_handle_ == NULL);
- EXPECT_FALSE(test_stream_->packet_.get());
+ EXPECT_FALSE(test_stream_->buffer_.get());
EXPECT_TRUE(test_stream_->stop_stream_);
}
@@ -338,7 +362,7 @@ TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) {
test_stream_->shared_data_.state());
EXPECT_TRUE(test_stream_->stop_stream_);
EXPECT_TRUE(test_stream_->playback_handle_ == NULL);
- EXPECT_FALSE(test_stream_->packet_.get());
+ EXPECT_FALSE(test_stream_->buffer_.get());
// Close the stream since we opened it to make destruction happy.
EXPECT_CALL(mock_manager_, ReleaseStream(test_stream_.get()));
@@ -372,7 +396,7 @@ TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) {
test_stream_->shared_data_.state());
EXPECT_TRUE(test_stream_->stop_stream_);
EXPECT_TRUE(test_stream_->playback_handle_ == NULL);
- EXPECT_FALSE(test_stream_->packet_.get());
+ EXPECT_FALSE(test_stream_->buffer_.get());
// Close the stream since we opened it to make destruction happy.
EXPECT_CALL(mock_manager_, ReleaseStream(test_stream_.get()));
@@ -389,6 +413,10 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
Return(0)));
EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
.WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kTestPacketSize),
+ SetArgumentPointee<2>(kTestPacketSize / 2),
+ Return(0)));
// Open the stream.
ASSERT_TRUE(test_stream_->Open(kTestPacketSize));
@@ -400,26 +428,11 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
EXPECT_CALL(mock_alsa_wrapper_, PcmPrepare(kFakeHandle))
.WillOnce(Return(0));
- // Expect the pre-roll.
- MockAudioSourceCallback mock_callback;
- EXPECT_CALL(mock_alsa_wrapper_, PcmState(kFakeHandle))
- .Times(2)
- .WillRepeatedly(Return(SND_PCM_STATE_RUNNING));
- EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(kFakeHandle, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgumentPointee<1>(0), Return(0)));
- EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), _, kTestPacketSize, 0))
- .Times(2)
- .WillRepeatedly(Return(kTestPacketSize));
- EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
- .Times(2)
- .WillRepeatedly(Return(kTestPacketSize));
-
// Expect scheduling.
EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
.WillOnce(Return(1));
+ MockAudioSourceCallback mock_callback;
test_stream_->Start(&mock_callback);
message_loop_.RunAllPending();
@@ -434,46 +447,52 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
}
TEST_F(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket) {
+ InitBuffer();
+
// Nothing should happen. Don't set any expectations and Our strict mocks
// should verify most of this.
- // Test regular used-up packet.
- packet_.used = packet_.size;
- test_stream_->WritePacket(&packet_);
-
- // Test empty packet.
- packet_.used = packet_.size = 0;
- test_stream_->WritePacket(&packet_);
+ // Test empty buffer.
+ test_stream_->buffer_->Clear();
+ test_stream_->WritePacket();
}
TEST_F(AlsaPcmOutputStreamTest, WritePacket_NormalPacket) {
+ InitBuffer();
+
// Write a little less than half the data.
- EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, packet_.buffer.get(), _))
- .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1));
+ int written = packet_->GetDataSize() / kTestBytesPerFrame / 2 - 1;
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, packet_->GetData(), _))
+ .WillOnce(Return(written));
- test_stream_->WritePacket(&packet_);
+ test_stream_->WritePacket();
- ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+ ASSERT_EQ(test_stream_->buffer_->forward_bytes(),
+ packet_->GetDataSize() - written * kTestBytesPerFrame);
// Write the rest.
EXPECT_CALL(mock_alsa_wrapper_,
- PcmWritei(_, packet_.buffer.get() + packet_.used, _))
- .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 + 1));
- test_stream_->WritePacket(&packet_);
- EXPECT_EQ(packet_.size, packet_.used);
+ PcmWritei(_, packet_->GetData() + written * kTestBytesPerFrame,
+ _))
+ .WillOnce(Return(packet_->GetDataSize() / kTestBytesPerFrame - written));
+ test_stream_->WritePacket();
+ EXPECT_EQ(0u, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) {
+ InitBuffer();
+
// Fail due to a recoverable error and see that PcmRecover code path
// continues normally.
EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _))
.WillOnce(Return(-EINTR));
EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(_, _, _))
- .WillOnce(Return(packet_.size / kTestBytesPerFrame / 2 - 1));
+ .WillOnce(Return(packet_->GetDataSize() / kTestBytesPerFrame / 2 - 1));
- test_stream_->WritePacket(&packet_);
+ test_stream_->WritePacket();
- ASSERT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+ ASSERT_EQ(test_stream_->buffer_->forward_bytes(),
+ packet_->GetDataSize() / 2 + kTestBytesPerFrame);
// Fail the next write, and see that stop_stream_ is set.
EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(_, _, _))
@@ -482,20 +501,24 @@ TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) {
.WillOnce(Return(kTestFailedErrno));
EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
.WillOnce(Return(kDummyMessage));
- test_stream_->WritePacket(&packet_);
- EXPECT_EQ(packet_.size / 2 - kTestBytesPerFrame, packet_.used);
+ test_stream_->WritePacket();
+ EXPECT_EQ(test_stream_->buffer_->forward_bytes(),
+ packet_->GetDataSize() / 2 + kTestBytesPerFrame);
EXPECT_TRUE(test_stream_->stop_stream_);
}
TEST_F(AlsaPcmOutputStreamTest, WritePacket_StopStream) {
+ InitBuffer();
+
// No expectations set on the strict mock because nothing should be called.
test_stream_->stop_stream_ = true;
- test_stream_->WritePacket(&packet_);
- EXPECT_EQ(packet_.size, packet_.used);
+ test_stream_->WritePacket();
+ EXPECT_EQ(0u, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
- packet_.used = packet_.size;
+ InitBuffer();
+ test_stream_->buffer_->Clear();
// Return a partially filled packet.
MockAudioSourceCallback mock_callback;
@@ -504,19 +527,18 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
.WillOnce(DoAll(SetArgumentPointee<1>(1), Return(0)));
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), packet_.buffer.get(),
- packet_.capacity, kTestBytesPerFrame))
+ OnMoreData(test_stream_.get(), _, _, kTestBytesPerFrame))
.WillOnce(Return(10));
test_stream_->shared_data_.set_source_callback(&mock_callback);
- test_stream_->BufferPacket(&packet_);
+ test_stream_->BufferPacket();
- EXPECT_EQ(0u, packet_.used);
- EXPECT_EQ(10u, packet_.size);
+ EXPECT_EQ(10u, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) {
- packet_.used = packet_.size;
+ InitBuffer();
+ test_stream_->buffer_->Clear();
// Simulate where the underrun has occurred right after checking the delay.
MockAudioSourceCallback mock_callback;
@@ -525,41 +547,38 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) {
EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
.WillOnce(DoAll(SetArgumentPointee<1>(-1), Return(0)));
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), packet_.buffer.get(),
- packet_.capacity, 0))
+ OnMoreData(test_stream_.get(), _, _, 0))
.WillOnce(Return(10));
test_stream_->shared_data_.set_source_callback(&mock_callback);
- test_stream_->BufferPacket(&packet_);
+ test_stream_->BufferPacket();
- EXPECT_EQ(0u, packet_.used);
- EXPECT_EQ(10u, packet_.size);
+ EXPECT_EQ(10u, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) {
- packet_.used = packet_.size;
+ InitBuffer();
+ test_stream_->buffer_->Clear();
// If ALSA has underrun then we should assume a delay of zero.
MockAudioSourceCallback mock_callback;
EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
.WillOnce(Return(SND_PCM_STATE_XRUN));
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), packet_.buffer.get(),
- packet_.capacity, 0))
+ OnMoreData(test_stream_.get(), _, _, 0))
.WillOnce(Return(10));
test_stream_->shared_data_.set_source_callback(&mock_callback);
- test_stream_->BufferPacket(&packet_);
+ test_stream_->BufferPacket();
- EXPECT_EQ(0u, packet_.used);
- EXPECT_EQ(10u, packet_.size);
+ EXPECT_EQ(10u, test_stream_->buffer_->forward_bytes());
}
-TEST_F(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket) {
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer) {
+ InitBuffer();
// No expectations set on the strict mock because nothing should be called.
- test_stream_->BufferPacket(&packet_);
- EXPECT_EQ(0u, packet_.used);
- EXPECT_EQ(kTestPacketSize, packet_.size);
+ test_stream_->BufferPacket();
+ EXPECT_EQ(kTestPacketSize, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_DeviceSelect) {
@@ -684,19 +703,21 @@ TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail) {
}
TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) {
+ InitBuffer();
test_stream_->stop_stream_ = true;
- test_stream_->BufferPacket(&packet_);
- EXPECT_EQ(0u, packet_.used);
- EXPECT_EQ(0u, packet_.size);
+ test_stream_->BufferPacket();
+ EXPECT_EQ(0u, test_stream_->buffer_->forward_bytes());
}
TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) {
test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened);
test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+ InitBuffer();
+
EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
.WillOnce(Return(10));
- test_stream_->ScheduleNextWrite(&packet_);
+ test_stream_->ScheduleNextWrite();
test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsClosed);
}
@@ -705,8 +726,10 @@ TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) {
test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsOpened);
test_stream_->shared_data_.TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+ InitBuffer();
+
test_stream_->stop_stream_ = true;
- test_stream_->ScheduleNextWrite(&packet_);
+ test_stream_->ScheduleNextWrite();
// TODO(ajwong): Find a way to test whether or not another task has been
// posted so we can verify that the Alsa code will indeed break the task
diff --git a/media/audio/linux/alsa_wrapper.cc b/media/audio/linux/alsa_wrapper.cc
index a028484..a70856a 100644
--- a/media/audio/linux/alsa_wrapper.cc
+++ b/media/audio/linux/alsa_wrapper.cc
@@ -67,6 +67,11 @@ int AlsaWrapper::PcmSetParams(snd_pcm_t* handle, snd_pcm_format_t format,
soft_resample, latency);
}
+int AlsaWrapper::PcmGetParams(snd_pcm_t* handle, snd_pcm_uframes_t* buffer_size,
+ snd_pcm_uframes_t* period_size) {
+ return snd_pcm_get_params(handle, buffer_size, period_size);
+}
+
snd_pcm_sframes_t AlsaWrapper::PcmAvailUpdate(snd_pcm_t* handle) {
return snd_pcm_avail_update(handle);
}
diff --git a/media/audio/linux/alsa_wrapper.h b/media/audio/linux/alsa_wrapper.h
index ce045ea..4469687 100644
--- a/media/audio/linux/alsa_wrapper.h
+++ b/media/audio/linux/alsa_wrapper.h
@@ -33,6 +33,8 @@ class AlsaWrapper {
snd_pcm_access_t access, unsigned int channels,
unsigned int rate, int soft_resample,
unsigned int latency);
+ virtual int PcmGetParams(snd_pcm_t* handle, snd_pcm_uframes_t* buffer_size,
+ snd_pcm_uframes_t* period_size);
virtual const char* PcmName(snd_pcm_t* handle);
virtual snd_pcm_sframes_t PcmAvailUpdate(snd_pcm_t* handle);
virtual snd_pcm_state_t PcmState(snd_pcm_t* handle);