audio_output_dispatcher_impl.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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 double volume = 0; 76 stream_proxy->GetVolume(&volume); 77 physical_stream->SetVolume(volume); 78 physical_stream->Start(callback); 79 proxy_to_physical_map_[stream_proxy] = physical_stream; 80 return true; 81} 82 83void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 84 DCHECK(message_loop_->BelongsToCurrentThread()); 85 86 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 87 DCHECK(it != proxy_to_physical_map_.end()); 88 AudioOutputStream* physical_stream = it->second; 89 proxy_to_physical_map_.erase(it); 90 91 physical_stream->Stop(); 92 93 ++paused_proxies_; 94 95 pausing_streams_.push_front(physical_stream); 96 97 // Don't recycle stream until two buffers worth of time has elapsed. 98 message_loop_->PostDelayedTask( 99 FROM_HERE, 100 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, 101 weak_this_.GetWeakPtr()), 102 pause_delay_); 103} 104 105void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 106 double volume) { 107 DCHECK(message_loop_->BelongsToCurrentThread()); 108 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 109 if (it != proxy_to_physical_map_.end()) { 110 AudioOutputStream* physical_stream = it->second; 111 physical_stream->SetVolume(volume); 112 } 113} 114 115void AudioOutputDispatcherImpl::StopStreamTask() { 116 DCHECK(message_loop_->BelongsToCurrentThread()); 117 118 if (pausing_streams_.empty()) 119 return; 120 121 AudioOutputStream* stream = pausing_streams_.back(); 122 pausing_streams_.pop_back(); 123 idle_streams_.push_back(stream); 124 close_timer_.Reset(); 125} 126 127void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 128 DCHECK(message_loop_->BelongsToCurrentThread()); 129 130 while (!pausing_streams_.empty()) { 131 idle_streams_.push_back(pausing_streams_.back()); 132 pausing_streams_.pop_back(); 133 } 134 135 DCHECK_GT(paused_proxies_, 0u); 136 paused_proxies_--; 137 138 while (idle_streams_.size() > paused_proxies_) { 139 idle_streams_.back()->Close(); 140 idle_streams_.pop_back(); 141 } 142} 143 144void AudioOutputDispatcherImpl::Shutdown() { 145 DCHECK(message_loop_->BelongsToCurrentThread()); 146 147 // Cancel any pending tasks to close paused streams or create new ones. 148 weak_this_.InvalidateWeakPtrs(); 149 150 // No AudioOutputProxy objects should hold a reference to us when we get 151 // to this stage. 152 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; 153 154 AudioOutputStreamList::iterator it = idle_streams_.begin(); 155 for (; it != idle_streams_.end(); ++it) 156 (*it)->Close(); 157 idle_streams_.clear(); 158 159 it = pausing_streams_.begin(); 160 for (; it != pausing_streams_.end(); ++it) 161 (*it)->Close(); 162 pausing_streams_.clear(); 163} 164 165bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 166 DCHECK(message_loop_->BelongsToCurrentThread()); 167 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 168 params_, output_device_id_, input_device_id_); 169 if (!stream) 170 return false; 171 172 if (!stream->Open()) { 173 stream->Close(); 174 return false; 175 } 176 idle_streams_.push_back(stream); 177 return true; 178} 179 180// This method is called by |close_timer_|. 181void AudioOutputDispatcherImpl::ClosePendingStreams() { 182 DCHECK(message_loop_->BelongsToCurrentThread()); 183 while (!idle_streams_.empty()) { 184 idle_streams_.back()->Close(); 185 idle_streams_.pop_back(); 186 } 187} 188 189} // namespace media 190