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); | |
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(std::max(idle_proxies_, static_cast<size_t>(1))); |
scherkus (not reviewing)
2013/12/09 23:48:03
I forget ... does 1u not work here?
DaleCurtis
2013/12/10 00:02:31
I seem to remember some of the bots (windows proba
| |
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(size_t keep_alive) { |
scherkus (not reviewing)
2013/12/09 23:48:03
food for thought ... this seems like a strange API
DaleCurtis
2013/12/10 00:02:31
Just having CloseAllIdleStreams() isn't sufficient
| |
145 DCHECK(message_loop_->BelongsToCurrentThread()); | |
146 if (idle_streams_.size() <= 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 |