diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-29 01:21:27 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-29 01:21:27 +0000 |
commit | 5c86ecf8936269cdc5f00f57b7531cdac7b3e706 (patch) | |
tree | 79392d0c2e959af863e2b6d4626a79d039b178ab /media | |
parent | e10eacda58687b9363919a9203181322b649946c (diff) | |
download | chromium_src-5c86ecf8936269cdc5f00f57b7531cdac7b3e706.zip chromium_src-5c86ecf8936269cdc5f00f57b7531cdac7b3e706.tar.gz chromium_src-5c86ecf8936269cdc5f00f57b7531cdac7b3e706.tar.bz2 |
Add AudioBuffersState struct. Use it for audio synchronization.
The new AudioBuffersState contains current state of the audio buffers. This
object is passed all the way from the device to the audio renderer. Audio
renderer uses this information to synchronize audio.
BUG=52196,49110
TEST=see repro steps for 51637 and 52196.
Review URL: http://codereview.chromium.org/3444017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60891 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
26 files changed, 351 insertions, 171 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) { diff --git a/media/base/seekable_buffer.cc b/media/base/seekable_buffer.cc index f076ee8..bb61b78 100644 --- a/media/base/seekable_buffer.cc +++ b/media/base/seekable_buffer.cc @@ -89,10 +89,15 @@ bool SeekableBuffer::Append(Buffer* buffer_in) { } bool SeekableBuffer::Append(const uint8* data, size_t size) { - DataBuffer* data_buffer = new DataBuffer(size); - memcpy(data_buffer->GetWritableData(), data, size); - data_buffer->SetDataSize(size); - return Append(data_buffer); + if (size > 0) { + DataBuffer* data_buffer = new DataBuffer(size); + memcpy(data_buffer->GetWritableData(), data, size); + data_buffer->SetDataSize(size); + return Append(data_buffer); + } else { + // Return true if we have forward capacity. + return forward_bytes_ < forward_capacity_; + } } bool SeekableBuffer::Seek(int32 offset) { diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc index ca0ee87..2f889b9 100644 --- a/media/filters/audio_renderer_base.cc +++ b/media/filters/audio_renderer_base.cc @@ -182,7 +182,8 @@ void AudioRendererBase::ConsumeAudioSamples(scoped_refptr<Buffer> buffer_in) { uint32 AudioRendererBase::FillBuffer(uint8* dest, uint32 dest_len, - const base::TimeDelta& playback_delay) { + const base::TimeDelta& playback_delay, + bool buffers_empty) { // The timestamp of the last buffer written during the last call to // FillBuffer(). base::TimeDelta last_fill_buffer_time; @@ -209,7 +210,7 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest, // Use two conditions to determine the end of playback: // 1. Algorithm has no audio data. // 2. Browser process has no audio data. - if (algorithm_->IsQueueEmpty() && !playback_delay.ToInternalValue()) { + if (algorithm_->IsQueueEmpty() && buffers_empty) { if (recieved_end_of_stream_ && !rendered_end_of_stream_) { rendered_end_of_stream_ = true; host()->NotifyEnded(); @@ -232,8 +233,7 @@ uint32 AudioRendererBase::FillBuffer(uint8* dest, // end since we use decoded packets to trigger time updates. A better // solution is to start a timer when an audio packet is decoded to allow // finer time update events. - if (playback_delay < last_fill_buffer_time) - last_fill_buffer_time -= playback_delay; + last_fill_buffer_time -= playback_delay; host()->SetTime(last_fill_buffer_time); } diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h index 783a8ba..36a90f0 100644 --- a/media/filters/audio_renderer_base.h +++ b/media/filters/audio_renderer_base.h @@ -78,7 +78,8 @@ class AudioRendererBase : public AudioRenderer { // Safe to call on any thread. uint32 FillBuffer(uint8* dest, uint32 len, - const base::TimeDelta& playback_delay); + const base::TimeDelta& playback_delay, + bool buffers_empty); // Helper to parse a media format and return whether we were successful // retrieving all the information we care about. diff --git a/media/filters/audio_renderer_base_unittest.cc b/media/filters/audio_renderer_base_unittest.cc index 3c25b77..03de168 100644 --- a/media/filters/audio_renderer_base_unittest.cc +++ b/media/filters/audio_renderer_base_unittest.cc @@ -210,7 +210,8 @@ TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) { uint8 buffer[kDataSize]; for (size_t i = 0; i < kMaxQueueSize; ++i) { EXPECT_EQ(kDataSize, - renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta())); + renderer_->FillBuffer(buffer, kDataSize, + base::TimeDelta(), true)); bytes_buffered -= kDataSize; } @@ -234,7 +235,8 @@ TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) { EXPECT_EQ(0u, bytes_buffered % kDataSize); while (bytes_buffered > 0) { EXPECT_EQ(kDataSize, - renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta())); + renderer_->FillBuffer(buffer, kDataSize, + base::TimeDelta(), true)); bytes_buffered -= kDataSize; } @@ -246,13 +248,15 @@ TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) { // Do an additional read to trigger NotifyEnded(). EXPECT_CALL(host_, NotifyEnded()); - EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta())); + EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, + base::TimeDelta(), true)); // We should now report ended. EXPECT_TRUE(renderer_->HasEnded()); // Further reads should return muted audio and not notify any more. - EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, base::TimeDelta())); + EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize, + base::TimeDelta(), true)); } } // namespace media diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc index 1a2a182..69188b3 100644 --- a/media/filters/audio_renderer_impl.cc +++ b/media/filters/audio_renderer_impl.cc @@ -52,19 +52,22 @@ void AudioRendererImpl::SetVolume(float volume) { stream_->SetVolume(volume); } -uint32 AudioRendererImpl::OnMoreData(AudioOutputStream* stream, void* dest_void, - uint32 len, uint32 pending_bytes) { +uint32 AudioRendererImpl::OnMoreData( + AudioOutputStream* stream, uint8* dest, uint32 len, + AudioBuffersState buffers_state) { // TODO(scherkus): handle end of stream. if (!stream_) return 0; - // TODO(scherkus): Maybe change OnMoreData to pass in char/uint8 or similar. // TODO(fbarchard): Waveout_output_win.h should handle zero length buffers // without clicking. - pending_bytes = static_cast<uint32>(ceil(pending_bytes * GetPlaybackRate())); - base::TimeDelta delay = base::TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond * pending_bytes / bytes_per_second_); - return FillBuffer(static_cast<uint8*>(dest_void), len, delay); + uint32 pending_bytes = static_cast<uint32>(ceil(buffers_state.total_bytes() * + GetPlaybackRate())); + base::TimeDelta delay = base::TimeDelta::FromMicroseconds( + base::Time::kMicrosecondsPerSecond * pending_bytes / + bytes_per_second_); + bool buffers_empty = buffers_state.pending_bytes == 0; + return FillBuffer(dest, len, delay, buffers_empty); } void AudioRendererImpl::OnClose(AudioOutputStream* stream) { diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h index 7bbb265e..fc61f80 100644 --- a/media/filters/audio_renderer_impl.h +++ b/media/filters/audio_renderer_impl.h @@ -43,8 +43,8 @@ class AudioRendererImpl : public AudioRendererBase, virtual void SetVolume(float volume); // AudioSourceCallback implementation. - virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest, - uint32 len, uint32 pending_bytes); + virtual uint32 OnMoreData(AudioOutputStream* stream, uint8* dest, + uint32 len, AudioBuffersState buffers_state); virtual void OnClose(AudioOutputStream* stream); virtual void OnError(AudioOutputStream* stream, int code); diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc index 62cbeee..55c4013 100644 --- a/media/filters/null_audio_renderer.cc +++ b/media/filters/null_audio_renderer.cc @@ -48,7 +48,8 @@ void NullAudioRenderer::ThreadMain() { if (GetPlaybackRate() > 0.0f) { size_t bytes = FillBuffer(buffer_.get(), buffer_size_, - base::TimeDelta()); + base::TimeDelta(), + true); // Calculate our sleep duration, taking playback rate into consideration. sleep_in_milliseconds = diff --git a/media/media.gyp b/media/media.gyp index 618bfcb..ee8acbc 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -20,6 +20,8 @@ ], 'msvs_guid': '6AE76406-B03B-11DD-94B1-80B556D89593', 'sources': [ + 'audio/audio_buffers_state.cc', + 'audio/audio_buffers_state.h', 'audio/audio_io.h', 'audio/audio_input_controller.cc', 'audio/audio_input_controller.h', |