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/message_loop/message_loop.h" 12 #include "base/time/time.h" 13 #include "media/audio/audio_io.h" 14 #include "media/audio/audio_output_proxy.h" 15 16 namespace media { 17 18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( 19 AudioManager* audio_manager, 20 const AudioParameters& params, 21 const std::string& output_device_id, 22 const std::string& input_device_id, 23 const base::TimeDelta& close_delay) 24 : AudioOutputDispatcher(audio_manager, 25 params, 26 output_device_id, 27 input_device_id), 28 idle_proxies_(0), 29 close_timer_(FROM_HERE, 30 close_delay, 31 this, 32 &AudioOutputDispatcherImpl::CloseAllIdleStreams), 33 audio_log_( 34 audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)), 35 audio_stream_id_(0) {} 36 37 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 38 DCHECK_EQ(idle_proxies_, 0u); 39 DCHECK(proxy_to_physical_map_.empty()); 40 DCHECK(idle_streams_.empty()); 41 } 42 43 bool AudioOutputDispatcherImpl::OpenStream() { 44 DCHECK(message_loop_->BelongsToCurrentThread()); 45 46 // Ensure that there is at least one open stream. 47 if (idle_streams_.empty() && !CreateAndOpenStream()) 48 return false; 49 50 ++idle_proxies_; 51 close_timer_.Reset(); 52 return true; 53 } 54 55 bool AudioOutputDispatcherImpl::StartStream( 56 AudioOutputStream::AudioSourceCallback* callback, 57 AudioOutputProxy* stream_proxy) { 58 DCHECK(message_loop_->BelongsToCurrentThread()); 59 DCHECK(proxy_to_physical_map_.find(stream_proxy) == 60 proxy_to_physical_map_.end()); 61 62 if (idle_streams_.empty() && !CreateAndOpenStream()) 63 return false; 64 65 AudioOutputStream* physical_stream = idle_streams_.back(); 66 idle_streams_.pop_back(); 67 68 DCHECK_GT(idle_proxies_, 0u); 69 --idle_proxies_; 70 71 double volume = 0; 72 stream_proxy->GetVolume(&volume); 73 physical_stream->SetVolume(volume); 74 const int stream_id = audio_stream_ids_[physical_stream]; 75 audio_log_->OnSetVolume(stream_id, volume); 76 physical_stream->Start(callback); 77 audio_log_->OnStarted(stream_id); 78 proxy_to_physical_map_[stream_proxy] = physical_stream; 79 80 close_timer_.Reset(); 81 return true; 82 } 83 84 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 85 DCHECK(message_loop_->BelongsToCurrentThread()); 86 87 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 88 DCHECK(it != proxy_to_physical_map_.end()); 89 AudioOutputStream* physical_stream = it->second; 90 proxy_to_physical_map_.erase(it); 91 92 physical_stream->Stop(); 93 audio_log_->OnStopped(audio_stream_ids_[physical_stream]); 94 ++idle_proxies_; 95 idle_streams_.push_back(physical_stream); 96 97 close_timer_.Reset(); 98 } 99 100 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 101 double volume) { 102 DCHECK(message_loop_->BelongsToCurrentThread()); 103 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 104 if (it != proxy_to_physical_map_.end()) { 105 AudioOutputStream* physical_stream = it->second; 106 physical_stream->SetVolume(volume); 107 audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume); 108 } 109 } 110 111 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 112 DCHECK(message_loop_->BelongsToCurrentThread()); 113 114 DCHECK_GT(idle_proxies_, 0u); 115 --idle_proxies_; 116 117 // Leave at least a single stream running until the close timer fires to help 118 // cycle time when streams are opened and closed repeatedly. 119 CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1))); 120 close_timer_.Reset(); 121 } 122 123 void AudioOutputDispatcherImpl::Shutdown() { 124 DCHECK(message_loop_->BelongsToCurrentThread()); 125 126 // Close all idle streams immediately. The |close_timer_| will handle 127 // invalidating any outstanding tasks upon its destruction. 128 CloseAllIdleStreams(); 129 } 130 131 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 132 DCHECK(message_loop_->BelongsToCurrentThread()); 133 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 134 params_, output_device_id_, input_device_id_); 135 if (!stream) 136 return false; 137 138 if (!stream->Open()) { 139 stream->Close(); 140 return false; 141 } 142 143 const int stream_id = audio_stream_id_++; 144 audio_stream_ids_[stream] = stream_id; 145 audio_log_->OnCreated( 146 stream_id, params_, input_device_id_, output_device_id_); 147 148 idle_streams_.push_back(stream); 149 return true; 150 } 151 152 void AudioOutputDispatcherImpl::CloseAllIdleStreams() { 153 DCHECK(message_loop_->BelongsToCurrentThread()); 154 CloseIdleStreams(0); 155 } 156 157 void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) { 158 DCHECK(message_loop_->BelongsToCurrentThread()); 159 if (idle_streams_.size() <= keep_alive) 160 return; 161 for (size_t i = keep_alive; i < idle_streams_.size(); ++i) { 162 AudioOutputStream* stream = idle_streams_[i]; 163 stream->Close(); 164 165 AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream); 166 DCHECK(it != audio_stream_ids_.end()); 167 audio_log_->OnClosed(it->second); 168 audio_stream_ids_.erase(it); 169 } 170 idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); 171 } 172 173 void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { 174 DCHECK(message_loop_->BelongsToCurrentThread()); 175 CloseAllIdleStreams(); 176 } 177 178 void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { 179 DCHECK(message_loop_->BelongsToCurrentThread()); 180 181 // Should only be called when the dispatcher is used with fake streams which 182 // don't need to be shutdown or restarted. 183 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); 184 } 185 186 } // namespace media 187