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