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