// 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 <algorithm> #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), idle_proxies_(0), close_timer_(FROM_HERE, close_delay, this, &AudioOutputDispatcherImpl::CloseAllIdleStreams), audio_log_( audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)), audio_stream_id_(0) {} AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { DCHECK_EQ(idle_proxies_, 0u); DCHECK(proxy_to_physical_map_.empty()); DCHECK(idle_streams_.empty()); } bool AudioOutputDispatcherImpl::OpenStream() { DCHECK(message_loop_->BelongsToCurrentThread()); // Ensure that there is at least one open stream. if (idle_streams_.empty() && !CreateAndOpenStream()) return false; ++idle_proxies_; close_timer_.Reset(); return true; } bool AudioOutputDispatcherImpl::StartStream( AudioOutputStream::AudioSourceCallback* callback, AudioOutputProxy* stream_proxy) { DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(proxy_to_physical_map_.find(stream_proxy) == proxy_to_physical_map_.end()); if (idle_streams_.empty() && !CreateAndOpenStream()) return false; AudioOutputStream* physical_stream = idle_streams_.back(); idle_streams_.pop_back(); DCHECK_GT(idle_proxies_, 0u); --idle_proxies_; double volume = 0; stream_proxy->GetVolume(&volume); physical_stream->SetVolume(volume); const int stream_id = audio_stream_ids_[physical_stream]; audio_log_->OnSetVolume(stream_id, volume); physical_stream->Start(callback); audio_log_->OnStarted(stream_id); proxy_to_physical_map_[stream_proxy] = physical_stream; close_timer_.Reset(); 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(); audio_log_->OnStopped(audio_stream_ids_[physical_stream]); ++idle_proxies_; idle_streams_.push_back(physical_stream); close_timer_.Reset(); } 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); audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume); } } void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_GT(idle_proxies_, 0u); --idle_proxies_; // Leave at least a single stream running until the close timer fires to help // cycle time when streams are opened and closed repeatedly. CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1))); close_timer_.Reset(); } void AudioOutputDispatcherImpl::Shutdown() { DCHECK(message_loop_->BelongsToCurrentThread()); // Close all idle streams immediately. The |close_timer_| will handle // invalidating any outstanding tasks upon its destruction. CloseAllIdleStreams(); } 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; } const int stream_id = audio_stream_id_++; audio_stream_ids_[stream] = stream_id; audio_log_->OnCreated( stream_id, params_, input_device_id_, output_device_id_); idle_streams_.push_back(stream); return true; } void AudioOutputDispatcherImpl::CloseAllIdleStreams() { DCHECK(message_loop_->BelongsToCurrentThread()); CloseIdleStreams(0); } void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) { DCHECK(message_loop_->BelongsToCurrentThread()); if (idle_streams_.size() <= keep_alive) return; for (size_t i = keep_alive; i < idle_streams_.size(); ++i) { AudioOutputStream* stream = idle_streams_[i]; stream->Close(); AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream); DCHECK(it != audio_stream_ids_.end()); audio_log_->OnClosed(it->second); audio_stream_ids_.erase(it); } idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); } void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { DCHECK(message_loop_->BelongsToCurrentThread()); CloseAllIdleStreams(); } void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { DCHECK(message_loop_->BelongsToCurrentThread()); // Should only be called when the dispatcher is used with fake streams which // don't need to be shutdown or restarted. CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); } } // namespace media