// Copyright (c) 2012 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, base::TimeDelta close_delay) : audio_manager_(audio_manager), message_loop_(MessageLoop::current()), params_(params), pause_delay_(base::TimeDelta::FromMilliseconds( 2 * params.samples_per_packet * base::Time::kMillisecondsPerSecond / params.sample_rate)), paused_proxies_(0), ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), close_timer_(FROM_HERE, close_delay, weak_this_.GetWeakPtr(), &AudioOutputDispatcher::ClosePendingStreams) { // We expect to be instantiated on the audio thread. Otherwise the // message_loop_ member will point to the wrong message loop! DCHECK(audio_manager->GetMessageLoop()->BelongsToCurrentThread()); } AudioOutputDispatcher::~AudioOutputDispatcher() { DCHECK_EQ(MessageLoop::current(), message_loop_); } 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, weak_this_.GetWeakPtr())); return stream; } void AudioOutputDispatcher::StreamStopped(AudioOutputStream* stream) { DCHECK_EQ(MessageLoop::current(), message_loop_); paused_proxies_++; pausing_streams_.push_front(stream); // Don't recycle stream until two buffers worth of time has elapsed. message_loop_->PostDelayedTask( FROM_HERE, base::Bind(&AudioOutputDispatcher::StopStreamTask, weak_this_.GetWeakPtr()), pause_delay_); } void AudioOutputDispatcher::StopStreamTask() { DCHECK_EQ(MessageLoop::current(), message_loop_); if (pausing_streams_.empty()) return; AudioOutputStream* stream = pausing_streams_.back(); pausing_streams_.pop_back(); idle_streams_.push_back(stream); close_timer_.Reset(); } 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(); } } void AudioOutputDispatcher::Shutdown() { DCHECK_EQ(MessageLoop::current(), message_loop_); // Cancel any pending tasks to close paused streams or create new ones. weak_this_.InvalidateWeakPtrs(); // No AudioOutputProxy objects should hold a reference to us when we get // to this stage. DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; AudioOutputStreamList::iterator it = idle_streams_.begin(); for (; it != idle_streams_.end(); ++it) (*it)->Close(); idle_streams_.clear(); it = pausing_streams_.begin(); for (; it != pausing_streams_.end(); ++it) (*it)->Close(); pausing_streams_.clear(); } 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() { DCHECK_EQ(MessageLoop::current(), message_loop_); while (!idle_streams_.empty()) { idle_streams_.back()->Close(); idle_streams_.pop_back(); } }