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 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::StreamOpened() { |
| 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 AudioOutputStream* AudioOutputMixer::StreamStarted( |
| 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 StreamOpened(); |
| 58 if (!physical_stream_.get()) |
| 59 return NULL; |
| 60 |
| 61 bool should_start = proxies_.empty(); |
| 62 { |
| 63 base::AutoLock lock(lock_); |
| 64 proxies_.push_back(stream_proxy); |
| 65 } |
| 66 // We cannot start physical stream under the lock, |
| 67 // OnMoreData() would try acquiring it... |
| 68 if (should_start) { |
| 69 physical_stream_->SetVolume(1.0); |
| 70 physical_stream_->Start(this); |
| 71 } |
| 72 return stream_proxy; |
| 73 } |
| 74 |
| 75 void AudioOutputMixer::StreamStopped(AudioOutputStream* physical_stream, |
| 76 AudioOutputProxy* stream_proxy) { |
| 77 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 78 DCHECK_EQ(stream_proxy, physical_stream); |
| 79 |
| 80 // Because of possible deadlock we cannot stop physical stream |
| 81 // under the lock, so acquire the lock, update proxy list, release the lock, |
| 82 // and only then stop physical stream if necessary. |
| 83 bool stop_physical_stream = false; |
| 84 { |
| 85 base::AutoLock lock(lock_); |
| 86 ProxyList::iterator it = std::find(proxies_.begin(), |
| 87 proxies_.end(), |
| 88 stream_proxy); |
| 89 if (it != proxies_.end()) { |
| 90 proxies_.erase(it); |
| 91 stop_physical_stream = proxies_.empty(); |
| 92 } |
| 93 } |
| 94 if (physical_stream_.get()) { |
| 95 if (stop_physical_stream) |
| 96 physical_stream_->Stop(); |
| 97 close_timer_.Reset(); |
| 98 } |
| 99 } |
| 100 |
| 101 void AudioOutputMixer::StreamVolumeSet(AudioOutputStream* physical_stream, |
| 102 double volume) { |
| 103 } |
| 104 |
| 105 void AudioOutputMixer::StreamClosed(AudioOutputProxy* stream_proxy) { |
| 106 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 107 StreamStopped(stream_proxy, stream_proxy); |
| 108 } |
| 109 |
| 110 void AudioOutputMixer::Shutdown() { |
| 111 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 112 |
| 113 // Cancel any pending tasks to close physical stream. |
| 114 weak_this_.InvalidateWeakPtrs(); |
| 115 |
| 116 while (!proxies_.empty()) { |
| 117 StreamStopped(proxies_[0], proxies_[0]); |
| 118 } |
| 119 ClosePhysicalStream(); |
| 120 |
| 121 // No AudioOutputProxy objects should hold a reference to us when we get |
| 122 // to this stage. |
| 123 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; |
| 124 } |
| 125 |
| 126 void AudioOutputMixer::ClosePhysicalStream() { |
| 127 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 128 if (proxies_.empty() && physical_stream_.get() != NULL) |
| 129 physical_stream_.release()->Close(); |
| 130 } |
| 131 |
| 132 // AudioSourceCallback implementation. |
| 133 uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream, |
| 134 uint8* dest, |
| 135 uint32 max_size, |
| 136 AudioBuffersState buffers_state) { |
| 137 max_size = std::min(max_size, |
| 138 static_cast<uint32>(params_.GetBytesPerBuffer())); |
| 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 (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
| 153 AudioOutputProxy* stream_proxy = *it; |
| 154 int pending_bytes = std::min(stream_proxy->pending_bytes(), |
| 155 buffers_state.pending_bytes); |
| 156 // Note: there is no way we can deduce hardware_delay_bytes for the |
| 157 // particular proxy stream. Use zero instead. |
| 158 uint32 actual_size = stream_proxy->audio_source_callback()->OnMoreData( |
| 159 stream, |
| 160 actual_dest, |
| 161 max_size, |
| 162 AudioBuffersState(buffers_state.pending_bytes, 0)); |
| 163 |
| 164 // Should update pending_bytes for each proxy. |
| 165 // If stream ended, pending_bytes goes down by max_size. |
| 166 if (actual_size == 0) { |
| 167 pending_bytes -= max_size; |
| 168 stream_proxy->set_pending_bytes(std::max(pending_bytes, 0)); |
| 169 continue; |
| 170 } |
| 171 |
| 172 // Otherwise, it goes up by amount of data. It cannot exceed max amount of |
| 173 // data we can buffer, but we don't know that value. So we increment |
| 174 // pending_bytes unconditionally but adjust it before actual use (which |
| 175 // would be on a next OnMoreData() call). |
| 176 stream_proxy->set_pending_bytes(pending_bytes + actual_size); |
| 177 |
| 178 // No need to mix muted stream. |
| 179 double volume = 0.0; |
| 180 stream_proxy->GetVolume(&volume); |
| 181 if (volume == 0.0) |
| 182 continue; |
| 183 |
| 184 // Different handling for first and all subsequent streams. |
| 185 if (first_stream) { |
| 186 if (volume != 1.0) { |
| 187 media::AdjustVolume(static_cast<void*>(actual_dest), |
| 188 actual_size, |
| 189 params_.channels(), |
| 190 bytes_per_sample, |
| 191 volume); |
| 192 } |
| 193 if (actual_size < max_size) |
| 194 memset(dest + actual_size, 0, max_size - actual_size); |
| 195 first_stream = false; |
| 196 actual_dest = mixer_data_.get(); |
| 197 actual_total_size = actual_size; |
| 198 } else { |
| 199 media::MixStreams(static_cast<void*>(dest), |
| 200 static_cast<void*>(actual_dest), |
| 201 actual_size, |
| 202 bytes_per_sample, |
| 203 volume); |
| 204 actual_total_size = std::max(actual_size, actual_total_size); |
| 205 } |
| 206 } |
| 207 return actual_total_size; |
| 208 } |
| 209 |
| 210 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { |
| 211 base::AutoLock lock(lock_); |
| 212 for (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
| 213 (*it)->audio_source_callback()->OnError(stream, code); |
| 214 } |
| 215 } |
| 216 |
| 217 void AudioOutputMixer::WaitTillDataReady() { |
| 218 base::AutoLock lock(lock_); |
| 219 for (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { |
| 220 (*it)->audio_source_callback()->WaitTillDataReady(); |
| 221 } |
| 222 } |
OLD | NEW |