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