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