diff options
-rw-r--r-- | content/browser/renderer_host/media/audio_sync_reader.cc | 32 | ||||
-rw-r--r-- | content/browser/renderer_host/media/audio_sync_reader.h | 11 | ||||
-rw-r--r-- | media/audio/audio_io.h | 4 | ||||
-rw-r--r-- | media/audio/audio_low_latency_input_output_unittest.cc | 1 | ||||
-rw-r--r-- | media/audio/audio_output_controller.cc | 91 | ||||
-rw-r--r-- | media/audio/audio_output_controller.h | 47 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 12 | ||||
-rw-r--r-- | media/audio/audio_output_resampler.cc | 7 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.cc | 4 | ||||
-rw-r--r-- | media/audio/pulse/pulse_input.cc | 3 | ||||
-rw-r--r-- | media/audio/pulse/pulse_output.cc | 1 | ||||
-rw-r--r-- | media/audio/pulse/pulse_unified.cc | 1 | ||||
-rw-r--r-- | media/audio/virtual_audio_output_stream.cc | 4 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 11 |
14 files changed, 73 insertions, 156 deletions
diff --git a/content/browser/renderer_host/media/audio_sync_reader.cc b/content/browser/renderer_host/media/audio_sync_reader.cc index 9ebf5ed..39fc80d1 100644 --- a/content/browser/renderer_host/media/audio_sync_reader.cc +++ b/content/browser/renderer_host/media/audio_sync_reader.cc @@ -68,11 +68,15 @@ void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { } } -int AudioSyncReader::Read(AudioBus* source, AudioBus* dest) { +int AudioSyncReader::Read(bool block, const AudioBus* source, AudioBus* dest) { ++renderer_callback_count_; - if (!DataReady()) + if (!DataReady()) { ++renderer_missed_callback_count_; + if (block) + WaitTillDataReady(); + } + // Copy optional synchronized live audio input for consumption by renderer // process. if (source && input_bus_) { @@ -162,4 +166,28 @@ bool AudioSyncReader::PrepareForeignSocketHandle( } #endif +void AudioSyncReader::WaitTillDataReady() { + base::TimeTicks start = base::TimeTicks::Now(); + const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20); +#if defined(OS_WIN) + // Sleep(0) on Windows lets the other threads run. + const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0); +#else + // We want to sleep for a bit here, as otherwise a backgrounded renderer won't + // get enough cpu to send the data and the high priority thread in the browser + // will use up a core causing even more skips. + const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2); +#endif + base::TimeDelta time_since_start; + do { + base::PlatformThread::Sleep(kSleep); + time_since_start = base::TimeTicks::Now() - start; + } while (!DataReady() && time_since_start < kMaxWait); + UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", + time_since_start, + base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(1000), + 50); +} + } // namespace content diff --git a/content/browser/renderer_host/media/audio_sync_reader.h b/content/browser/renderer_host/media/audio_sync_reader.h index 1f07b20..80f7b62 100644 --- a/content/browser/renderer_host/media/audio_sync_reader.h +++ b/content/browser/renderer_host/media/audio_sync_reader.h @@ -33,9 +33,10 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader { // media::AudioOutputController::SyncReader implementations. virtual void UpdatePendingBytes(uint32 bytes) OVERRIDE; - virtual int Read(media::AudioBus* source, media::AudioBus* dest) OVERRIDE; + virtual int Read(bool block, + const media::AudioBus* source, + media::AudioBus* dest) OVERRIDE; virtual void Close() OVERRIDE; - virtual bool DataReady() OVERRIDE; bool Init(); bool PrepareForeignSocketHandle(base::ProcessHandle process_handle, @@ -46,6 +47,12 @@ class AudioSyncReader : public media::AudioOutputController::SyncReader { #endif private: + // Indicates whether the renderer has data available for reading. + bool DataReady(); + + // Blocks until DataReady() is true or a timeout expires. + void WaitTillDataReady(); + base::SharedMemory* shared_memory_; // Number of input channels for synchronized I/O. diff --git a/media/audio/audio_io.h b/media/audio/audio_io.h index 0f3f673..473af0d 100644 --- a/media/audio/audio_io.h +++ b/media/audio/audio_io.h @@ -73,10 +73,6 @@ class MEDIA_EXPORT AudioOutputStream { // playback will not continue. virtual void OnError(AudioOutputStream* stream) = 0; - // Will block until the client has written its audio data or 1.5 seconds - // have elapsed. - virtual void WaitTillDataReady() {} - protected: virtual ~AudioSourceCallback() {} }; diff --git a/media/audio/audio_low_latency_input_output_unittest.cc b/media/audio/audio_low_latency_input_output_unittest.cc index 4dd22ab..f4aeab4 100644 --- a/media/audio/audio_low_latency_input_output_unittest.cc +++ b/media/audio/audio_low_latency_input_output_unittest.cc @@ -258,7 +258,6 @@ class FullDuplexAudioSinkSource } virtual void OnError(AudioOutputStream* stream) OVERRIDE {} - virtual void WaitTillDataReady() OVERRIDE {} protected: // Converts from bytes to milliseconds taking the sample rate and size diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc index 8e33c7f..aea6440 100644 --- a/media/audio/audio_output_controller.cc +++ b/media/audio/audio_output_controller.cc @@ -52,8 +52,7 @@ AudioOutputController::AudioOutputController(AudioManager* audio_manager, num_allowed_io_(0), sync_reader_(sync_reader), message_loop_(audio_manager->GetMessageLoop()), - number_polling_attempts_left_(0), - weak_this_(this) { + number_polling_attempts_left_(0) { DCHECK(audio_manager); DCHECK(handler_); DCHECK(sync_reader_); @@ -149,52 +148,16 @@ void AudioOutputController::DoCreate(bool is_for_device_change) { void AudioOutputController::DoPlay() { DCHECK(message_loop_->BelongsToCurrentThread()); + SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); // We can start from created or paused state. if (state_ != kCreated && state_ != kPaused) return; - state_ = kStarting; - // Ask for first packet. sync_reader_->UpdatePendingBytes(0); - // Cannot start stream immediately, should give renderer some time - // to deliver data. - // TODO(vrk): The polling here and in WaitTillDataReady() is pretty clunky. - // Refine the API such that polling is no longer needed. (crbug.com/112196) - number_polling_attempts_left_ = kPollNumAttempts; - DCHECK(!weak_this_.HasWeakPtrs()); - message_loop_->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioOutputController::PollAndStartIfDataReady, - weak_this_.GetWeakPtr()), - TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); -} - -void AudioOutputController::PollAndStartIfDataReady() { - DCHECK(message_loop_->BelongsToCurrentThread()); - SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); - - DCHECK_EQ(kStarting, state_); - - // If we are ready to start the stream, start it. - if (--number_polling_attempts_left_ == 0 || - sync_reader_->DataReady()) { - StartStream(); - } else { - message_loop_->PostDelayedTask( - FROM_HERE, - base::Bind(&AudioOutputController::PollAndStartIfDataReady, - weak_this_.GetWeakPtr()), - TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); - } -} - -void AudioOutputController::StartStream() { - DCHECK(message_loop_->BelongsToCurrentThread()); state_ = kPlaying; - silence_detector_.reset(new AudioSilenceDetector( params_.sample_rate(), TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis), @@ -214,11 +177,7 @@ void AudioOutputController::StartStream() { void AudioOutputController::StopStream() { DCHECK(message_loop_->BelongsToCurrentThread()); - if (state_ == kStarting) { - // Cancel in-progress polling start. - weak_this_.InvalidateWeakPtrs(); - state_ = kPaused; - } else if (state_ == kPlaying) { + if (state_ == kPlaying) { stream_->Stop(); DisallowEntryToOnMoreIOData(); silence_detector_->Stop(true); @@ -262,7 +221,6 @@ void AudioOutputController::DoSetVolume(double volume) { switch (state_) { case kCreated: - case kStarting: case kPlaying: case kPaused: stream_->SetVolume(volume_); @@ -291,15 +249,23 @@ int AudioOutputController::OnMoreIOData(AudioBus* source, // The OS level audio APIs on Linux and Windows all have problems requesting // data on a fixed interval. Sometimes they will issue calls back to back - // which can cause glitching, so wait until the renderer is ready for Read(). + // which can cause glitching, so wait until the renderer is ready. + // + // We also need to wait when diverting since the virtual stream will call this + // multiple times without waiting. + // + // NEVER wait on OSX unless a virtual stream is connected, otherwise we can + // end up hanging the entire OS. // // See many bugs for context behind this decision: http://crbug.com/170498, // http://crbug.com/171651, http://crbug.com/174985, and more. #if defined(OS_WIN) || defined(OS_LINUX) - WaitTillDataReady(); + const bool kShouldBlock = true; +#else + const bool kShouldBlock = diverting_to_stream_ != NULL; #endif - const int frames = sync_reader_->Read(source, dest); + const int frames = sync_reader_->Read(kShouldBlock, source, dest); DCHECK_LE(0, frames); sync_reader_->UpdatePendingBytes( buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); @@ -310,34 +276,6 @@ int AudioOutputController::OnMoreIOData(AudioBus* source, return frames; } -void AudioOutputController::WaitTillDataReady() { - // Most of the time the data is ready already. - if (sync_reader_->DataReady()) - return; - - base::TimeTicks start = base::TimeTicks::Now(); - const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20); -#if defined(OS_WIN) - // Sleep(0) on windows lets the other threads run. - const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0); -#else - // We want to sleep for a bit here, as otherwise a backgrounded renderer won't - // get enough cpu to send the data and the high priority thread in the browser - // will use up a core causing even more skips. - const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2); -#endif - base::TimeDelta time_since_start; - do { - base::PlatformThread::Sleep(kSleep); - time_since_start = base::TimeTicks::Now() - start; - } while (!sync_reader_->DataReady() && (time_since_start < kMaxWait)); - UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", - time_since_start, - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromMilliseconds(1000), - 50); -} - void AudioOutputController::OnError(AudioOutputStream* stream) { // Handle error on the audio controller thread. message_loop_->PostTask(FROM_HERE, base::Bind( @@ -381,7 +319,6 @@ void AudioOutputController::OnDeviceChange() { // Get us back to the original state or an equivalent state. switch (original_state) { - case kStarting: case kPlaying: DoPlay(); return; diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h index 1e56633..93acc27 100644 --- a/media/audio/audio_output_controller.h +++ b/media/audio/audio_output_controller.h @@ -9,9 +9,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" #include "base/timer.h" -#include "media/audio/audio_buffers_state.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_source_diverter.h" @@ -28,14 +26,14 @@ // // Here is a state transition diagram for the AudioOutputController: // -// *[ Empty ] --> [ Created ] --> [ Starting ] --> [ Playing ] --. -// | | | ^ | | -// | | | | | | -// | | | | v | -// | | | `--------- [ Paused ] | -// | | | | | -// | v v | | -// `-----------> [ Closed ] <-------------------------' +// *[ Empty ] --> [ Created ] --> [ Playing ] -------. +// | | | ^ | +// | | | | | +// | | | | v +// | | | `----- [ Paused ] +// | | | | +// | v v | +// `-----------> [ Closed ] <-----------' // // * Initial state // @@ -51,9 +49,6 @@ // AudioSourceCallback interface. AudioOutputController uses the SyncReader // passed to it via construction to synchronously fulfill this read request. // -// Since AudioOutputController uses AudioManager's message loop the controller -// uses WeakPtr to allow safe cancellation of pending tasks. -// namespace media { @@ -93,16 +88,14 @@ class MEDIA_EXPORT AudioOutputController // prepare more data and perform synchronization. virtual void UpdatePendingBytes(uint32 bytes) = 0; - // Attempt to completely fill |dest|, return the actual number of - // frames that could be read. - // |source| may optionally be provided for input data. - virtual int Read(AudioBus* source, AudioBus* dest) = 0; + // Attempt to completely fill |dest|, return the actual number of frames + // that could be read. |source| may optionally be provided for input data. + // If |block| is specified, the Read() will block until data is available + // or a timeout is reached. + virtual int Read(bool block, const AudioBus* source, AudioBus* dest) = 0; // Close this synchronous reader. virtual void Close() = 0; - - // Check if data is ready. - virtual bool DataReady() = 0; }; // Factory method for creating an AudioOutputController. @@ -142,9 +135,6 @@ class MEDIA_EXPORT AudioOutputController AudioBus* dest, AudioBuffersState buffers_state) OVERRIDE; virtual void OnError(AudioOutputStream* stream) OVERRIDE; - // Deprecated: Currently only used for starting audio playback and for audio - // mirroring. - virtual void WaitTillDataReady() OVERRIDE; // AudioDeviceListener implementation. When called AudioOutputController will // shutdown the existing |stream_|, transition to the kRecreating state, @@ -162,7 +152,6 @@ class MEDIA_EXPORT AudioOutputController enum State { kEmpty, kCreated, - kStarting, kPlaying, kPaused, kClosed, @@ -185,7 +174,6 @@ class MEDIA_EXPORT AudioOutputController // The following methods are executed on the audio manager thread. void DoCreate(bool is_for_device_change); void DoPlay(); - void PollAndStartIfDataReady(); void DoPause(); void DoClose(); void DoSetVolume(double volume); @@ -197,8 +185,7 @@ class MEDIA_EXPORT AudioOutputController // silence and call EventHandler::OnAudible() when state changes occur. void MaybeInvokeAudibleCallback(); - // Helper methods that start/stop physical stream. - void StartStream(); + // Helper method that stops the physical stream. void StopStream(); // Helper method that stops, closes, and NULLs |*stream_|. @@ -216,8 +203,6 @@ class MEDIA_EXPORT AudioOutputController // Used by the unified IO to open the correct input device. std::string input_device_id_; - // Note: It's important to invalidate the weak pointers whenever stream_ is - // changed. See comment for weak_this_. AudioOutputStream* stream_; // When non-NULL, audio is being diverted to this stream. @@ -248,10 +233,6 @@ class MEDIA_EXPORT AudioOutputController // Number of times left. int number_polling_attempts_left_; - // Used to auto-cancel the delayed tasks that are created to poll for data - // (when starting-up a stream). - base::WeakPtrFactory<AudioOutputController> weak_this_; - // Scans audio samples from OnMoreIOData() as input and causes // EventHandler::OnAudbile() to be called whenever a transition to a period of // silence or non-silence is detected. diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc index c2630b0..ab4f652 100644 --- a/media/audio/audio_output_controller_unittest.cc +++ b/media/audio/audio_output_controller_unittest.cc @@ -55,9 +55,8 @@ class MockAudioOutputControllerSyncReader MockAudioOutputControllerSyncReader() {} MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes)); - MOCK_METHOD2(Read, int(AudioBus* source, AudioBus* dest)); + MOCK_METHOD3(Read, int(bool block, const AudioBus* source, AudioBus* dest)); MOCK_METHOD0(Close, void()); - MOCK_METHOD0(DataReady, bool()); private: DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerSyncReader); @@ -86,10 +85,10 @@ ACTION_P(SignalEvent, event) { static const float kBufferNonZeroData = 1.0f; ACTION(PopulateBuffer) { - arg1->Zero(); + arg2->Zero(); // Note: To confirm the buffer will be populated in these tests, it's // sufficient that only the first float in channel 0 is set to the value. - arg1->channel(0)[0] = kBufferNonZeroData; + arg2->channel(0)[0] = kBufferNonZeroData; } class AudioOutputControllerTest : public testing::Test { @@ -142,13 +141,10 @@ class AudioOutputControllerTest : public testing::Test { // sent from the render process. EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_)) .Times(AtLeast(1)); - EXPECT_CALL(mock_sync_reader_, Read(_, _)) + EXPECT_CALL(mock_sync_reader_, Read(_, _, _)) .WillRepeatedly(DoAll(PopulateBuffer(), SignalEvent(&read_event_), Return(params_.frames_per_buffer()))); - EXPECT_CALL(mock_sync_reader_, DataReady()) - .WillRepeatedly(Return(true)); - controller_->Play(); } diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc index ea1848e..ad12d27 100644 --- a/media/audio/audio_output_resampler.cc +++ b/media/audio/audio_output_resampler.cc @@ -36,7 +36,6 @@ class OnMoreDataConverter AudioBus* dest, AudioBuffersState buffers_state) OVERRIDE; virtual void OnError(AudioOutputStream* stream) OVERRIDE; - virtual void WaitTillDataReady() OVERRIDE; // Sets |source_callback_|. If this is not a new object, then Stop() must be // called before Start(). @@ -393,10 +392,4 @@ void OnMoreDataConverter::OnError(AudioOutputStream* stream) { source_callback_->OnError(stream); } -void OnMoreDataConverter::WaitTillDataReady() { - base::AutoLock auto_lock(source_lock_); - if (source_callback_) - source_callback_->WaitTillDataReady(); -} - } // namespace media diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc index 3c2520e..9058555 100644 --- a/media/audio/linux/alsa_output.cc +++ b/media/audio/linux/alsa_output.cc @@ -744,10 +744,8 @@ int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, AudioBuffersState buffers_state) { TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); - if (source_callback_) { - source_callback_->WaitTillDataReady(); + if (source_callback_) return source_callback_->OnMoreData(audio_bus, buffers_state); - } return 0; } diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc index c365c47..cc62085 100644 --- a/media/audio/pulse/pulse_input.cc +++ b/media/audio/pulse/pulse_input.cc @@ -278,8 +278,7 @@ void PulseAudioInputStream::ReadData() { if (buffer_->forward_bytes() < packet_size) break; - // TODO(xians): improve the code by implementing a WaitTillDataReady on the - // input side. + // TODO(xians): Remove once PPAPI is using circular buffers. DVLOG(1) << "OnData is being called consecutively, sleep 5ms to " << "wait until render consumes the data"; base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5)); diff --git a/media/audio/pulse/pulse_output.cc b/media/audio/pulse/pulse_output.cc index 1734954..99ede3b 100644 --- a/media/audio/pulse/pulse_output.cc +++ b/media/audio/pulse/pulse_output.cc @@ -129,7 +129,6 @@ void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) { uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( pa_stream_, params_.sample_rate(), params_.GetBytesPerFrame()); - source_callback_->WaitTillDataReady(); frames_filled = source_callback_->OnMoreData( audio_bus_.get(), AudioBuffersState(0, hardware_delay)); } diff --git a/media/audio/pulse/pulse_unified.cc b/media/audio/pulse/pulse_unified.cc index ee14341..cfa55a2 100644 --- a/media/audio/pulse/pulse_unified.cc +++ b/media/audio/pulse/pulse_unified.cc @@ -157,7 +157,6 @@ void PulseAudioUnifiedStream::WriteData(size_t requested_bytes) { uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( output_stream_, params_.sample_rate(), params_.GetBytesPerFrame()); - source_callback_->WaitTillDataReady(); fifo_->Read(input_data_buffer_.get(), requested_bytes); input_bus_->FromInterleaved( input_data_buffer_.get(), params_.frames_per_buffer(), 2); diff --git a/media/audio/virtual_audio_output_stream.cc b/media/audio/virtual_audio_output_stream.cc index 005fbc2..254a70a 100644 --- a/media/audio/virtual_audio_output_stream.cc +++ b/media/audio/virtual_audio_output_stream.cc @@ -74,10 +74,6 @@ double VirtualAudioOutputStream::ProvideInput(AudioBus* audio_bus, DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(callback_); - // A VirtualAudioInputStream with a larger buffer size may be calling this - // multiple times without waiting, so we need to block and ensure the data is - // ready to prevent glitching. - callback_->WaitTillDataReady(); const int frames = callback_->OnMoreData(audio_bus, AudioBuffersState()); if (frames < audio_bus->frames()) audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 9b03c31..47d4fa6 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -204,10 +204,6 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { pending_bytes_ = 0; for (int ix = 0; ix != num_buffers_; ++ix) { WAVEHDR* buffer = GetBuffer(ix); - // Caller waits for 1st packet to become available, but not for others, - // so we wait for them here. - if (ix != 0) - callback_->WaitTillDataReady(); QueueNextPacket(buffer); // Read more data. pending_bytes_ += buffer->dwBufferLength; } @@ -343,13 +339,6 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { // return to us how many bytes were used. // TODO(fbarchard): Handle used 0 by queueing more. - // HACK: Yield if Read() is called too often. On older platforms which are - // still using the WaveOut backend, we run into synchronization issues where - // the renderer has not finished filling the shared memory when Read() is - // called. Reading too early will lead to clicks and pops. See issues: - // http://crbug.com/161307 and http://crbug.com/61022 - callback_->WaitTillDataReady(); - // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. int frames_filled = callback_->OnMoreData( audio_bus_.get(), AudioBuffersState(pending_bytes_, 0)); |