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