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_mixer.h" | 5 #include "media/audio/audio_output_mixer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | |
8 | 9 |
9 #include "base/bind.h" | 10 #include "base/bind.h" |
10 #include "base/compiler_specific.h" | 11 #include "base/compiler_specific.h" |
11 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
12 #include "base/time.h" | 13 #include "base/time.h" |
13 #include "media/audio/audio_io.h" | 14 #include "media/audio/audio_io.h" |
14 #include "media/audio/audio_output_proxy.h" | 15 #include "media/audio/audio_output_proxy.h" |
15 #include "media/audio/audio_util.h" | 16 #include "media/audio/audio_util.h" |
16 | 17 |
17 namespace media { | 18 namespace media { |
(...skipping 19 matching lines...) Expand all Loading... | |
37 | 38 |
38 if (physical_stream_.get()) | 39 if (physical_stream_.get()) |
39 return true; | 40 return true; |
40 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); | 41 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); |
41 if (!stream) | 42 if (!stream) |
42 return false; | 43 return false; |
43 if (!stream->Open()) { | 44 if (!stream->Open()) { |
44 stream->Close(); | 45 stream->Close(); |
45 return false; | 46 return false; |
46 } | 47 } |
48 buffer_data_.clear(); // Just in case. | |
47 physical_stream_.reset(stream); | 49 physical_stream_.reset(stream); |
48 close_timer_.Reset(); | 50 close_timer_.Reset(); |
49 return true; | 51 return true; |
50 } | 52 } |
51 | 53 |
52 bool AudioOutputMixer::StartStream( | 54 bool AudioOutputMixer::StartStream( |
53 AudioOutputStream::AudioSourceCallback* callback, | 55 AudioOutputStream::AudioSourceCallback* callback, |
54 AudioOutputProxy* stream_proxy) { | 56 AudioOutputProxy* stream_proxy) { |
55 DCHECK_EQ(MessageLoop::current(), message_loop_); | 57 DCHECK_EQ(MessageLoop::current(), message_loop_); |
56 | 58 |
57 // May need to re-open the physical stream if no active proxies and | 59 // May need to re-open the physical stream if no active proxies and |
58 // enough time had pass. | 60 // enough time had pass. |
59 OpenStream(); | 61 OpenStream(); |
60 if (!physical_stream_.get()) | 62 if (!physical_stream_.get()) |
61 return false; | 63 return false; |
62 | 64 |
63 double volume = 0.0; | 65 double volume = 0.0; |
64 stream_proxy->GetVolume(&volume); | 66 stream_proxy->GetVolume(&volume); |
65 bool should_start = proxies_.empty(); | 67 bool should_start = proxies_.empty(); |
66 { | 68 { |
67 base::AutoLock lock(lock_); | 69 base::AutoLock lock(lock_); |
68 ProxyData* proxy_data = &proxies_[stream_proxy]; | 70 ProxyData* proxy_data = &proxies_[stream_proxy]; |
69 proxy_data->audio_source_callback = callback; | 71 proxy_data->audio_source_callback = callback; |
70 proxy_data->volume = volume; | 72 proxy_data->volume = volume; |
71 proxy_data->pending_bytes = 0; | 73 proxy_data->pending_bytes = 0; |
74 proxy_data->prebuffering = true; | |
72 } | 75 } |
73 // We cannot start physical stream under the lock, | 76 // We cannot start physical stream under the lock, |
74 // OnMoreData() would try acquiring it... | 77 // OnMoreData() would try acquiring it... |
75 if (should_start) { | 78 if (should_start) { |
76 physical_stream_->SetVolume(1.0); | 79 physical_stream_->SetVolume(1.0); |
77 physical_stream_->Start(this); | 80 physical_stream_->Start(this); |
78 } | 81 } |
79 return true; | 82 return true; |
80 } | 83 } |
81 | 84 |
82 void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) { | 85 void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) { |
83 DCHECK_EQ(MessageLoop::current(), message_loop_); | 86 DCHECK_EQ(MessageLoop::current(), message_loop_); |
84 | 87 |
85 // Because of possible deadlock we cannot stop physical stream under the lock | 88 // Because of possible deadlock we cannot stop physical stream under the lock |
86 // (physical_stream_->Stop() can call OnError(), and it acquires the lock to | 89 // (physical_stream_->Stop() can call OnError(), and it acquires the lock to |
87 // iterate through proxies), so acquire the lock, update proxy list, release | 90 // iterate through proxies), so acquire the lock, update proxy list, release |
88 // the lock, and only then stop physical stream if necessary. | 91 // the lock, and only then stop physical stream if necessary. |
89 bool stop_physical_stream = false; | 92 bool stop_physical_stream = false; |
90 { | 93 { |
91 base::AutoLock lock(lock_); | 94 base::AutoLock lock(lock_); |
92 ProxyMap::iterator it = proxies_.find(stream_proxy); | 95 ProxyMap::iterator it = proxies_.find(stream_proxy); |
93 if (it != proxies_.end()) { | 96 if (it != proxies_.end()) { |
94 proxies_.erase(it); | 97 proxies_.erase(it); |
95 stop_physical_stream = proxies_.empty(); | 98 stop_physical_stream = proxies_.empty(); |
96 } | 99 } |
97 } | 100 } |
98 if (physical_stream_.get()) { | 101 if (physical_stream_.get()) { |
99 if (stop_physical_stream) | 102 if (stop_physical_stream) { |
100 physical_stream_->Stop(); | 103 physical_stream_->Stop(); |
104 buffer_data_.clear(); // Just in case. | |
105 } | |
101 close_timer_.Reset(); | 106 close_timer_.Reset(); |
102 } | 107 } |
103 } | 108 } |
104 | 109 |
105 void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy, | 110 void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
106 double volume) { | 111 double volume) { |
107 DCHECK_EQ(MessageLoop::current(), message_loop_); | 112 DCHECK_EQ(MessageLoop::current(), message_loop_); |
108 | 113 |
109 ProxyMap::iterator it = proxies_.find(stream_proxy); | 114 ProxyMap::iterator it = proxies_.find(stream_proxy); |
110 | 115 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 uint32 AudioOutputMixer::OnMoreData(uint8* dest, | 153 uint32 AudioOutputMixer::OnMoreData(uint8* dest, |
149 uint32 max_size, | 154 uint32 max_size, |
150 AudioBuffersState buffers_state) { | 155 AudioBuffersState buffers_state) { |
151 max_size = std::min(max_size, | 156 max_size = std::min(max_size, |
152 static_cast<uint32>(params_.GetBytesPerBuffer())); | 157 static_cast<uint32>(params_.GetBytesPerBuffer())); |
153 // TODO(enal): consider getting rid of lock as it is in time-critical code. | 158 // TODO(enal): consider getting rid of lock as it is in time-critical code. |
154 // E.g. swap |proxies_| with local variable, and merge 2 lists | 159 // E.g. swap |proxies_| with local variable, and merge 2 lists |
155 // at the end. That would speed things up but complicate stopping | 160 // at the end. That would speed things up but complicate stopping |
156 // the stream. | 161 // the stream. |
157 base::AutoLock lock(lock_); | 162 base::AutoLock lock(lock_); |
163 | |
164 // Did we see this buffer before? | |
vrk (LEFT CHROMIUM)
2012/04/26 00:54:57
For linux, I believe we will always have a unique
| |
165 // If so, get lengths of released data, and delete buffer from the buffer map. | |
166 int bytes_released = 0; | |
167 BufferMap::iterator it = buffer_data_.find(dest); | |
168 if (it != buffer_data_.end()) { | |
169 bytes_released = it->second.length; | |
170 buffer_data_.erase(it); | |
171 } | |
158 if (proxies_.empty()) | 172 if (proxies_.empty()) |
159 return 0; | 173 return 0; |
160 uint32 actual_total_size = 0; | 174 uint32 actual_total_size = 0; |
161 uint32 bytes_per_sample = params_.bits_per_sample() >> 3; | 175 uint32 bytes_per_sample = params_.bits_per_sample() >> 3; |
162 | 176 |
163 // Go through all the streams, getting data for every one of them | 177 // Go through all the streams, getting data for every one of them |
164 // and mixing it into destination. | 178 // and mixing it into destination. |
165 // Minor optimization: for the first stream we are writing data directly into | 179 // Minor optimization: for the first stream we are writing data directly into |
166 // destination. This way we don't have to mix the data when there is only one | 180 // destination. This way we don't have to mix the data when there is only one |
167 // active stream, and net win in other cases, too. | 181 // active stream, and net win in other cases, too. |
168 bool first_stream = true; | 182 bool first_stream = true; |
169 uint8* actual_dest = dest; | 183 uint8* actual_dest = dest; |
170 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | 184 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
171 ProxyData* proxy_data = &it->second; | 185 ProxyData* proxy_data = &it->second; |
172 // TODO(enal): We don't know |pending _bytes| for individual stream, and we | 186 |
173 // should give that value to individual stream's OnMoreData(). I believe it | 187 // Handle prebuffering: |
174 // can be used there to evaluate exact position of data it should return. | 188 // * If we already saw this buffer for this proxy, prebuffering is done, |
175 // Current code "sorta works" if everything works perfectly, but would have | 189 // proxy now is in the steady state. |
176 // problems if some of the buffers are only partially filled -- we don't | 190 // * Otherwise add buffer to the set for this proxy. |
177 // know how how much data was in the buffer OS returned to us, so we cannot | 191 if (proxy_data->prebuffering) { |
178 // correctly calculate new value. If we know number of buffers we can solve | 192 std::set<void*>::iterator it = proxy_data->buffers.find(dest); |
179 // the problem by storing not one value but vector of them. | 193 if (it != proxy_data->buffers.end()) { |
180 int pending_bytes = std::min(proxy_data->pending_bytes, | 194 // Release memory used by set, we don't need it anymore. |
181 buffers_state.pending_bytes); | 195 std::set<void*> empty; |
196 empty.swap(proxy_data->buffers); | |
197 proxy_data->prebuffering = false; | |
198 } else { | |
199 proxy_data->buffers.insert(dest); | |
200 } | |
201 } | |
202 if (!proxy_data->prebuffering) { | |
203 proxy_data->pending_bytes -= bytes_released; | |
204 DCHECK_GE(proxy_data->pending_bytes, 0); | |
205 } | |
206 | |
182 // Note: there is no way we can deduce hardware_delay_bytes for the | 207 // Note: there is no way we can deduce hardware_delay_bytes for the |
183 // particular proxy stream. Use zero instead. | 208 // particular proxy stream. Use zero instead. |
184 uint32 actual_size = proxy_data->audio_source_callback->OnMoreData( | 209 uint32 actual_size = proxy_data->audio_source_callback->OnMoreData( |
185 actual_dest, | 210 actual_dest, |
186 max_size, | 211 max_size, |
187 AudioBuffersState(pending_bytes, 0)); | 212 AudioBuffersState(proxy_data->pending_bytes, 0)); |
188 | 213 if (actual_size == 0) |
189 // Should update pending_bytes for each proxy. | |
190 // If stream ended, pending_bytes goes down by max_size. | |
191 if (actual_size == 0) { | |
192 pending_bytes -= max_size; | |
193 proxy_data->pending_bytes = std::max(pending_bytes, 0); | |
194 continue; | 214 continue; |
195 } | |
196 | |
197 // Otherwise, it goes up by amount of data. It cannot exceed max amount of | |
198 // data we can buffer, but we don't know that value. So we increment | |
199 // pending_bytes unconditionally but adjust it before actual use (which | |
200 // would be on a next OnMoreData() call). | |
201 proxy_data->pending_bytes = pending_bytes + actual_size; | |
202 | 215 |
203 // No need to mix muted stream. | 216 // No need to mix muted stream. |
204 double volume = proxy_data->volume; | 217 double volume = proxy_data->volume; |
205 if (volume == 0.0) | 218 if (volume == 0.0) |
206 continue; | 219 continue; |
207 | 220 |
208 // Different handling for first and all subsequent streams. | 221 // Different handling for first and all subsequent streams. |
209 if (first_stream) { | 222 if (first_stream) { |
210 if (volume != 1.0) { | 223 if (volume != 1.0) { |
211 media::AdjustVolume(actual_dest, | 224 media::AdjustVolume(actual_dest, |
212 actual_size, | 225 actual_size, |
213 params_.channels(), | 226 params_.channels(), |
214 bytes_per_sample, | 227 bytes_per_sample, |
215 volume); | 228 volume); |
216 } | 229 } |
217 if (actual_size < max_size) | 230 if (actual_size < max_size) |
218 memset(dest + actual_size, 0, max_size - actual_size); | 231 memset(dest + actual_size, 0, max_size - actual_size); |
219 first_stream = false; | 232 first_stream = false; |
220 actual_dest = mixer_data_.get(); | 233 actual_dest = mixer_data_.get(); |
221 actual_total_size = actual_size; | 234 actual_total_size = actual_size; |
222 } else { | 235 } else { |
223 media::MixStreams(dest, | 236 media::MixStreams(dest, |
224 actual_dest, | 237 actual_dest, |
225 actual_size, | 238 actual_size, |
226 bytes_per_sample, | 239 bytes_per_sample, |
227 volume); | 240 volume); |
228 actual_total_size = std::max(actual_size, actual_total_size); | 241 actual_total_size = std::max(actual_size, actual_total_size); |
229 } | 242 } |
230 } | 243 } |
244 | |
245 // Now go through all proxies once again and increase pending_bytes | |
246 // for each proxy. Could not do it earlier because we did not know | |
247 // actual_total_size. | |
248 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
249 it->second.pending_bytes += actual_total_size; | |
250 } | |
251 | |
252 buffer_data_.insert( | |
253 std::pair<void*, BufferData>(dest, BufferData(actual_total_size))); | |
254 DCHECK_LE(buffer_data_.size(), 5u); | |
231 return actual_total_size; | 255 return actual_total_size; |
232 } | 256 } |
233 | 257 |
234 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { | 258 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { |
235 base::AutoLock lock(lock_); | 259 base::AutoLock lock(lock_); |
236 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | 260 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
237 it->second.audio_source_callback->OnError(it->first, code); | 261 it->second.audio_source_callback->OnError(it->first, code); |
238 } | 262 } |
239 } | 263 } |
240 | 264 |
241 void AudioOutputMixer::WaitTillDataReady() { | 265 void AudioOutputMixer::WaitTillDataReady() { |
242 base::AutoLock lock(lock_); | 266 base::AutoLock lock(lock_); |
243 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | 267 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
244 it->second.audio_source_callback->WaitTillDataReady(); | 268 it->second.audio_source_callback->WaitTillDataReady(); |
245 } | 269 } |
246 } | 270 } |
247 | 271 |
248 } // namespace media | 272 } // namespace media |
OLD | NEW |