diff options
Diffstat (limited to 'media/audio')
-rw-r--r-- | media/audio/audio_buffers_state.cc | 22 | ||||
-rw-r--r-- | media/audio/audio_buffers_state.h | 33 | ||||
-rw-r--r-- | media/audio/audio_io.h | 6 | ||||
-rw-r--r-- | media/audio/audio_output_controller.cc | 45 | ||||
-rw-r--r-- | media/audio/audio_output_controller.h | 19 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 88 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.cc | 5 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.h | 4 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.cc | 55 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.h | 5 | ||||
-rw-r--r-- | media/audio/linux/alsa_output_unittest.cc | 55 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.cc | 6 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac_unittest.cc | 26 | ||||
-rw-r--r-- | media/audio/simple_sources.cc | 12 | ||||
-rw-r--r-- | media/audio/simple_sources.h | 9 | ||||
-rw-r--r-- | media/audio/simple_sources_unittest.cc | 5 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 59 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 6 |
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) { |