Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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_mixer.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/message_loop.h" | |
| 12 #include "base/time.h" | |
| 13 #include "media/audio/audio_io.h" | |
| 14 #include "media/audio/audio_output_proxy.h" | |
| 15 #include "media/audio/audio_util.h" | |
| 16 | |
| 17 AudioOutputMixer::AudioOutputMixer(AudioManager* audio_manager, | |
| 18 const AudioParameters& params, | |
| 19 const base::TimeDelta& close_delay) | |
| 20 : AudioOutputDispatcher(audio_manager, params), | |
| 21 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), | |
| 22 close_timer_(FROM_HERE, | |
| 23 close_delay, | |
| 24 weak_this_.GetWeakPtr(), | |
| 25 &AudioOutputMixer::ClosePhysicalStream) { | |
| 26 // TODO(enal): align data. | |
| 27 mixer_data_.reset(new uint8[params_.GetBytesPerBuffer()]); | |
| 28 } | |
| 29 | |
| 30 AudioOutputMixer::~AudioOutputMixer() { | |
| 31 } | |
| 32 | |
| 33 bool AudioOutputMixer::OpenStream() { | |
| 34 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 35 | |
| 36 if (physical_stream_.get()) | |
| 37 return true; | |
| 38 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); | |
| 39 if (!stream) | |
| 40 return false; | |
| 41 if (!stream->Open()) { | |
| 42 stream->Close(); | |
| 43 return false; | |
| 44 } | |
| 45 physical_stream_.reset(stream); | |
| 46 close_timer_.Reset(); | |
| 47 return true; | |
| 48 } | |
| 49 | |
| 50 bool AudioOutputMixer::StartStream( | |
| 51 AudioOutputStream::AudioSourceCallback* callback, | |
| 52 AudioOutputProxy* stream_proxy) { | |
| 53 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 54 | |
| 55 // May need to re-open the physical stream if no active proxies and | |
| 56 // enough time had pass. | |
| 57 OpenStream(); | |
| 58 if (!physical_stream_.get()) | |
| 59 return false; | |
| 60 | |
| 61 bool should_start = proxies_.empty(); | |
| 62 { | |
| 63 base::AutoLock lock(lock_); | |
| 64 proxies_[stream_proxy].pending_bytes = 0; | |
| 65 } | |
| 66 // We cannot start physical stream under the lock, | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
I don't understand this problem. Start() should no
enal1
2012/04/16 22:01:35
Not right. Starting stream will call OnMoreData()
vrk (LEFT CHROMIUM)
2012/04/18 14:57:36
Ah, my mistake! I was only looking at the linux im
| |
| 67 // OnMoreData() would try acquiring it... | |
| 68 if (should_start) { | |
| 69 physical_stream_->SetVolume(1.0); | |
| 70 physical_stream_->Start(this); | |
| 71 } | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) { | |
| 76 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 77 | |
| 78 // Because of possible deadlock we cannot stop physical stream | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
Again, what is the deadlock you're worried about h
enal1
2012/04/16 22:01:35
Updated comment.
| |
| 79 // under the lock, so acquire the lock, update proxy list, release the lock, | |
| 80 // and only then stop physical stream if necessary. | |
| 81 bool stop_physical_stream = false; | |
| 82 { | |
| 83 base::AutoLock lock(lock_); | |
| 84 ProxyMap::iterator it = proxies_.find(stream_proxy); | |
| 85 if (it != proxies_.end()) { | |
| 86 proxies_.erase(it); | |
| 87 stop_physical_stream = proxies_.empty(); | |
| 88 } | |
| 89 } | |
| 90 if (physical_stream_.get()) { | |
| 91 if (stop_physical_stream) | |
| 92 physical_stream_->Stop(); | |
| 93 close_timer_.Reset(); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy, | |
| 98 double volume) { | |
| 99 } | |
| 100 | |
| 101 void AudioOutputMixer::CloseStream(AudioOutputProxy* stream_proxy) { | |
| 102 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 103 StopStream(stream_proxy); | |
| 104 } | |
| 105 | |
| 106 void AudioOutputMixer::Shutdown() { | |
| 107 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 108 | |
| 109 // Cancel any pending tasks to close physical stream. | |
| 110 weak_this_.InvalidateWeakPtrs(); | |
| 111 | |
| 112 while (!proxies_.empty()) { | |
| 113 StopStream(proxies_.begin()->first); | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
nit: I don't like this because it assumes StopStre
enal1
2012/04/16 22:01:35
Updated comment in the header file, now it says th
| |
| 114 } | |
| 115 ClosePhysicalStream(); | |
| 116 | |
| 117 // No AudioOutputProxy objects should hold a reference to us when we get | |
| 118 // to this stage. | |
| 119 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | |
| 120 } | |
| 121 | |
| 122 void AudioOutputMixer::ClosePhysicalStream() { | |
| 123 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 124 if (proxies_.empty() && physical_stream_.get() != NULL) | |
| 125 physical_stream_.release()->Close(); | |
| 126 } | |
| 127 | |
| 128 // AudioSourceCallback implementation. | |
| 129 uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream, | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
What is the first parameter of OnMoreData() used f
enal1
2012/04/16 22:01:35
It is not used, and I am planning to get rid of it
| |
| 130 uint8* dest, | |
| 131 uint32 max_size, | |
| 132 AudioBuffersState buffers_state) { | |
| 133 max_size = std::min(max_size, | |
| 134 static_cast<uint32>(params_.GetBytesPerBuffer())); | |
| 135 // TODO(enal): consider getting rid of lock as it is in time-critical code. | |
| 136 // E.g. swap |proxies_| with local variable, and merge 2 lists | |
| 137 // at the end. That would speed things up but complicate stopping | |
| 138 // the stream. | |
| 139 base::AutoLock lock(lock_); | |
| 140 if (proxies_.empty()) | |
| 141 return 0; | |
| 142 uint32 actual_total_size = 0; | |
| 143 uint32 bytes_per_sample = params_.bits_per_sample() >> 3; | |
| 144 | |
| 145 // Go through all the streams, getting data for every one of them | |
| 146 // and mixing it into destination. | |
| 147 // Minor optimization: for the first stream we are writing data directly into | |
| 148 // destination. This way we don't have to mix the data when there is only one | |
| 149 // active stream, and net win in other cases, too. | |
| 150 bool first_stream = true; | |
| 151 uint8* actual_dest = dest; | |
| 152 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
| 153 AudioOutputProxy* stream_proxy = it->first; | |
| 154 ProxyData* proxy_data = &it->second; | |
| 155 // TODO(enal): We don't know |pending _bytes| for individual stream, and we | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
The pending bytes problem is a tricky one indeed..
enal1
2012/04/16 22:01:35
Yes, I checked that.
| |
| 156 // should givw that value to individual stream's OnMoreData(). I believe it | |
| 157 // can be used there to evaluate exact position of data it should return. | |
| 158 // Current code "sorta works" if everything works perfectly, but would have | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
(sorry in advance for this huge comment, but I can
enal1
2012/04/16 22:01:35
We always feed entire buffer to the OS, so numbers
vrk (LEFT CHROMIUM)
2012/04/18 14:57:36
Thanks for the explanation! Yeah, sounds like some
| |
| 159 // problems if some of the buffers are only partially filled -- we don't | |
| 160 // know how how much data was in the buffer OS returned to us, so we cannot | |
| 161 // correctly calculate new value. If we know number of buffers we can solve | |
| 162 // the problem by storing not one value but vector of them. | |
| 163 int pending_bytes = std::min(proxy_data->pending_bytes, | |
| 164 buffers_state.pending_bytes); | |
| 165 // Note: there is no way we can deduce hardware_delay_bytes for the | |
| 166 // particular proxy stream. Use zero instead. | |
| 167 uint32 actual_size = stream_proxy->OnMoreData( | |
| 168 stream_proxy, | |
| 169 actual_dest, | |
| 170 max_size, | |
| 171 AudioBuffersState(pending_bytes, 0)); | |
| 172 | |
| 173 // Should update pending_bytes for each proxy. | |
| 174 // If stream ended, pending_bytes goes down by max_size. | |
|
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
After a stream is ended, when does it get removed
enal1
2012/04/16 22:01:35
StopStream() does that.
| |
| 175 if (actual_size == 0) { | |
| 176 pending_bytes -= max_size; | |
| 177 proxy_data->pending_bytes = std::max(pending_bytes, 0); | |
| 178 continue; | |
| 179 } | |
| 180 | |
| 181 // Otherwise, it goes up by amount of data. It cannot exceed max amount of | |
| 182 // data we can buffer, but we don't know that value. So we increment | |
| 183 // pending_bytes unconditionally but adjust it before actual use (which | |
| 184 // would be on a next OnMoreData() call). | |
| 185 proxy_data->pending_bytes = pending_bytes + actual_size; | |
| 186 | |
| 187 // No need to mix muted stream. | |
| 188 double volume = 0.0; | |
| 189 stream_proxy->GetVolume(&volume); | |
| 190 if (volume == 0.0) | |
| 191 continue; | |
| 192 | |
| 193 // Different handling for first and all subsequent streams. | |
| 194 if (first_stream) { | |
| 195 if (volume != 1.0) { | |
| 196 media::AdjustVolume(actual_dest, | |
| 197 actual_size, | |
| 198 params_.channels(), | |
| 199 bytes_per_sample, | |
| 200 volume); | |
| 201 } | |
| 202 if (actual_size < max_size) | |
| 203 memset(dest + actual_size, 0, max_size - actual_size); | |
| 204 first_stream = false; | |
| 205 actual_dest = mixer_data_.get(); | |
| 206 actual_total_size = actual_size; | |
| 207 } else { | |
| 208 media::MixStreams(dest, | |
| 209 actual_dest, | |
| 210 actual_size, | |
| 211 bytes_per_sample, | |
| 212 volume); | |
| 213 actual_total_size = std::max(actual_size, actual_total_size); | |
| 214 } | |
| 215 } | |
| 216 return actual_total_size; | |
| 217 } | |
| 218 | |
| 219 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { | |
| 220 base::AutoLock lock(lock_); | |
| 221 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
| 222 it->first->OnError(it->first, code); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 void AudioOutputMixer::WaitTillDataReady() { | |
| 227 base::AutoLock lock(lock_); | |
| 228 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
| 229 it->first->WaitTillDataReady(); | |
| 230 } | |
| 231 } | |
| OLD | NEW |