audio_output_dispatcher_impl.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/single_thread_task_runner.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 base::TimeDelta& close_delay) 23 : AudioOutputDispatcher(audio_manager, 24 params, 25 output_device_id), 26 idle_proxies_(0), 27 close_timer_(FROM_HERE, 28 close_delay, 29 this, 30 &AudioOutputDispatcherImpl::CloseAllIdleStreams), 31 audio_log_( 32 audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)), 33 audio_stream_id_(0) {} 34 35AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 36 DCHECK_EQ(idle_proxies_, 0u); 37 DCHECK(proxy_to_physical_map_.empty()); 38 DCHECK(idle_streams_.empty()); 39} 40 41bool AudioOutputDispatcherImpl::OpenStream() { 42 DCHECK(task_runner_->BelongsToCurrentThread()); 43 44 // Ensure that there is at least one open stream. 45 if (idle_streams_.empty() && !CreateAndOpenStream()) 46 return false; 47 48 ++idle_proxies_; 49 close_timer_.Reset(); 50 return true; 51} 52 53bool AudioOutputDispatcherImpl::StartStream( 54 AudioOutputStream::AudioSourceCallback* callback, 55 AudioOutputProxy* stream_proxy) { 56 DCHECK(task_runner_->BelongsToCurrentThread()); 57 DCHECK(proxy_to_physical_map_.find(stream_proxy) == 58 proxy_to_physical_map_.end()); 59 60 if (idle_streams_.empty() && !CreateAndOpenStream()) 61 return false; 62 63 AudioOutputStream* physical_stream = idle_streams_.back(); 64 idle_streams_.pop_back(); 65 66 DCHECK_GT(idle_proxies_, 0u); 67 --idle_proxies_; 68 69 double volume = 0; 70 stream_proxy->GetVolume(&volume); 71 physical_stream->SetVolume(volume); 72 const int stream_id = audio_stream_ids_[physical_stream]; 73 audio_log_->OnSetVolume(stream_id, volume); 74 physical_stream->Start(callback); 75 audio_log_->OnStarted(stream_id); 76 proxy_to_physical_map_[stream_proxy] = physical_stream; 77 78 close_timer_.Reset(); 79 return true; 80} 81 82void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 83 DCHECK(task_runner_->BelongsToCurrentThread()); 84 85 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 86 DCHECK(it != proxy_to_physical_map_.end()); 87 AudioOutputStream* physical_stream = it->second; 88 proxy_to_physical_map_.erase(it); 89 90 physical_stream->Stop(); 91 audio_log_->OnStopped(audio_stream_ids_[physical_stream]); 92 ++idle_proxies_; 93 idle_streams_.push_back(physical_stream); 94 95 close_timer_.Reset(); 96} 97 98void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 99 double volume) { 100 DCHECK(task_runner_->BelongsToCurrentThread()); 101 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 102 if (it != proxy_to_physical_map_.end()) { 103 AudioOutputStream* physical_stream = it->second; 104 physical_stream->SetVolume(volume); 105 audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume); 106 } 107} 108 109void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 110 DCHECK(task_runner_->BelongsToCurrentThread()); 111 112 DCHECK_GT(idle_proxies_, 0u); 113 --idle_proxies_; 114 115 // Leave at least a single stream running until the close timer fires to help 116 // cycle time when streams are opened and closed repeatedly. 117 CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1))); 118 close_timer_.Reset(); 119} 120 121void AudioOutputDispatcherImpl::Shutdown() { 122 DCHECK(task_runner_->BelongsToCurrentThread()); 123 124 // Close all idle streams immediately. The |close_timer_| will handle 125 // invalidating any outstanding tasks upon its destruction. 126 CloseAllIdleStreams(); 127} 128 129bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 130 DCHECK(task_runner_->BelongsToCurrentThread()); 131 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 132 params_, device_id_); 133 if (!stream) 134 return false; 135 136 if (!stream->Open()) { 137 stream->Close(); 138 return false; 139 } 140 141 const int stream_id = audio_stream_id_++; 142 audio_stream_ids_[stream] = stream_id; 143 audio_log_->OnCreated( 144 stream_id, params_, device_id_); 145 146 idle_streams_.push_back(stream); 147 return true; 148} 149 150void AudioOutputDispatcherImpl::CloseAllIdleStreams() { 151 DCHECK(task_runner_->BelongsToCurrentThread()); 152 CloseIdleStreams(0); 153} 154 155void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) { 156 DCHECK(task_runner_->BelongsToCurrentThread()); 157 if (idle_streams_.size() <= keep_alive) 158 return; 159 for (size_t i = keep_alive; i < idle_streams_.size(); ++i) { 160 AudioOutputStream* stream = idle_streams_[i]; 161 stream->Close(); 162 163 AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream); 164 DCHECK(it != audio_stream_ids_.end()); 165 audio_log_->OnClosed(it->second); 166 audio_stream_ids_.erase(it); 167 } 168 idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); 169} 170 171void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { 172 DCHECK(task_runner_->BelongsToCurrentThread()); 173 CloseAllIdleStreams(); 174} 175 176void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { 177 DCHECK(task_runner_->BelongsToCurrentThread()); 178 179 // Should only be called when the dispatcher is used with fake streams which 180 // don't need to be shutdown or restarted. 181 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); 182} 183 184} // namespace media 185