| // Copyright (c) 2011 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_output_dispatcher.h" |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/message_loop.h" |
| #include "base/time.h" |
| #include "media/audio/audio_io.h" |
| |
| AudioOutputDispatcher::AudioOutputDispatcher( |
| AudioManager* audio_manager, const AudioParameters& params, |
| int close_delay_ms) |
| : audio_manager_(audio_manager), |
| message_loop_(audio_manager->GetMessageLoop()), |
| params_(params), |
| pause_delay_milliseconds_(2 * params.samples_per_packet * |
| base::Time::kMillisecondsPerSecond / params.sample_rate), |
| paused_proxies_(0), |
| ALLOW_THIS_IN_INITIALIZER_LIST(close_timer_(FROM_HERE, |
| base::TimeDelta::FromMilliseconds(close_delay_ms), |
| this, &AudioOutputDispatcher::ClosePendingStreams)) { |
| } |
| |
| AudioOutputDispatcher::~AudioOutputDispatcher() { |
| } |
| |
| bool AudioOutputDispatcher::StreamOpened() { |
| DCHECK_EQ(MessageLoop::current(), message_loop_); |
| paused_proxies_++; |
| |
| // Ensure that there is at least one open stream. |
| if (idle_streams_.empty() && !CreateAndOpenStream()) { |
| return false; |
| } |
| |
| close_timer_.Reset(); |
| |
| return true; |
| } |
| |
| AudioOutputStream* AudioOutputDispatcher::StreamStarted() { |
| DCHECK_EQ(MessageLoop::current(), message_loop_); |
| |
| if (idle_streams_.empty() && !CreateAndOpenStream()) { |
| return NULL; |
| } |
| |
| AudioOutputStream* stream = idle_streams_.back(); |
| idle_streams_.pop_back(); |
| |
| DCHECK_GT(paused_proxies_, 0u); |
| paused_proxies_--; |
| |
| close_timer_.Reset(); |
| |
| // Schedule task to allocate streams for other proxies if we need to. |
| message_loop_->PostTask(FROM_HERE, base::Bind( |
| &AudioOutputDispatcher::OpenTask, this)); |
| |
| return stream; |
| } |
| |
| void AudioOutputDispatcher::StreamStopped(AudioOutputStream* stream) { |
| DCHECK_EQ(MessageLoop::current(), message_loop_); |
| |
| paused_proxies_++; |
| |
| pausing_streams_.push_front(stream); |
| close_timer_.Reset(); |
| |
| // Don't recycle stream until two buffers worth of time has elapsed. |
| message_loop_->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&AudioOutputDispatcher::StopStreamTask, this), |
| pause_delay_milliseconds_); |
| } |
| |
| void AudioOutputDispatcher::StopStreamTask() { |
| if (pausing_streams_.empty()) |
| return; |
| AudioOutputStream* stream = pausing_streams_.back(); |
| pausing_streams_.pop_back(); |
| idle_streams_.push_back(stream); |
| } |
| |
| void AudioOutputDispatcher::StreamClosed() { |
| DCHECK_EQ(MessageLoop::current(), message_loop_); |
| |
| while (!pausing_streams_.empty()) { |
| idle_streams_.push_back(pausing_streams_.back()); |
| pausing_streams_.pop_back(); |
| } |
| |
| DCHECK_GT(paused_proxies_, 0u); |
| paused_proxies_--; |
| |
| while (idle_streams_.size() > paused_proxies_) { |
| idle_streams_.back()->Close(); |
| idle_streams_.pop_back(); |
| } |
| } |
| |
| MessageLoop* AudioOutputDispatcher::message_loop() { |
| return message_loop_; |
| } |
| |
| bool AudioOutputDispatcher::CreateAndOpenStream() { |
| AudioOutputStream* stream = |
| audio_manager_->MakeAudioOutputStream(params_); |
| if (!stream) { |
| return false; |
| } |
| if (!stream->Open()) { |
| stream->Close(); |
| return false; |
| } |
| idle_streams_.push_back(stream); |
| return true; |
| } |
| |
| void AudioOutputDispatcher::OpenTask() { |
| // Make sure that we have at least one stream allocated if there |
| // are paused streams. |
| if (paused_proxies_ > 0 && idle_streams_.empty() && |
| pausing_streams_.empty()) { |
| CreateAndOpenStream(); |
| } |
| |
| close_timer_.Reset(); |
| } |
| |
| // This method is called by |close_timer_|. |
| void AudioOutputDispatcher::ClosePendingStreams() { |
| while (!idle_streams_.empty()) { |
| idle_streams_.back()->Close(); |
| idle_streams_.pop_back(); |
| } |
| } |