Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/audio_output_dispatcher_impl.h" | 5 #include "media/audio/audio_output_dispatcher_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
| 12 #include "base/time/time.h" | 12 #include "base/time/time.h" |
| 13 #include "media/audio/audio_io.h" | 13 #include "media/audio/audio_io.h" |
| 14 #include "media/audio/audio_output_proxy.h" | 14 #include "media/audio/audio_output_proxy.h" |
| 15 | 15 |
| 16 namespace media { | 16 namespace media { |
| 17 | 17 |
| 18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( | 18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( |
| 19 AudioManager* audio_manager, | 19 AudioManager* audio_manager, |
| 20 const AudioParameters& params, | 20 const AudioParameters& params, |
| 21 const std::string& output_device_id, | 21 const std::string& output_device_id, |
| 22 const std::string& input_device_id, | 22 const std::string& input_device_id, |
| 23 const base::TimeDelta& close_delay) | 23 const base::TimeDelta& close_delay) |
| 24 : AudioOutputDispatcher(audio_manager, params, output_device_id, | 24 : AudioOutputDispatcher(audio_manager, |
| 25 input_device_id), | 25 params, |
| 26 pause_delay_(base::TimeDelta::FromMicroseconds( | 26 output_device_id, |
| 27 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / | 27 input_device_id), |
| 28 static_cast<float>(params.sample_rate()))), | 28 idle_proxies_(0), |
| 29 paused_proxies_(0), | |
| 30 weak_this_(this), | |
| 31 close_timer_(FROM_HERE, | 29 close_timer_(FROM_HERE, |
| 32 close_delay, | 30 close_delay, |
| 33 this, | 31 this, |
| 34 &AudioOutputDispatcherImpl::ClosePendingStreams) { | 32 &AudioOutputDispatcherImpl::CloseAllIdleStreams) {} |
| 35 } | |
| 36 | 33 |
| 37 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { | 34 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { |
| 35 DCHECK_EQ(idle_proxies_, 0u); | |
| 38 DCHECK(proxy_to_physical_map_.empty()); | 36 DCHECK(proxy_to_physical_map_.empty()); |
| 39 DCHECK(idle_streams_.empty()); | 37 DCHECK(idle_streams_.empty()); |
| 40 DCHECK(pausing_streams_.empty()); | |
| 41 } | 38 } |
| 42 | 39 |
| 43 bool AudioOutputDispatcherImpl::OpenStream() { | 40 bool AudioOutputDispatcherImpl::OpenStream() { |
| 44 DCHECK(message_loop_->BelongsToCurrentThread()); | 41 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 45 | 42 |
| 46 paused_proxies_++; | 43 // Ensure that there is at least one open stream. |
| 44 if (idle_streams_.empty() && !CreateAndOpenStream()) | |
| 45 return false; | |
| 47 | 46 |
| 48 // Ensure that there is at least one open stream. | 47 ++idle_proxies_; |
| 49 if (idle_streams_.empty() && !CreateAndOpenStream()) { | |
| 50 paused_proxies_--; | |
| 51 return false; | |
| 52 } | |
| 53 | |
| 54 close_timer_.Reset(); | 48 close_timer_.Reset(); |
| 55 return true; | 49 return true; |
| 56 } | 50 } |
| 57 | 51 |
| 58 bool AudioOutputDispatcherImpl::StartStream( | 52 bool AudioOutputDispatcherImpl::StartStream( |
| 59 AudioOutputStream::AudioSourceCallback* callback, | 53 AudioOutputStream::AudioSourceCallback* callback, |
| 60 AudioOutputProxy* stream_proxy) { | 54 AudioOutputProxy* stream_proxy) { |
| 61 DCHECK(message_loop_->BelongsToCurrentThread()); | 55 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 62 DCHECK(proxy_to_physical_map_.find(stream_proxy) == | 56 DCHECK(proxy_to_physical_map_.find(stream_proxy) == |
| 63 proxy_to_physical_map_.end()); | 57 proxy_to_physical_map_.end()); |
| 64 | 58 |
| 65 if (idle_streams_.empty() && !CreateAndOpenStream()) | 59 if (idle_streams_.empty() && !CreateAndOpenStream()) |
| 66 return false; | 60 return false; |
| 67 | 61 |
| 68 AudioOutputStream* physical_stream = idle_streams_.back(); | 62 AudioOutputStream* physical_stream = idle_streams_.back(); |
| 69 DCHECK(physical_stream); | |
| 70 idle_streams_.pop_back(); | 63 idle_streams_.pop_back(); |
| 71 | 64 |
| 72 DCHECK_GT(paused_proxies_, 0u); | 65 DCHECK_GT(idle_proxies_, 0u); |
| 73 --paused_proxies_; | 66 --idle_proxies_; |
| 74 | |
| 75 close_timer_.Reset(); | |
| 76 | 67 |
| 77 double volume = 0; | 68 double volume = 0; |
| 78 stream_proxy->GetVolume(&volume); | 69 stream_proxy->GetVolume(&volume); |
| 79 physical_stream->SetVolume(volume); | 70 physical_stream->SetVolume(volume); |
| 80 physical_stream->Start(callback); | 71 physical_stream->Start(callback); |
| 81 proxy_to_physical_map_[stream_proxy] = physical_stream; | 72 proxy_to_physical_map_[stream_proxy] = physical_stream; |
| 73 | |
| 74 close_timer_.Reset(); | |
| 82 return true; | 75 return true; |
| 83 } | 76 } |
| 84 | 77 |
| 85 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { | 78 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { |
| 86 DCHECK(message_loop_->BelongsToCurrentThread()); | 79 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 87 | 80 |
| 88 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | 81 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); |
| 89 DCHECK(it != proxy_to_physical_map_.end()); | 82 DCHECK(it != proxy_to_physical_map_.end()); |
| 90 AudioOutputStream* physical_stream = it->second; | 83 AudioOutputStream* physical_stream = it->second; |
| 91 proxy_to_physical_map_.erase(it); | 84 proxy_to_physical_map_.erase(it); |
| 92 | 85 |
| 93 physical_stream->Stop(); | 86 physical_stream->Stop(); |
| 87 ++idle_proxies_; | |
| 88 idle_streams_.push_back(physical_stream); | |
|
scherkus (not reviewing)
2013/12/06 23:04:14
what's the difference between idle_streams_ and id
DaleCurtis
2013/12/07 00:33:33
idle_streams_ may be empty when idle_proxies_ > 0.
scherkus (not reviewing)
2013/12/09 18:53:12
I suppose I don't understand what an idle_proxy_ i
DaleCurtis
2013/12/09 19:16:36
An idle proxy is an AudioOutputProxy object in the
| |
| 94 | 89 |
| 95 ++paused_proxies_; | 90 close_timer_.Reset(); |
| 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 } | 91 } |
| 106 | 92 |
| 107 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, | 93 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
| 108 double volume) { | 94 double volume) { |
| 109 DCHECK(message_loop_->BelongsToCurrentThread()); | 95 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 110 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | 96 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); |
| 111 if (it != proxy_to_physical_map_.end()) { | 97 if (it != proxy_to_physical_map_.end()) { |
| 112 AudioOutputStream* physical_stream = it->second; | 98 AudioOutputStream* physical_stream = it->second; |
| 113 physical_stream->SetVolume(volume); | 99 physical_stream->SetVolume(volume); |
| 114 } | 100 } |
| 115 } | 101 } |
| 116 | 102 |
| 117 void AudioOutputDispatcherImpl::StopStreamTask() { | |
| 118 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 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 | |
| 129 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { | 103 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { |
| 130 DCHECK(message_loop_->BelongsToCurrentThread()); | 104 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 131 | 105 |
| 132 while (!pausing_streams_.empty()) { | 106 DCHECK_GT(idle_proxies_, 0u); |
| 133 idle_streams_.push_back(pausing_streams_.back()); | 107 --idle_proxies_; |
| 134 pausing_streams_.pop_back(); | |
| 135 } | |
| 136 | 108 |
| 137 DCHECK_GT(paused_proxies_, 0u); | 109 // Leave at least a single stream running until the close timer fires to help |
| 138 paused_proxies_--; | 110 // cycle time when streams are opened and closed repeatedly. |
| 139 | 111 CloseIdleStreams(idle_proxies_ + 1); |
| 140 while (idle_streams_.size() > paused_proxies_) { | 112 close_timer_.Reset(); |
| 141 idle_streams_.back()->Close(); | |
| 142 idle_streams_.pop_back(); | |
| 143 } | |
| 144 } | 113 } |
| 145 | 114 |
| 146 void AudioOutputDispatcherImpl::Shutdown() { | 115 void AudioOutputDispatcherImpl::Shutdown() { |
| 147 DCHECK(message_loop_->BelongsToCurrentThread()); | 116 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 148 | 117 |
| 149 // Cancel any pending tasks to close paused streams or create new ones. | 118 // Close all idle streams immediately. The |close_timer_| will handle |
| 150 weak_this_.InvalidateWeakPtrs(); | 119 // invalidating any outstanding tasks upon its destruction. |
| 151 | 120 CloseAllIdleStreams(); |
| 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 } | 121 } |
| 166 | 122 |
| 167 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { | 123 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { |
| 168 DCHECK(message_loop_->BelongsToCurrentThread()); | 124 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 169 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( | 125 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
| 170 params_, output_device_id_, input_device_id_); | 126 params_, output_device_id_, input_device_id_); |
| 171 if (!stream) | 127 if (!stream) |
| 172 return false; | 128 return false; |
| 173 | 129 |
| 174 if (!stream->Open()) { | 130 if (!stream->Open()) { |
| 175 stream->Close(); | 131 stream->Close(); |
| 176 return false; | 132 return false; |
| 177 } | 133 } |
| 134 | |
| 178 idle_streams_.push_back(stream); | 135 idle_streams_.push_back(stream); |
| 179 return true; | 136 return true; |
| 180 } | 137 } |
| 181 | 138 |
| 182 // This method is called by |close_timer_|. | 139 void AudioOutputDispatcherImpl::CloseAllIdleStreams() { |
| 183 void AudioOutputDispatcherImpl::ClosePendingStreams() { | |
| 184 DCHECK(message_loop_->BelongsToCurrentThread()); | 140 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 185 while (!idle_streams_.empty()) { | 141 CloseIdleStreams(0); |
| 186 idle_streams_.back()->Close(); | 142 } |
| 187 idle_streams_.pop_back(); | 143 |
| 188 } | 144 void AudioOutputDispatcherImpl::CloseIdleStreams(int keep_alive) { |
| 145 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 146 if (idle_streams_.size() <= static_cast<size_t>(keep_alive)) | |
| 147 return; | |
| 148 for (size_t i = keep_alive; i < idle_streams_.size(); ++i) | |
| 149 idle_streams_[i]->Close(); | |
| 150 idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end()); | |
| 189 } | 151 } |
| 190 | 152 |
| 191 void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { | 153 void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { |
| 192 DCHECK(message_loop_->BelongsToCurrentThread()); | 154 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 193 while (!pausing_streams_.empty()) { | 155 CloseAllIdleStreams(); |
| 194 idle_streams_.push_back(pausing_streams_.back()); | |
| 195 pausing_streams_.pop_back(); | |
| 196 } | |
| 197 ClosePendingStreams(); | |
| 198 } | 156 } |
| 199 | 157 |
| 200 void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { | 158 void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { |
| 201 DCHECK(message_loop_->BelongsToCurrentThread()); | 159 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 202 | 160 |
| 203 // Should only be called when the dispatcher is used with fake streams which | 161 // Should only be called when the dispatcher is used with fake streams which |
| 204 // don't need to be shutdown or restarted. | 162 // don't need to be shutdown or restarted. |
| 205 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); | 163 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); |
| 206 } | 164 } |
| 207 | 165 |
| 208 } // namespace media | 166 } // namespace media |
| OLD | NEW |