1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/audio/audio_output_dispatcher_impl.h" 6 7#include <algorithm> 8 9#include "base/bind.h" 10#include "base/compiler_specific.h" 11#include "base/single_thread_task_runner.h" 12#include "base/time/time.h" 13#include "media/audio/audio_io.h" 14#include "media/audio/audio_output_proxy.h" 15 16namespace media { 17 18AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( 19 AudioManager* audio_manager, 20 const AudioParameters& params, 21 const std::string& output_device_id, 22 const base::TimeDelta& close_delay) 23 : AudioOutputDispatcher(audio_manager, 24 params, 25 output_device_id), 26 idle_proxies_(0), 27 close_timer_(FROM_HERE, 28 close_delay, 29 this, 30 &AudioOutputDispatcherImpl::CloseAllIdleStreams), 31 audio_log_( 32 audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)), 33 audio_stream_id_(0) {} 34 35AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 36 DCHECK_EQ(idle_proxies_, 0u); 37 DCHECK(proxy_to_physical_map_.empty()); 38 DCHECK(idle_streams_.empty()); 39} 40 41bool AudioOutputDispatcherImpl::OpenStream() { 42 DCHECK(task_runner_->BelongsToCurrentThread()); 43 44 // Ensure that there is at least one open stream. 45 if (idle_streams_.empty() && !CreateAndOpenStream()) 46 return false; 47 48 ++idle_proxies_; 49 close_timer_.Reset(); 50 return true; 51} 52 53bool AudioOutputDispatcherImpl::StartStream( 54 AudioOutputStream::AudioSourceCallback* callback, 55 AudioOutputProxy* stream_proxy) { 56 DCHECK(task_runner_->BelongsToCurrentThread()); 57 DCHECK(proxy_to_physical_map_.find(stream_proxy) == 58 proxy_to_physical_map_.end()); 59 60 if (idle_streams_.empty() && !CreateAndOpenStream()) 61 return false; 62 63 AudioOutputStream* physical_stream = idle_streams_.back(); 64 idle_streams_.pop_back(); 65 66 DCHECK_GT(idle_proxies_, 0u); 67 --idle_proxies_; 68 69 double volume = 0; 70 stream_proxy->GetVolume(&volume); 71 physical_stream->SetVolume(volume); 72 const int stream_id = audio_stream_ids_[physical_stream]; 73 audio_log_->OnSetVolume(stream_id, volume); 74 physical_stream->Start(callback); 75 audio_log_->OnStarted(stream_id); 76 proxy_to_physical_map_[stream_proxy] = physical_stream; 77 78 close_timer_.Reset(); 79 return true; 80} 81 82void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 83 DCHECK(task_runner_->BelongsToCurrentThread()); 84 85 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 86 DCHECK(it != proxy_to_physical_map_.end()); 87 AudioOutputStream* physical_stream = it->second; 88 proxy_to_physical_map_.erase(it); 89 90 physical_stream->Stop(); 91 audio_log_->OnStopped(audio_stream_ids_[physical_stream]); 92 ++idle_proxies_; 93 idle_streams_.push_back(physical_stream); 94 95 close_timer_.Reset(); 96} 97 98void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 99 double volume) { 100 DCHECK(task_runner_->BelongsToCurrentThread()); 101 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 102 if (it != proxy_to_physical_map_.end()) { 103 AudioOutputStream* physical_stream = it->second; 104 physical_stream->SetVolume(volume); 105 audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume); 106 } 107} 108 109void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 110 DCHECK(task_runner_->BelongsToCurrentThread()); 111 112 DCHECK_GT(idle_proxies_, 0u); 113 --idle_proxies_; 114 115 // Leave at least a single stream running until the close timer fires to help 116 // cycle time when streams are opened and closed repeatedly. 117 CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1))); 118 close_timer_.Reset(); 119} 120 121void AudioOutputDispatcherImpl::Shutdown() { 122 DCHECK(task_runner_->BelongsToCurrentThread()); 123 124 // Close all idle streams immediately. The |close_timer_| will handle 125 // invalidating any outstanding tasks upon its destruction. 126 CloseAllIdleStreams(); 127 128 // No AudioOutputProxy objects should hold a reference to us when we get 129 // to this stage. 130 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; 131} 132 133bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 134 DCHECK(task_runner_->BelongsToCurrentThread()); 135 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 136 params_, device_id_); 137 if (!stream) 138 return false; 139 140 if (!stream->Open()) { 141 stream->Close(); 142 return false; 143 } 144 145 const int stream_id = audio_stream_id_++; 146 audio_stream_ids_[stream] = stream_id; 147 audio_log_->OnCreated( 148 stream_id, params_, device_id_); 149 150 idle_streams_.push_back(stream); 151 return true; 152} 153 154void AudioOutputDispatcherImpl::CloseAllIdleStreams() { 155 DCHECK(task_runner_->BelongsToCurrentThread()); 156 CloseIdleStreams(0); 157} 158 159void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) { 160 DCHECK(task_runner_->BelongsToCurrentThread()); 161 if (idle_streams_.size() <= keep_alive) 162 return; 163 for (size_t i = keep_alive; i < idle_streams_.size(); ++i) { 164 AudioOutputStream* stream = idle_streams_[i]; 165 stream->Close(); 166 167 AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream); 168 DCHECK(it != audio_stream_ids_.end()); 169 audio_log_->OnClosed(it->second); 170 audio_stream_ids_.erase(it); 171 } 172 idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); 173} 174 175} // namespace media 176