summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/audio_buffers_state.cc22
-rw-r--r--media/audio/audio_buffers_state.h33
-rw-r--r--media/audio/audio_io.h6
-rw-r--r--media/audio/audio_output_controller.cc45
-rw-r--r--media/audio/audio_output_controller.h19
-rw-r--r--media/audio/audio_output_controller_unittest.cc88
-rw-r--r--media/audio/fake_audio_output_stream.cc5
-rw-r--r--media/audio/fake_audio_output_stream.h4
-rw-r--r--media/audio/linux/alsa_output.cc55
-rw-r--r--media/audio/linux/alsa_output.h5
-rw-r--r--media/audio/linux/alsa_output_unittest.cc55
-rw-r--r--media/audio/mac/audio_output_mac.cc6
-rw-r--r--media/audio/mac/audio_output_mac_unittest.cc26
-rw-r--r--media/audio/simple_sources.cc12
-rw-r--r--media/audio/simple_sources.h9
-rw-r--r--media/audio/simple_sources_unittest.cc5
-rw-r--r--media/audio/win/audio_output_win_unittest.cc59
-rw-r--r--media/audio/win/waveout_output_win.cc6
18 files changed, 312 insertions, 148 deletions
diff --git a/media/audio/audio_buffers_state.cc b/media/audio/audio_buffers_state.cc
new file mode 100644
index 0000000..f31cf1d
--- /dev/null
+++ b/media/audio/audio_buffers_state.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 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/audio/audio_buffers_state.h"
+
+AudioBuffersState::AudioBuffersState()
+ : pending_bytes(0),
+ hardware_delay_bytes(0),
+ timestamp(base::Time::Now()) {
+}
+
+AudioBuffersState::AudioBuffersState(int pending_bytes,
+ int hardware_delay_bytes)
+ : pending_bytes(pending_bytes),
+ hardware_delay_bytes(hardware_delay_bytes),
+ timestamp(base::Time::Now()) {
+}
+
+int AudioBuffersState::total_bytes() {
+ return pending_bytes + hardware_delay_bytes;
+}
diff --git a/media/audio/audio_buffers_state.h b/media/audio/audio_buffers_state.h
new file mode 100644
index 0000000..e3a2faf
--- /dev/null
+++ b/media/audio/audio_buffers_state.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 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_AUDIO_AUDIO_BUFFERS_STATE_H_
+#define MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
+
+#include "base/time.h"
+
+// AudioBuffersState struct stores current state of audio buffers along with
+// the timestamp of the moment this state corresponds to. It is used for audio
+// synchronization.
+struct AudioBuffersState {
+ AudioBuffersState();
+ AudioBuffersState(int pending_bytes, int hardware_delay_bytes);
+
+ int total_bytes();
+
+ // Number of bytes we currently have in our software buffer.
+ int pending_bytes;
+
+ // Number of bytes that have been written to the device, but haven't
+ // been played yet.
+ int hardware_delay_bytes;
+
+ // Timestamp of the moment when the buffers state was captured. It is used
+ // to account for the time it takes to deliver AudioBuffersState from
+ // the browser process to the renderer.
+ base::Time timestamp;
+};
+
+
+#endif // MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h
index 4f23686..3aa22cf 100644
--- a/media/audio/audio_io.h
+++ b/media/audio/audio_io.h
@@ -6,6 +6,7 @@
#define MEDIA_AUDIO_AUDIO_IO_H_
#include "base/basictypes.h"
+#include "media/audio/audio_buffers_state.h"
// Low-level audio output support. To make sound there are 3 objects involved:
// - AudioSource : produces audio samples on a pull model. Implements
@@ -55,8 +56,9 @@ class AudioOutputStream {
// |dest| is platform and format specific.
// |pending_bytes| is the number of bytes will be played before the
// requested data is played.
- virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes) = 0;
+ virtual uint32 OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) = 0;
// The stream is done with this callback. After this call the audio source
// can go away or be destroyed.
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index 439583c..12bcf72 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -37,8 +37,8 @@ AudioOutputController::AudioOutputController(EventHandler* handler,
stream_(NULL),
volume_(1.0),
state_(kEmpty),
- hardware_pending_bytes_(0),
- buffer_capacity_(capacity),
+ buffer_(0, capacity),
+ pending_request_(false),
sync_reader_(sync_reader) {
}
@@ -133,7 +133,8 @@ void AudioOutputController::SetVolume(double volume) {
void AudioOutputController::EnqueueData(const uint8* data, uint32 size) {
// Write data to the push source and ask for more data if needed.
AutoLock auto_lock(lock_);
- push_source_.Write(data, size);
+ buffer_.Append(data, size);
+ pending_request_ = false;
SubmitOnMoreData_Locked();
}
@@ -222,7 +223,7 @@ void AudioOutputController::DoFlush() {
if (!sync_reader_) {
if (state_ != kPaused)
return;
- push_source_.ClearAll();
+ buffer_.Clear();
}
}
@@ -265,34 +266,30 @@ void AudioOutputController::DoReportError(int code) {
handler_->OnError(this, code);
}
-uint32 AudioOutputController::OnMoreData(AudioOutputStream* stream,
- void* dest,
- uint32 max_size,
- uint32 pending_bytes) {
+uint32 AudioOutputController::OnMoreData(
+ AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state) {
// If regular latency mode is used.
if (!sync_reader_) {
AutoLock auto_lock(lock_);
- // Record the callback time.
- last_callback_time_ = base::Time::Now();
+ // Save current buffers state.
+ buffers_state_ = buffers_state;
if (state_ != kPlaying) {
// Don't read anything. Save the number of bytes in the hardware buffer.
- hardware_pending_bytes_ = pending_bytes;
return 0;
}
- // Push source doesn't need to know the stream and number of pending
- // bytes. So just pass in NULL and 0.
- uint32 size = push_source_.OnMoreData(NULL, dest, max_size, 0);
- hardware_pending_bytes_ = pending_bytes + size;
+ uint32 size = buffer_.Read(dest, max_size);
+ buffers_state_.pending_bytes += size;
SubmitOnMoreData_Locked();
return size;
}
// Low latency mode.
uint32 size = sync_reader_->Read(dest, max_size);
- sync_reader_->UpdatePendingBytes(pending_bytes + size);
+ sync_reader_->UpdatePendingBytes(buffers_state.total_bytes() + size);
return size;
}
@@ -302,9 +299,6 @@ void AudioOutputController::OnClose(AudioOutputStream* stream) {
// Push source doesn't need to know the stream so just pass in NULL.
if (LowLatencyMode()) {
sync_reader_->Close();
- } else {
- AutoLock auto_lock(lock_);
- push_source_.OnClose(NULL);
}
}
@@ -318,18 +312,21 @@ void AudioOutputController::OnError(AudioOutputStream* stream, int code) {
void AudioOutputController::SubmitOnMoreData_Locked() {
lock_.AssertAcquired();
- if (push_source_.UnProcessedBytes() > buffer_capacity_)
+ if (buffer_.forward_bytes() > buffer_.forward_capacity())
+ return;
+
+ if (pending_request_)
return;
+ pending_request_ = true;
- base::Time timestamp = last_callback_time_;
- uint32 pending_bytes = hardware_pending_bytes_ +
- push_source_.UnProcessedBytes();
+ AudioBuffersState buffers_state = buffers_state_;
+ buffers_state.pending_bytes += buffer_.forward_bytes();
// If we need more data then call the event handler to ask for more data.
// It is okay that we don't lock in this block because the parameters are
// correct and in the worst case we are just asking more data than needed.
AutoUnlock auto_unlock(lock_);
- handler_->OnMoreData(this, timestamp, pending_bytes);
+ handler_->OnMoreData(this, buffers_state);
}
} // namespace media
diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h
index 8208e28..6c1ba89 100644
--- a/media/audio/audio_output_controller.h
+++ b/media/audio/audio_output_controller.h
@@ -9,6 +9,7 @@
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
+#include "media/audio/audio_buffers_state.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/simple_sources.h"
@@ -76,8 +77,7 @@ class AudioOutputController
// |pending_bytes| is the number of bytes still on the controller.
// |timestamp| is then time when |pending_bytes| is recorded.
virtual void OnMoreData(AudioOutputController* controller,
- base::Time timestamp,
- uint32 pending_bytes) = 0;
+ AudioBuffersState buffers_state) = 0;
};
// A synchronous reader interface used by AudioOutputController for
@@ -156,8 +156,8 @@ class AudioOutputController
///////////////////////////////////////////////////////////////////////////
// AudioSourceCallback methods.
- virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes);
+ virtual uint32 OnMoreData(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state);
virtual void OnClose(AudioOutputStream* stream);
virtual void OnError(AudioOutputStream* stream, int code);
@@ -189,15 +189,14 @@ class AudioOutputController
// is not required for reading on the audio controller thread.
State state_;
- uint32 hardware_pending_bytes_;
- base::Time last_callback_time_;
+ AudioBuffersState buffers_state_;
- // The |lock_| must be acquired whenever we access |push_source_|.
+ // The |lock_| must be acquired whenever we access |buffer_|.
Lock lock_;
- // PushSource role is to buffer and it's only used in regular latency mode.
- PushSource push_source_;
- uint32 buffer_capacity_;
+ media::SeekableBuffer buffer_;
+
+ bool pending_request_;
// SyncReader is used only in low latency mode for synchronous reading.
SyncReader* sync_reader_;
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index b479974d..6997016 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -37,9 +37,8 @@ class MockAudioOutputControllerEventHandler
MOCK_METHOD1(OnPaused, void(AudioOutputController* controller));
MOCK_METHOD2(OnError, void(AudioOutputController* controller,
int error_code));
- MOCK_METHOD3(OnMoreData,
- void(AudioOutputController* controller,
- base::Time timestamp, uint32 pending_bytes));
+ MOCK_METHOD2(OnMoreData, void(AudioOutputController* controller,
+ AudioBuffersState buffers_state));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
@@ -75,12 +74,6 @@ ACTION_P(SignalEvent, event) {
event->Signal();
}
-ACTION_P3(SignalEvent, event, count, limit) {
- if (++*count >= limit) {
- event->Signal();
- }
-}
-
// Helper functions used to close audio controller.
static void SignalClosedEvent(base::WaitableEvent* event) {
event->Signal();
@@ -98,6 +91,11 @@ TEST(AudioOutputControllerTest, CreateAndClose) {
return;
MockAudioOutputControllerEventHandler event_handler;
+
+ EXPECT_CALL(event_handler, OnCreated(NotNull()))
+ .Times(1);
+ EXPECT_CALL(event_handler, OnMoreData(NotNull(), _));
+
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannels,
kSampleRate, kBitsPerSample);
scoped_refptr<AudioOutputController> controller =
@@ -115,7 +113,6 @@ TEST(AudioOutputControllerTest, PlayAndClose) {
MockAudioOutputControllerEventHandler event_handler;
base::WaitableEvent event(false, false);
- int count = 0;
// If OnCreated is called then signal the event.
EXPECT_CALL(event_handler, OnCreated(NotNull()))
@@ -126,9 +123,9 @@ TEST(AudioOutputControllerTest, PlayAndClose) {
.Times(Exactly(1));
// If OnMoreData is called enough then signal the event.
- EXPECT_CALL(event_handler, OnMoreData(NotNull(), _, 0))
+ EXPECT_CALL(event_handler, OnMoreData(NotNull(), _))
.Times(AtLeast(10))
- .WillRepeatedly(SignalEvent(&event, &count, 10));
+ .WillRepeatedly(SignalEvent(&event));
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannels,
kSampleRate, kBitsPerSample);
@@ -140,9 +137,14 @@ TEST(AudioOutputControllerTest, PlayAndClose) {
// Wait for OnCreated() to be called.
event.Wait();
- // Play and then wait for the event to be signaled.
controller->Play();
- event.Wait();
+
+ // Wait until the date is requested at least 10 times.
+ for (int i = 0; i < 10; i++) {
+ event.Wait();
+ uint8 buf[1];
+ controller->EnqueueData(buf, 0);
+ }
// Now stop the controller.
CloseAudioController(controller);
@@ -154,7 +156,7 @@ TEST(AudioOutputControllerTest, PlayPauseClose) {
MockAudioOutputControllerEventHandler event_handler;
base::WaitableEvent event(false, false);
- int count = 0;
+ base::WaitableEvent pause_event(false, false);
// If OnCreated is called then signal the event.
EXPECT_CALL(event_handler, OnCreated(NotNull()))
@@ -166,14 +168,14 @@ TEST(AudioOutputControllerTest, PlayPauseClose) {
.Times(Exactly(1));
// If OnMoreData is called enough then signal the event.
- EXPECT_CALL(event_handler, OnMoreData(NotNull(), _, 0))
+ EXPECT_CALL(event_handler, OnMoreData(NotNull(), _))
.Times(AtLeast(10))
- .WillRepeatedly(SignalEvent(&event, &count, 10));
+ .WillRepeatedly(SignalEvent(&event));
// And then OnPaused() will be called.
EXPECT_CALL(event_handler, OnPaused(NotNull()))
.Times(Exactly(1))
- .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
+ .WillOnce(InvokeWithoutArgs(&pause_event, &base::WaitableEvent::Signal));
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannels,
kSampleRate, kBitsPerSample);
@@ -184,16 +186,20 @@ TEST(AudioOutputControllerTest, PlayPauseClose) {
// Wait for OnCreated() to be called.
event.Wait();
- event.Reset();
- // Play and then wait for the event to be signaled.
controller->Play();
- event.Wait();
- event.Reset();
+
+ // Wait until the date is requested at least 10 times.
+ for (int i = 0; i < 10; i++) {
+ event.Wait();
+ uint8 buf[1];
+ controller->EnqueueData(buf, 0);
+ }
// And then wait for pause to complete.
+ ASSERT_FALSE(pause_event.IsSignaled());
controller->Pause();
- event.Wait();
+ pause_event.Wait();
// Now stop the controller.
CloseAudioController(controller);
@@ -205,7 +211,7 @@ TEST(AudioOutputControllerTest, PlayPausePlay) {
MockAudioOutputControllerEventHandler event_handler;
base::WaitableEvent event(false, false);
- int count = 0;
+ base::WaitableEvent pause_event(false, false);
// If OnCreated is called then signal the event.
EXPECT_CALL(event_handler, OnCreated(NotNull()))
@@ -218,14 +224,14 @@ TEST(AudioOutputControllerTest, PlayPausePlay) {
.RetiresOnSaturation();
// If OnMoreData() is called enough then signal the event.
- EXPECT_CALL(event_handler, OnMoreData(NotNull(), _, 0))
- .Times(AtLeast(10))
- .WillRepeatedly(SignalEvent(&event, &count, 10));
+ EXPECT_CALL(event_handler, OnMoreData(NotNull(), _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(SignalEvent(&event));
// And then OnPaused() will be called.
EXPECT_CALL(event_handler, OnPaused(NotNull()))
.Times(Exactly(1))
- .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
+ .WillOnce(InvokeWithoutArgs(&pause_event, &base::WaitableEvent::Signal));
// OnPlaying() will be called only once.
EXPECT_CALL(event_handler, OnPlaying(NotNull()))
@@ -241,22 +247,30 @@ TEST(AudioOutputControllerTest, PlayPausePlay) {
// Wait for OnCreated() to be called.
event.Wait();
- event.Reset();
- // Play and then wait for the event to be signaled.
controller->Play();
- event.Wait();
- event.Reset();
+
+ // Wait until the date is requested at least 10 times.
+ for (int i = 0; i < 10; i++) {
+ event.Wait();
+ uint8 buf[1];
+ controller->EnqueueData(buf, 0);
+ }
// And then wait for pause to complete.
+ ASSERT_FALSE(pause_event.IsSignaled());
controller->Pause();
- event.Wait();
- event.Reset();
+ pause_event.Wait();
// Then we play again.
- // Play and then wait for the event to be signaled.
controller->Play();
- event.Wait();
+
+ // Wait until the date is requested at least 10 times.
+ for (int i = 0; i < 10; i++) {
+ event.Wait();
+ uint8 buf[1];
+ controller->EnqueueData(buf, 0);
+ }
// Now stop the controller.
CloseAudioController(controller);
@@ -292,7 +306,7 @@ TEST(AudioOutputControllerTest, CloseTwice) {
.WillOnce(SignalEvent(&event));
// One OnMoreData() is expected.
- EXPECT_CALL(event_handler, OnMoreData(NotNull(), _, 0))
+ EXPECT_CALL(event_handler, OnMoreData(NotNull(), _))
.Times(AtLeast(1))
.WillRepeatedly(SignalEvent(&event));
diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc
index 4f9dd84..1048fed 100644
--- a/media/audio/fake_audio_output_stream.cc
+++ b/media/audio/fake_audio_output_stream.cc
@@ -36,14 +36,15 @@ bool FakeAudioOutputStream::Open(uint32 packet_size) {
if (packet_size < sizeof(int16))
return false;
packet_size_ = packet_size;
- buffer_.reset(new char[packet_size_]);
+ buffer_.reset(new uint8[packet_size_]);
return true;
}
void FakeAudioOutputStream::Start(AudioSourceCallback* callback) {
callback_ = callback;
memset(buffer_.get(), 0, packet_size_);
- callback_->OnMoreData(this, buffer_.get(), packet_size_, 0);
+ callback_->OnMoreData(this, buffer_.get(), packet_size_,
+ AudioBuffersState(0, 0));
}
void FakeAudioOutputStream::Stop() {
diff --git a/media/audio/fake_audio_output_stream.h b/media/audio/fake_audio_output_stream.h
index c107c58..0914a1f 100644
--- a/media/audio/fake_audio_output_stream.h
+++ b/media/audio/fake_audio_output_stream.h
@@ -26,7 +26,7 @@ class FakeAudioOutputStream : public AudioOutputStream {
virtual void GetVolume(double* volume);
virtual void Close();
- char* buffer() { return buffer_.get(); }
+ uint8* buffer() { return buffer_.get(); }
double volume() { return volume_; }
private:
@@ -39,7 +39,7 @@ class FakeAudioOutputStream : public AudioOutputStream {
double volume_;
AudioSourceCallback* callback_;
- scoped_array<char> buffer_;
+ scoped_array<uint8> buffer_;
uint32 packet_size_;
bool closed_;
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc
index 47a9f56..9a07493 100644
--- a/media/audio/linux/alsa_output.cc
+++ b/media/audio/linux/alsa_output.cc
@@ -468,16 +468,25 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
// 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
+ // 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 = buffer_->forward_bytes() * bytes_per_frame_ /
- bytes_per_output_frame_;
+
+ // Amount of data currently in the ALSA's buffer.
+ uint32 alsa_buffered_frames = (alsa_buffer_frames_ - GetAvailableFrames());
+
+ // |buffer_delay| includes our buffer and ALSA's buffer.
+ uint32 buffer_delay = alsa_buffered_frames * bytes_per_frame_ +
+ buffer_->forward_bytes() * bytes_per_frame_ / bytes_per_output_frame_;
+
+ uint32 hardware_delay = (GetCurrentDelay() - alsa_buffered_frames) *
+ bytes_per_frame_;
scoped_refptr<media::DataBuffer> packet =
new media::DataBuffer(packet_size_);
size_t packet_size =
- shared_data_.OnMoreData(this, packet->GetWritableData(),
- packet->GetBufferSize(), delay);
+ shared_data_.OnMoreData(
+ this, packet->GetWritableData(), packet->GetBufferSize(),
+ AudioBuffersState(buffer_delay, hardware_delay));
CHECK(packet_size <= packet->GetBufferSize()) <<
"Data source overran buffer.";
@@ -715,6 +724,33 @@ std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) {
return guessed_device;
}
+snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() {
+ snd_pcm_sframes_t delay = -1;
+
+ // 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 = -1;
+ error = wrapper_->PcmRecover(playback_handle_,
+ error,
+ kPcmRecoverIsSilent);
+ if (error < 0) {
+ LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
+ }
+ }
+ }
+
+ // snd_pcm_delay() may not work in the beginning of the stream. In this case
+ // return delay of data we know currently is in the ALSA's buffer.
+ if (delay < 0)
+ delay = alsa_buffer_frames_ - GetAvailableFrames();
+
+ return delay;
+}
+
snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() {
DCHECK_EQ(message_loop_, MessageLoop::current());
@@ -879,13 +915,12 @@ void AlsaPcmOutputStream::SharedData::set_volume(float v) {
volume_ = v;
}
-uint32 AlsaPcmOutputStream::SharedData::OnMoreData(AudioOutputStream* stream,
- void* dest,
- uint32 max_size,
- uint32 pending_bytes) {
+uint32 AlsaPcmOutputStream::SharedData::OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
AutoLock l(lock_);
if (source_callback_) {
- return source_callback_->OnMoreData(stream, dest, max_size, pending_bytes);
+ return source_callback_->OnMoreData(stream, dest, max_size, buffers_state);
}
return 0;
diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h
index cbc50b3..bd1ecb9 100644
--- a/media/audio/linux/alsa_output.h
+++ b/media/audio/linux/alsa_output.h
@@ -143,6 +143,7 @@ class AlsaPcmOutputStream :
static uint32 FramesToMillis(uint32 frames, uint32 sample_rate);
std::string FindDeviceForChannels(uint32 channels);
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_|.
@@ -176,8 +177,8 @@ class AlsaPcmOutputStream :
// is passed into the output stream, but ownership is not transfered which
// requires a synchronization on access of the |source_callback_| to avoid
// using a deleted callback.
- uint32 OnMoreData(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes);
+ uint32 OnMoreData(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state);
void OnClose(AudioOutputStream* stream);
void OnError(AudioOutputStream* stream, int code);
diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc
index c741bd8..a37dadb 100644
--- a/media/audio/linux/alsa_output_unittest.cc
+++ b/media/audio/linux/alsa_output_unittest.cc
@@ -13,8 +13,11 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::AllOf;
+using testing::AtLeast;
using testing::DoAll;
using testing::Eq;
+using testing::Field;
using testing::InSequence;
using testing::Invoke;
using testing::InvokeWithoutArgs;
@@ -64,8 +67,9 @@ class MockAlsaWrapper : public AlsaWrapper {
class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
public:
- MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes));
+ MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream,
+ uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state));
MOCK_METHOD1(OnClose, void(AudioOutputStream* stream));
MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
};
@@ -438,8 +442,14 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
// 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))
+ OnMoreData(test_stream_.get(), _, kTestPacketSize, _))
.Times(2)
.WillOnce(Return(kTestPacketSize))
.WillOnce(Return(0));
@@ -448,12 +458,14 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
// Expect scheduling.
EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
- .Times(3)
- .WillOnce(Return(kTestFramesPerPacket)) // Buffer is empty.
+ .Times(AtLeast(5))
.WillOnce(Return(kTestFramesPerPacket)) // Buffer is empty.
- .WillOnce(DoAll(InvokeWithoutArgs(&message_loop_,
- &MessageLoop::QuitNow),
- Return(0))); // Buffer is full.
+ .WillOnce(Return(kTestFramesPerPacket))
+ .WillOnce(Return(kTestFramesPerPacket))
+ .WillOnce(Return(kTestFramesPerPacket))
+ .WillRepeatedly(DoAll(InvokeWithoutArgs(&message_loop_,
+ &MessageLoop::QuitNow),
+ Return(0))); // Buffer is full.
test_stream_->Start(&mock_callback);
message_loop_.RunAllPending();
@@ -542,10 +554,17 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
InitBuffer();
test_stream_->buffer_->Clear();
- // Return a partially filled packet.
MockAudioSourceCallback mock_callback;
+ EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
+ .WillOnce(Return(SND_PCM_STATE_RUNNING));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(1), Return(0)));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+ .WillRepeatedly(Return(0)); // Buffer is full.
+
+ // Return a partially filled packet.
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), _, _, 0))
+ OnMoreData(test_stream_.get(), _, _, _))
.WillOnce(Return(10));
bool source_exhausted;
@@ -563,8 +582,14 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) {
// Simulate where the underrun has occurred right after checking the delay.
MockAudioSourceCallback mock_callback;
+ EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
+ .WillOnce(Return(SND_PCM_STATE_RUNNING));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(-1), Return(0)));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+ .WillRepeatedly(Return(0)); // Buffer is full.
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), _, _, 0))
+ OnMoreData(test_stream_.get(), _, _, _))
.WillOnce(Return(10));
bool source_exhausted;
@@ -582,8 +607,14 @@ TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) {
// 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_alsa_wrapper_, PcmAvailUpdate(_))
+ .WillRepeatedly(Return(0)); // Buffer is full.
EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), _, _, 0))
+ OnMoreData(test_stream_.get(), _, _, AllOf(
+ Field(&AudioBuffersState::pending_bytes, 0),
+ Field(&AudioBuffersState::hardware_delay_bytes, 0))))
.WillOnce(Return(10));
bool source_exhausted;
diff --git a/media/audio/mac/audio_output_mac.cc b/media/audio/mac/audio_output_mac.cc
index 45de9a7..8d68b27 100644
--- a/media/audio/mac/audio_output_mac.cc
+++ b/media/audio/mac/audio_output_mac.cc
@@ -216,8 +216,10 @@ void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this,
if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer)
audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize;
uint32 capacity = buffer->mAudioDataBytesCapacity;
- uint32 filled = source->OnMoreData(audio_stream, buffer->mAudioData,
- capacity, audio_stream->pending_bytes_);
+ // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState.
+ uint32 filled = source->OnMoreData(
+ audio_stream, reinterpret_cast<uint8*>(buffer->mAudioData), capacity,
+ AudioBuffersState(audio_stream->pending_bytes_, 0));
// In order to keep the callback running, we need to provide a positive amount
// of data to the audio queue. To simulate the behavior of Windows, we write
diff --git a/media/audio/mac/audio_output_mac_unittest.cc b/media/audio/mac/audio_output_mac_unittest.cc
index 892dafe..061316f 100644
--- a/media/audio/mac/audio_output_mac_unittest.cc
+++ b/media/audio/mac/audio_output_mac_unittest.cc
@@ -12,6 +12,7 @@
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
+using ::testing::Field;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::NiceMock;
@@ -20,8 +21,9 @@ using ::testing::Return;
class MockAudioSource : public AudioOutputStream::AudioSourceCallback {
public:
- MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes));
+ MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size,
+ AudioBuffersState buffers_state));
MOCK_METHOD1(OnClose, void(AudioOutputStream* stream));
MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
};
@@ -36,8 +38,9 @@ TEST(MacAudioTest, SineWaveAudio16MonoTest) {
freq, AudioParameters::kTelephoneSampleRate);
// TODO(cpu): Put the real test when the mock renderer is ported.
- int16 buffer[samples] = { 0xffff };
- source.OnMoreData(NULL, buffer, sizeof(buffer), 0);
+ uint16 buffer[samples] = { 0xffff };
+ source.OnMoreData(NULL, reinterpret_cast<uint8*>(buffer), sizeof(buffer),
+ AudioBuffersState(0, 0));
EXPECT_EQ(0, buffer[0]);
EXPECT_EQ(5126, buffer[1]);
}
@@ -132,8 +135,8 @@ TEST(MacAudioTest, PCMWaveStreamPlay200HzTone22KssMono) {
}
// Custom action to clear a memory buffer.
-static void ClearBuffer(AudioOutputStream* strea, void* dest,
- uint32 max_size, uint32 pending_bytes) {
+static void ClearBuffer(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state) {
memset(dest, 0, max_size);
}
@@ -156,11 +159,16 @@ TEST(MacAudioTest, PCMWaveStreamPendingBytes) {
// And then we will try to provide zero data so the amount of pending bytes
// will go down and eventually read zero.
InSequence s;
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes, 0)))
.WillOnce(DoAll(Invoke(&ClearBuffer), Return(bytes_100_ms)));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ bytes_100_ms)))
.WillOnce(DoAll(Invoke(&ClearBuffer), Return(bytes_100_ms)));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ bytes_100_ms)))
.WillOnce(Return(0));
EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, _))
.Times(AnyNumber())
diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc
index 70d67b5..5445600 100644
--- a/media/audio/simple_sources.cc
+++ b/media/audio/simple_sources.cc
@@ -28,8 +28,9 @@ SineWaveAudioSource::SineWaveAudioSource(Format format, int channels,
// The implementation could be more efficient if a lookup table is constructed
// but it is efficient enough for our simple needs.
-uint32 SineWaveAudioSource::OnMoreData(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes) {
+uint32 SineWaveAudioSource::OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState audio_buffers) {
const double kTwoPi = 2.0 * 3.141592653589;
double f = freq_ / sample_freq_;
int16* sin_tbl = reinterpret_cast<int16*>(dest);
@@ -58,9 +59,10 @@ PushSource::PushSource()
PushSource::~PushSource() { }
-uint32 PushSource::OnMoreData(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes) {
- return buffer_.Read(static_cast<uint8*>(dest), max_size);
+uint32 PushSource::OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
+ return buffer_.Read(dest, max_size);
}
void PushSource::OnClose(AudioOutputStream* stream) {
diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h
index d94c2ed..4f4dc07 100644
--- a/media/audio/simple_sources.h
+++ b/media/audio/simple_sources.h
@@ -26,8 +26,9 @@ class SineWaveAudioSource : public AudioOutputStream::AudioSourceCallback {
virtual ~SineWaveAudioSource() {}
// Implementation of AudioSourceCallback.
- virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes);
+ virtual uint32 OnMoreData(
+ AudioOutputStream* stream, uint8* dest, uint32 max_size,
+ AudioBuffersState audio_buffers);
virtual void OnClose(AudioOutputStream* stream);
virtual void OnError(AudioOutputStream* stream, int code);
@@ -70,8 +71,8 @@ class PushSource : public AudioOutputStream::AudioSourceCallback,
virtual uint32 UnProcessedBytes();
// Implementation of AudioSourceCallback.
- virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes);
+ virtual uint32 OnMoreData(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state);
virtual void OnClose(AudioOutputStream* stream);
virtual void OnError(AudioOutputStream* stream, int code);
diff --git a/media/audio/simple_sources_unittest.cc b/media/audio/simple_sources_unittest.cc
index dd65e36..2737cd0 100644
--- a/media/audio/simple_sources_unittest.cc
+++ b/media/audio/simple_sources_unittest.cc
@@ -38,7 +38,7 @@ TEST(SimpleSourcesTest, PushSourceSmallerWrite) {
// Choose two prime numbers for read and write sizes.
const uint32 kWriteSize = 283;
const uint32 kReadSize = 293;
- scoped_array<char> read_data(new char[kReadSize]);
+ scoped_array<uint8> read_data(new uint8[kReadSize]);
// Create a PushSource.
PushSource push_source;
@@ -54,7 +54,8 @@ TEST(SimpleSourcesTest, PushSourceSmallerWrite) {
// Read everything from the push source.
for (uint32 i = 0; i < kDataSize; i += kReadSize) {
uint32 size = std::min(kDataSize - i , kReadSize);
- EXPECT_EQ(size, push_source.OnMoreData(NULL, read_data.get(), size, 0));
+ EXPECT_EQ(size, push_source.OnMoreData(NULL, read_data.get(), size,
+ AudioBuffersState()));
EXPECT_EQ(0, memcmp(data.get() + i, read_data.get(), size));
}
EXPECT_EQ(0u, push_source.UnProcessedBytes());
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc
index 01cddf1..b6d3bf3 100644
--- a/media/audio/win/audio_output_win_unittest.cc
+++ b/media/audio/win/audio_output_win_unittest.cc
@@ -19,6 +19,7 @@
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
+using ::testing::Field;
using ::testing::InSequence;
using ::testing::NiceMock;
using ::testing::NotNull;
@@ -39,8 +40,8 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback {
was_closed_(0) {
}
// AudioSourceCallback::OnMoreData implementation:
- virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes) {
+ virtual uint32 OnMoreData(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size, AudioBuffersState buffers_state) {
++callback_count_;
// Touch the first byte to make sure memory is good.
if (max_size)
@@ -96,9 +97,10 @@ class TestSourceTripleBuffer : public TestSourceBasic {
}
// Override of TestSourceBasic::OnMoreData.
virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes) {
+ uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
// Call the base, which increments the callback_count_.
- TestSourceBasic::OnMoreData(stream, dest, max_size, 0);
+ TestSourceBasic::OnMoreData(stream, dest, max_size, buffers_state);
if (callback_count() % kNumBuffers == 2) {
set_error(!CompareExistingIfNotNULL(2, dest));
} else if (callback_count() % kNumBuffers == 1) {
@@ -132,9 +134,10 @@ class TestSourceLaggy : public TestSourceBasic {
: laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) {
}
virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes) {
+ uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
// Call the base, which increments the callback_count_.
- TestSourceBasic::OnMoreData(stream, dest, max_size, 0);
+ TestSourceBasic::OnMoreData(stream, dest, max_size, buffers_state);
if (callback_count() > kNumBuffers) {
::Sleep(lag_in_ms_);
}
@@ -147,8 +150,9 @@ class TestSourceLaggy : public TestSourceBasic {
class MockAudioSource : public AudioOutputStream::AudioSourceCallback {
public:
- MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, void* dest,
- uint32 max_size, uint32 pending_bytes));
+ MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, uint8* dest,
+ uint32 max_size,
+ AudioBuffersState buffers_state));
MOCK_METHOD1(OnClose, void(AudioOutputStream* stream));
MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
};
@@ -583,19 +587,27 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
// And then we will try to provide zero data so the amount of pending bytes
// will go down and eventually read zero.
InSequence s;
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes, 0)))
.WillOnce(Return(bytes_100_ms));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ bytes_100_ms)))
.WillOnce(Return(bytes_100_ms));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(),
- bytes_100_ms, 2 * bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ 2 * bytes_100_ms)))
.WillOnce(Return(bytes_100_ms));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(),
- bytes_100_ms, 2 * bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ 2 * bytes_100_ms)))
.WillOnce(Return(0));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes,
+ bytes_100_ms)))
.WillOnce(Return(0));
- EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0))
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms,
+ Field(&AudioBuffersState::pending_bytes, 0)))
.Times(AnyNumber())
.WillRepeatedly(Return(0));
@@ -619,8 +631,9 @@ class SyncSocketSource : public AudioOutputStream::AudioSourceCallback {
// AudioSourceCallback::OnMoreData implementation:
virtual uint32 OnMoreData(AudioOutputStream* stream,
- void* dest, uint32 max_size, uint32 pending_bytes) {
- socket_->Send(&pending_bytes, sizeof(pending_bytes));
+ uint8* dest, uint32 max_size,
+ AudioBuffersState buffers_state) {
+ socket_->Send(&buffers_state, sizeof(buffers_state));
uint32 got = socket_->Receive(dest, max_size);
return got;
}
@@ -653,17 +666,17 @@ DWORD __stdcall SyncSocketThread(void* context) {
const int kTwoSecBytes =
AudioParameters::kAudioCDSampleRate * 2 * sizeof(uint16);
- char* buffer = new char[kTwoSecBytes];
+ uint8* buffer = new uint8[kTwoSecBytes];
SineWaveAudioSource sine(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM,
1, ctx.sine_freq, ctx.sample_rate);
- sine.OnMoreData(NULL, buffer, kTwoSecBytes, 0);
+ sine.OnMoreData(NULL, buffer, kTwoSecBytes, AudioBuffersState());
- int pending_bytes = -1;
+ AudioBuffersState buffers_state;
int times = 0;
for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size) {
- if (ctx.socket->Receive(&pending_bytes, sizeof(pending_bytes)) == 0)
+ if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0)
break;
- if ((times > 0) && (pending_bytes < 1000)) __debugbreak();
+ if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak();
ctx.socket->Send(&buffer[ix], ctx.packet_size);
++times;
}
diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc
index 819f1b7..03da37d 100644
--- a/media/audio/win/waveout_output_win.cc
+++ b/media/audio/win/waveout_output_win.cc
@@ -236,8 +236,10 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) {
// scale up the amount of pending bytes.
// TODO(fbarchard): Handle used 0 by queueing more.
uint32 scaled_pending_bytes = pending_bytes_ * channels_ / format_.nChannels;
- uint32 used = callback_->OnMoreData(this, buffer->lpData, buffer_size_,
- scaled_pending_bytes);
+ // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState.
+ uint32 used = callback_->OnMoreData(
+ this, reinterpret_cast<uint8*>(buffer->lpData), buffer_size_,
+ AudioBuffersState(scaled_pending_bytes, 0));
if (used <= buffer_size_) {
buffer->dwBufferLength = used * format_.nChannels / channels_;
if (channels_ > 2 && format_.nChannels == 2) {