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 #include "media/audio/audio_util.h" 16 17 namespace media { 18 19 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( 20 AudioManager* audio_manager, 21 const AudioParameters& params, 22 const std::string& input_device_id, 23 const base::TimeDelta& close_delay) 24 : AudioOutputDispatcher(audio_manager, params, input_device_id), 25 pause_delay_(base::TimeDelta::FromMicroseconds( 26 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / 27 static_cast<float>(params.sample_rate()))), 28 paused_proxies_(0), 29 weak_this_(this), 30 close_timer_(FROM_HERE, 31 close_delay, 32 this, 33 &AudioOutputDispatcherImpl::ClosePendingStreams) { 34 } 35 36 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 37 DCHECK(proxy_to_physical_map_.empty()); 38 DCHECK(idle_streams_.empty()); 39 DCHECK(pausing_streams_.empty()); 40 } 41 42 bool AudioOutputDispatcherImpl::OpenStream() { 43 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 44 45 paused_proxies_++; 46 47 // Ensure that there is at least one open stream. 48 if (idle_streams_.empty() && !CreateAndOpenStream()) { 49 paused_proxies_--; 50 return false; 51 } 52 53 close_timer_.Reset(); 54 return true; 55 } 56 57 bool AudioOutputDispatcherImpl::StartStream( 58 AudioOutputStream::AudioSourceCallback* callback, 59 AudioOutputProxy* stream_proxy) { 60 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 61 62 if (idle_streams_.empty() && !CreateAndOpenStream()) 63 return false; 64 65 AudioOutputStream* physical_stream = idle_streams_.back(); 66 DCHECK(physical_stream); 67 idle_streams_.pop_back(); 68 69 DCHECK_GT(paused_proxies_, 0u); 70 --paused_proxies_; 71 72 close_timer_.Reset(); 73 74 // Schedule task to allocate streams for other proxies if we need to. 75 message_loop_->PostTask(FROM_HERE, base::Bind( 76 &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr())); 77 78 double volume = 0; 79 stream_proxy->GetVolume(&volume); 80 physical_stream->SetVolume(volume); 81 physical_stream->Start(callback); 82 proxy_to_physical_map_[stream_proxy] = physical_stream; 83 return true; 84 } 85 86 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 87 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 88 89 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 90 DCHECK(it != proxy_to_physical_map_.end()); 91 AudioOutputStream* physical_stream = it->second; 92 proxy_to_physical_map_.erase(it); 93 94 physical_stream->Stop(); 95 96 ++paused_proxies_; 97 98 pausing_streams_.push_front(physical_stream); 99 100 // Don't recycle stream until two buffers worth of time has elapsed. 101 message_loop_->PostDelayedTask( 102 FROM_HERE, 103 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, 104 weak_this_.GetWeakPtr()), 105 pause_delay_); 106 } 107 108 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 109 double volume) { 110 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 111 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 112 if (it != proxy_to_physical_map_.end()) { 113 AudioOutputStream* physical_stream = it->second; 114 physical_stream->SetVolume(volume); 115 } 116 } 117 118 void AudioOutputDispatcherImpl::StopStreamTask() { 119 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 120 121 if (pausing_streams_.empty()) 122 return; 123 124 AudioOutputStream* stream = pausing_streams_.back(); 125 pausing_streams_.pop_back(); 126 idle_streams_.push_back(stream); 127 close_timer_.Reset(); 128 } 129 130 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 131 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 132 133 while (!pausing_streams_.empty()) { 134 idle_streams_.push_back(pausing_streams_.back()); 135 pausing_streams_.pop_back(); 136 } 137 138 DCHECK_GT(paused_proxies_, 0u); 139 paused_proxies_--; 140 141 while (idle_streams_.size() > paused_proxies_) { 142 idle_streams_.back()->Close(); 143 idle_streams_.pop_back(); 144 } 145 } 146 147 void AudioOutputDispatcherImpl::Shutdown() { 148 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 149 150 // Cancel any pending tasks to close paused streams or create new ones. 151 weak_this_.InvalidateWeakPtrs(); 152 153 // No AudioOutputProxy objects should hold a reference to us when we get 154 // to this stage. 155 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; 156 157 AudioOutputStreamList::iterator it = idle_streams_.begin(); 158 for (; it != idle_streams_.end(); ++it) 159 (*it)->Close(); 160 idle_streams_.clear(); 161 162 it = pausing_streams_.begin(); 163 for (; it != pausing_streams_.end(); ++it) 164 (*it)->Close(); 165 pausing_streams_.clear(); 166 } 167 168 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 169 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 170 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 171 params_, input_device_id_); 172 if (!stream) 173 return false; 174 175 if (!stream->Open()) { 176 stream->Close(); 177 return false; 178 } 179 idle_streams_.push_back(stream); 180 return true; 181 } 182 183 void AudioOutputDispatcherImpl::OpenTask() { 184 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 185 // Make sure that we have at least one stream allocated if there 186 // are paused streams. 187 if (paused_proxies_ > 0 && idle_streams_.empty() && 188 pausing_streams_.empty()) { 189 CreateAndOpenStream(); 190 } 191 192 close_timer_.Reset(); 193 } 194 195 // This method is called by |close_timer_|. 196 void AudioOutputDispatcherImpl::ClosePendingStreams() { 197 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 198 while (!idle_streams_.empty()) { 199 idle_streams_.back()->Close(); 200 idle_streams_.pop_back(); 201 } 202 } 203 204 } // namespace media 205