// 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_impl.h" #include #include "base/bind.h" #include "base/compiler_specific.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "media/audio/audio_io.h" #include "media/audio/audio_output_proxy.h" namespace media { AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( AudioManager* audio_manager, const AudioParameters& params, const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) : AudioOutputDispatcher(audio_manager, params, output_device_id, input_device_id), pause_delay_(base::TimeDelta::FromMicroseconds( 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast(params.sample_rate()))), paused_proxies_(0), weak_this_(this), close_timer_(FROM_HERE, close_delay, this, &AudioOutputDispatcherImpl::ClosePendingStreams) { } AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { DCHECK(proxy_to_physical_map_.empty()); DCHECK(idle_streams_.empty()); DCHECK(pausing_streams_.empty()); } bool AudioOutputDispatcherImpl::OpenStream() { DCHECK(message_loop_->BelongsToCurrentThread()); paused_proxies_++; // Ensure that there is at least one open stream. if (idle_streams_.empty() && !CreateAndOpenStream()) { paused_proxies_--; return false; } close_timer_.Reset(); return true; } bool AudioOutputDispatcherImpl::StartStream( AudioOutputStream::AudioSourceCallback* callback, AudioOutputProxy* stream_proxy) { DCHECK(message_loop_->BelongsToCurrentThread()); if (idle_streams_.empty() && !CreateAndOpenStream()) return false; AudioOutputStream* physical_stream = idle_streams_.back(); DCHECK(physical_stream); idle_streams_.pop_back(); DCHECK_GT(paused_proxies_, 0u); --paused_proxies_; close_timer_.Reset(); double volume = 0; stream_proxy->GetVolume(&volume); physical_stream->SetVolume(volume); physical_stream->Start(callback); proxy_to_physical_map_[stream_proxy] = physical_stream; return true; } void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { DCHECK(message_loop_->BelongsToCurrentThread()); AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); DCHECK(it != proxy_to_physical_map_.end()); AudioOutputStream* physical_stream = it->second; proxy_to_physical_map_.erase(it); physical_stream->Stop(); ++paused_proxies_; pausing_streams_.push_front(physical_stream); // Don't recycle stream until two buffers worth of time has elapsed. message_loop_->PostDelayedTask( FROM_HERE, base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, weak_this_.GetWeakPtr()), pause_delay_); } void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) { DCHECK(message_loop_->BelongsToCurrentThread()); AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); if (it != proxy_to_physical_map_.end()) { AudioOutputStream* physical_stream = it->second; physical_stream->SetVolume(volume); } } void AudioOutputDispatcherImpl::StopStreamTask() { DCHECK(message_loop_->BelongsToCurrentThread()); if (pausing_streams_.empty()) return; AudioOutputStream* stream = pausing_streams_.back(); pausing_streams_.pop_back(); idle_streams_.push_back(stream); close_timer_.Reset(); } void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { DCHECK(message_loop_->BelongsToCurrentThread()); 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 AudioOutputDispatcherImpl::Shutdown() { DCHECK(message_loop_->BelongsToCurrentThread()); // 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 AudioOutputDispatcherImpl::CreateAndOpenStream() { DCHECK(message_loop_->BelongsToCurrentThread()); AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( params_, output_device_id_, input_device_id_); if (!stream) return false; if (!stream->Open()) { stream->Close(); return false; } idle_streams_.push_back(stream); return true; } // This method is called by |close_timer_|. void AudioOutputDispatcherImpl::ClosePendingStreams() { DCHECK(message_loop_->BelongsToCurrentThread()); while (!idle_streams_.empty()) { idle_streams_.back()->Close(); idle_streams_.pop_back(); } } } // namespace media