Chromium Code Reviews| 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_resampler.h" | 5 #include "media/audio/audio_output_resampler.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| 11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 13 #include "base/time.h" | 13 #include "base/time.h" |
| 14 #include "media/audio/audio_io.h" | 14 #include "media/audio/audio_io.h" |
| 15 #include "media/audio/audio_output_dispatcher_impl.h" | 15 #include "media/audio/audio_output_dispatcher_impl.h" |
| 16 #include "media/audio/audio_output_proxy.h" | 16 #include "media/audio/audio_output_proxy.h" |
| 17 #include "media/audio/audio_util.h" | 17 #include "media/audio/audio_util.h" |
| 18 #include "media/audio/sample_rates.h" | 18 #include "media/audio/sample_rates.h" |
| 19 #include "media/base/audio_pull_fifo.h" | 19 #include "media/base/audio_transform.h" |
| 20 #include "media/base/channel_mixer.h" | |
| 21 #include "media/base/limits.h" | 20 #include "media/base/limits.h" |
| 22 #include "media/base/media_switches.h" | 21 #include "media/base/media_switches.h" |
| 23 #include "media/base/multi_channel_resampler.h" | |
| 24 | 22 |
| 25 namespace media { | 23 namespace media { |
| 26 | 24 |
| 27 class OnMoreDataResampler : public AudioOutputStream::AudioSourceCallback { | 25 class OnMoreDataTransform |
| 26 : public AudioOutputStream::AudioSourceCallback, | |
| 27 public AudioTransform::AudioTransformInput { | |
| 28 public: | 28 public: |
| 29 OnMoreDataResampler(double io_ratio, | 29 OnMoreDataTransform(const AudioParameters& input_params, |
| 30 const AudioParameters& input_params, | |
| 31 const AudioParameters& output_params); | 30 const AudioParameters& output_params); |
| 32 virtual ~OnMoreDataResampler(); | 31 virtual ~OnMoreDataTransform(); |
| 33 | 32 |
| 34 // AudioSourceCallback interface. | 33 // AudioSourceCallback interface. |
| 35 virtual int OnMoreData(AudioBus* dest, | 34 virtual int OnMoreData(AudioBus* dest, |
| 36 AudioBuffersState buffers_state) OVERRIDE; | 35 AudioBuffersState buffers_state) OVERRIDE; |
| 37 virtual int OnMoreIOData(AudioBus* source, | 36 virtual int OnMoreIOData(AudioBus* source, |
| 38 AudioBus* dest, | 37 AudioBus* dest, |
| 39 AudioBuffersState buffers_state) OVERRIDE; | 38 AudioBuffersState buffers_state) OVERRIDE; |
| 40 virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; | 39 virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; |
| 41 virtual void WaitTillDataReady() OVERRIDE; | 40 virtual void WaitTillDataReady() OVERRIDE; |
| 42 | 41 |
| 43 // Sets |source_callback_|. If this is not a new object, then Stop() must be | 42 // Sets |source_callback_|. If this is not a new object, then Stop() must be |
| 44 // called before Start(). | 43 // called before Start(). |
| 45 void Start(AudioOutputStream::AudioSourceCallback* callback); | 44 void Start(AudioOutputStream::AudioSourceCallback* callback); |
| 46 | 45 |
| 47 // Clears |source_callback_| and flushes the resampler. | 46 // Clears |source_callback_| and flushes the resampler. |
| 48 void Stop(); | 47 void Stop(); |
| 49 | 48 |
| 50 private: | 49 private: |
| 51 // Called by MultiChannelResampler when more data is necessary. | 50 // AudioTransform::ProvideAudioTransformInput implementation. |
|
Chris Rogers
2012/11/14 23:50:49
class name is wrong
DaleCurtis
2012/11/16 23:51:05
Done.
| |
| 52 void ProvideInput(AudioBus* audio_bus); | 51 virtual float ProvideAudioTransformInput( |
| 53 | 52 AudioBus* audio_bus, base::TimeDelta buffer_delay) OVERRIDE; |
| 54 // Called by AudioPullFifo when more data is necessary. Requires | |
| 55 // |source_lock_| to have been acquired. | |
| 56 void SourceCallback_Locked(AudioBus* audio_bus); | |
| 57 | |
| 58 // Passes through |source| to the |source_callback_| OnMoreIOData() call. | |
| 59 void SourceIOCallback_Locked(AudioBus* source, AudioBus* dest); | |
| 60 | 53 |
| 61 // Ratio of input bytes to output bytes used to correct playback delay with | 54 // Ratio of input bytes to output bytes used to correct playback delay with |
| 62 // regard to buffering and resampling. | 55 // regard to buffering and resampling. |
| 63 double io_ratio_; | 56 double io_ratio_; |
| 64 | 57 |
| 65 // Source callback and associated lock. | 58 // Source callback and associated lock. |
| 66 base::Lock source_lock_; | 59 base::Lock source_lock_; |
| 67 AudioOutputStream::AudioSourceCallback* source_callback_; | 60 AudioOutputStream::AudioSourceCallback* source_callback_; |
| 68 | 61 |
| 62 // |source| passed to OnMoreIOData() which should be passed downstream. | |
| 63 AudioBus* source_bus_; | |
| 64 | |
| 69 // Last AudioBuffersState object received via OnMoreData(), used to correct | 65 // Last AudioBuffersState object received via OnMoreData(), used to correct |
| 70 // playback delay by ProvideInput() and passed on to |source_callback_|. | 66 // playback delay by ProvideInput() and passed on to |source_callback_|. |
| 71 AudioBuffersState current_buffers_state_; | 67 AudioBuffersState current_buffers_state_; |
| 72 | 68 |
| 73 // Total number of bytes (in terms of output parameters) stored in resampler | 69 int input_bytes_per_second_; |
|
miu
2012/11/12 20:51:59
nit: could be "const int"
DaleCurtis
2012/11/16 23:51:05
Done.
| |
| 74 // or FIFO buffers which have not been sent to the audio device. | |
| 75 int outstanding_audio_bytes_; | |
| 76 | 70 |
| 77 // Used to buffer data between the client and the output device in cases where | 71 // Handles resampling, rebuffering, and channel mixing between input and |
|
Chris Rogers
2012/11/14 23:50:49
rebuffering? How about just "buffering"
DaleCurtis
2012/11/16 23:51:05
Done.
| |
| 78 // the client buffer size is not the same as the output device buffer size. | 72 // output parameters. |
| 79 // Bound to SourceCallback_Locked() so must only be used when |source_lock_| | 73 AudioTransform audio_transform_; |
| 80 // has already been acquired. | |
| 81 scoped_ptr<AudioPullFifo> audio_fifo_; | |
| 82 | 74 |
| 83 // Handles resampling. | 75 DISALLOW_COPY_AND_ASSIGN(OnMoreDataTransform); |
| 84 scoped_ptr<MultiChannelResampler> resampler_; | |
| 85 | |
| 86 // Handles channel transforms. |unmixed_audio_| is a temporary destination | |
| 87 // for audio data before it goes into the channel mixer. | |
| 88 scoped_ptr<ChannelMixer> channel_mixer_; | |
| 89 scoped_ptr<AudioBus> unmixed_audio_; | |
| 90 | |
| 91 int output_bytes_per_frame_; | |
| 92 int input_bytes_per_frame_; | |
| 93 | |
| 94 // Since resampling is expensive, figure out if we should downmix channels | |
| 95 // before resampling. | |
| 96 bool downmix_early_; | |
| 97 | |
| 98 DISALLOW_COPY_AND_ASSIGN(OnMoreDataResampler); | |
| 99 }; | 76 }; |
| 100 | 77 |
| 101 // Record UMA statistics for hardware output configuration. | 78 // Record UMA statistics for hardware output configuration. |
| 102 static void RecordStats(const AudioParameters& output_params) { | 79 static void RecordStats(const AudioParameters& output_params) { |
| 103 UMA_HISTOGRAM_ENUMERATION( | 80 UMA_HISTOGRAM_ENUMERATION( |
| 104 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), | 81 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), |
| 105 limits::kMaxBitsPerSample); | 82 limits::kMaxBitsPerSample); |
| 106 UMA_HISTOGRAM_ENUMERATION( | 83 UMA_HISTOGRAM_ENUMERATION( |
| 107 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), | 84 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), |
| 108 CHANNEL_LAYOUT_MAX); | 85 CHANNEL_LAYOUT_MAX); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 163 AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(), | 140 AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(), |
| 164 input_params.sample_rate(), input_params.bits_per_sample(), | 141 input_params.sample_rate(), input_params.bits_per_sample(), |
| 165 frames_per_buffer); | 142 frames_per_buffer); |
| 166 } | 143 } |
| 167 | 144 |
| 168 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, | 145 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| 169 const AudioParameters& input_params, | 146 const AudioParameters& input_params, |
| 170 const AudioParameters& output_params, | 147 const AudioParameters& output_params, |
| 171 const base::TimeDelta& close_delay) | 148 const base::TimeDelta& close_delay) |
| 172 : AudioOutputDispatcher(audio_manager, input_params), | 149 : AudioOutputDispatcher(audio_manager, input_params), |
| 173 io_ratio_(1), | |
| 174 close_delay_(close_delay), | 150 close_delay_(close_delay), |
| 175 output_params_(output_params), | 151 output_params_(output_params), |
| 176 streams_opened_(false) { | 152 streams_opened_(false) { |
| 177 DCHECK(input_params.IsValid()); | 153 DCHECK(input_params.IsValid()); |
| 178 DCHECK(output_params.IsValid()); | 154 DCHECK(output_params.IsValid()); |
| 179 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); | 155 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); |
| 180 | 156 |
| 181 // Record UMA statistics for the hardware configuration. | 157 // Record UMA statistics for the hardware configuration. |
| 182 RecordStats(output_params); | 158 RecordStats(output_params); |
| 183 | 159 |
| 184 Initialize(); | 160 Initialize(); |
| 185 } | 161 } |
| 186 | 162 |
| 187 AudioOutputResampler::~AudioOutputResampler() { | 163 AudioOutputResampler::~AudioOutputResampler() { |
| 188 DCHECK(callbacks_.empty()); | 164 DCHECK(callbacks_.empty()); |
| 189 } | 165 } |
| 190 | 166 |
| 191 void AudioOutputResampler::Initialize() { | 167 void AudioOutputResampler::Initialize() { |
| 192 DCHECK(!streams_opened_); | 168 DCHECK(!streams_opened_); |
| 193 DCHECK(callbacks_.empty()); | 169 DCHECK(callbacks_.empty()); |
| 194 | |
| 195 io_ratio_ = 1; | |
| 196 | |
| 197 // Only resample or rebuffer if the input parameters don't match the output | |
| 198 // parameters to avoid any unnecessary work. | |
| 199 if (params_.channels() != output_params_.channels() || | |
| 200 params_.sample_rate() != output_params_.sample_rate() || | |
| 201 params_.bits_per_sample() != output_params_.bits_per_sample() || | |
| 202 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { | |
| 203 if (params_.sample_rate() != output_params_.sample_rate()) { | |
| 204 double io_sample_rate_ratio = params_.sample_rate() / | |
| 205 static_cast<double>(output_params_.sample_rate()); | |
| 206 // Include the I/O resampling ratio in our global I/O ratio. | |
| 207 io_ratio_ *= io_sample_rate_ratio; | |
| 208 } | |
| 209 | |
| 210 // Include bits per channel differences. | |
| 211 io_ratio_ *= static_cast<double>(params_.bits_per_sample()) / | |
| 212 output_params_.bits_per_sample(); | |
| 213 | |
| 214 // Include channel count differences. | |
| 215 io_ratio_ *= static_cast<double>(params_.channels()) / | |
| 216 output_params_.channels(); | |
| 217 | |
| 218 DVLOG(1) << "I/O ratio is " << io_ratio_; | |
| 219 } else { | |
| 220 DVLOG(1) << "Input and output params are the same; in pass-through mode."; | |
| 221 } | |
| 222 | |
| 223 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | |
| 224 // we've stabilized the issues there. | |
| 225 dispatcher_ = new AudioOutputDispatcherImpl( | 170 dispatcher_ = new AudioOutputDispatcherImpl( |
| 226 audio_manager_, output_params_, close_delay_); | 171 audio_manager_, output_params_, close_delay_); |
| 227 } | 172 } |
| 228 | 173 |
| 229 bool AudioOutputResampler::OpenStream() { | 174 bool AudioOutputResampler::OpenStream() { |
| 230 DCHECK_EQ(MessageLoop::current(), message_loop_); | 175 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 231 | 176 |
| 232 if (dispatcher_->OpenStream()) { | 177 if (dispatcher_->OpenStream()) { |
| 233 // Only record the UMA statistic if we didn't fallback during construction | 178 // Only record the UMA statistic if we didn't fallback during construction |
| 234 // and only for the first stream we open. | 179 // and only for the first stream we open. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 | 212 |
| 268 // Retry, if this fails, there's nothing left to do but report the error back. | 213 // Retry, if this fails, there's nothing left to do but report the error back. |
| 269 return dispatcher_->OpenStream(); | 214 return dispatcher_->OpenStream(); |
| 270 } | 215 } |
| 271 | 216 |
| 272 bool AudioOutputResampler::StartStream( | 217 bool AudioOutputResampler::StartStream( |
| 273 AudioOutputStream::AudioSourceCallback* callback, | 218 AudioOutputStream::AudioSourceCallback* callback, |
| 274 AudioOutputProxy* stream_proxy) { | 219 AudioOutputProxy* stream_proxy) { |
| 275 DCHECK_EQ(MessageLoop::current(), message_loop_); | 220 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 276 | 221 |
| 277 OnMoreDataResampler* resampler_callback = NULL; | 222 OnMoreDataTransform* resampler_callback = NULL; |
| 278 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 223 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 279 if (it == callbacks_.end()) { | 224 if (it == callbacks_.end()) { |
| 280 resampler_callback = new OnMoreDataResampler( | 225 resampler_callback = new OnMoreDataTransform(params_, output_params_); |
| 281 io_ratio_, params_, output_params_); | |
| 282 callbacks_[stream_proxy] = resampler_callback; | 226 callbacks_[stream_proxy] = resampler_callback; |
| 283 } else { | 227 } else { |
| 284 resampler_callback = it->second; | 228 resampler_callback = it->second; |
| 285 } | 229 } |
| 286 resampler_callback->Start(callback); | 230 resampler_callback->Start(callback); |
| 287 return dispatcher_->StartStream(resampler_callback, stream_proxy); | 231 return dispatcher_->StartStream(resampler_callback, stream_proxy); |
| 288 } | 232 } |
| 289 | 233 |
| 290 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, | 234 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
| 291 double volume) { | 235 double volume) { |
| 292 DCHECK_EQ(MessageLoop::current(), message_loop_); | 236 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 237 // TODO(dalecurtis): Remove this and instead perform volume adjustment during | |
| 238 // the OnMoreDataTransform::ProvideInput_Locked() call. | |
|
Chris Rogers
2012/11/14 23:50:49
Not that we need to change anything for this CL, b
DaleCurtis
2012/11/15 00:30:59
Hmmm, is <audio> really the only person using stre
| |
| 293 dispatcher_->StreamVolumeSet(stream_proxy, volume); | 239 dispatcher_->StreamVolumeSet(stream_proxy, volume); |
| 294 } | 240 } |
| 295 | 241 |
| 296 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { | 242 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { |
| 297 DCHECK_EQ(MessageLoop::current(), message_loop_); | 243 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 298 dispatcher_->StopStream(stream_proxy); | 244 dispatcher_->StopStream(stream_proxy); |
| 299 | 245 |
| 300 // Now that StopStream() has completed the underlying physical stream should | 246 // Now that StopStream() has completed the underlying physical stream should |
| 301 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the | 247 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the |
| 302 // OnMoreDataResampler. | 248 // OnMoreDataTransform. |
| 303 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 249 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 304 if (it != callbacks_.end()) | 250 if (it != callbacks_.end()) |
| 305 it->second->Stop(); | 251 it->second->Stop(); |
| 306 } | 252 } |
| 307 | 253 |
| 308 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { | 254 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { |
| 309 DCHECK_EQ(MessageLoop::current(), message_loop_); | 255 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 310 dispatcher_->CloseStream(stream_proxy); | 256 dispatcher_->CloseStream(stream_proxy); |
| 311 | 257 |
| 312 // We assume that StopStream() is always called prior to CloseStream(), so | 258 // We assume that StopStream() is always called prior to CloseStream(), so |
| 313 // that it is safe to delete the OnMoreDataResampler here. | 259 // that it is safe to delete the OnMoreDataTransform here. |
| 314 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 260 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 315 if (it != callbacks_.end()) { | 261 if (it != callbacks_.end()) { |
| 316 delete it->second; | 262 delete it->second; |
| 317 callbacks_.erase(it); | 263 callbacks_.erase(it); |
| 318 } | 264 } |
| 319 } | 265 } |
| 320 | 266 |
| 321 void AudioOutputResampler::Shutdown() { | 267 void AudioOutputResampler::Shutdown() { |
| 322 DCHECK_EQ(MessageLoop::current(), message_loop_); | 268 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 323 | 269 |
| 324 // No AudioOutputProxy objects should hold a reference to us when we get | 270 // No AudioOutputProxy objects should hold a reference to us when we get |
| 325 // to this stage. | 271 // to this stage. |
| 326 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | 272 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; |
| 327 | 273 |
| 328 dispatcher_->Shutdown(); | 274 dispatcher_->Shutdown(); |
| 329 DCHECK(callbacks_.empty()); | 275 DCHECK(callbacks_.empty()); |
| 330 } | 276 } |
| 331 | 277 |
| 332 OnMoreDataResampler::OnMoreDataResampler( | 278 OnMoreDataTransform::OnMoreDataTransform(const AudioParameters& input_params, |
| 333 double io_ratio, const AudioParameters& input_params, | 279 const AudioParameters& output_params) |
| 334 const AudioParameters& output_params) | 280 : source_callback_(NULL), |
| 335 : io_ratio_(io_ratio), | 281 source_bus_(NULL), |
| 336 source_callback_(NULL), | 282 input_bytes_per_second_(input_params.GetBytesPerSecond()), |
| 337 outstanding_audio_bytes_(0), | 283 audio_transform_(input_params, output_params) { |
| 338 output_bytes_per_frame_(output_params.GetBytesPerFrame()), | 284 io_ratio_ = static_cast<double>( |
|
miu
2012/11/12 20:51:59
The indentation here makes it look like you're cas
DaleCurtis
2012/11/16 23:51:05
Done.
| |
| 339 input_bytes_per_frame_(input_params.GetBytesPerFrame()), | 285 input_params.sample_rate() * input_params.GetBytesPerFrame()) / |
| 340 downmix_early_(false) { | 286 (output_params.sample_rate() * output_params.GetBytesPerFrame()); |
|
Chris Rogers
2012/11/14 23:50:49
Not that we have to change this right now, but can
DaleCurtis
2012/11/15 00:30:59
Yes I think that's a good idea, we need to stop pa
| |
| 341 // Handle different input and output channel layouts. | |
| 342 if (input_params.channel_layout() != output_params.channel_layout()) { | |
| 343 DVLOG(1) << "Remixing channel layout from " << input_params.channel_layout() | |
| 344 << " to " << output_params.channel_layout() << "; from " | |
| 345 << input_params.channels() << " channels to " | |
| 346 << output_params.channels() << " channels."; | |
| 347 channel_mixer_.reset(new ChannelMixer( | |
| 348 input_params.channel_layout(), output_params.channel_layout())); | |
| 349 | |
| 350 // Pare off data as early as we can for efficiency. | |
| 351 downmix_early_ = input_params.channels() > output_params.channels(); | |
| 352 if (downmix_early_) { | |
| 353 DVLOG(1) << "Remixing channel layout prior to resampling."; | |
| 354 // If we're downmixing early we need a temporary AudioBus which matches | |
| 355 // the the input channel count and input frame size since we're passing | |
| 356 // |unmixed_audio_| directly to the |source_callback_|. | |
| 357 unmixed_audio_ = AudioBus::Create(input_params); | |
| 358 } else { | |
| 359 // Instead, if we're not downmixing early we need a temporary AudioBus | |
| 360 // which matches the input channel count but uses the output frame size | |
| 361 // since we'll mix into the AudioBus from the output stream. | |
| 362 unmixed_audio_ = AudioBus::Create( | |
| 363 input_params.channels(), output_params.frames_per_buffer()); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 // Only resample if necessary since it's expensive. | |
| 368 if (input_params.sample_rate() != output_params.sample_rate()) { | |
| 369 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " | |
| 370 << output_params.sample_rate(); | |
| 371 double io_sample_rate_ratio = input_params.sample_rate() / | |
| 372 static_cast<double>(output_params.sample_rate()); | |
| 373 resampler_.reset(new MultiChannelResampler( | |
| 374 downmix_early_ ? output_params.channels() : | |
| 375 input_params.channels(), | |
| 376 io_sample_rate_ratio, base::Bind( | |
| 377 &OnMoreDataResampler::ProvideInput, base::Unretained(this)))); | |
| 378 } | |
| 379 | |
| 380 // Since the resampler / output device may want a different buffer size than | |
| 381 // the caller asked for, we need to use a FIFO to ensure that both sides | |
| 382 // read in chunk sizes they're configured for. | |
| 383 if (input_params.sample_rate() != output_params.sample_rate() || | |
| 384 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | |
| 385 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() | |
| 386 << " to " << output_params.frames_per_buffer(); | |
| 387 audio_fifo_.reset(new AudioPullFifo( | |
| 388 downmix_early_ ? output_params.channels() : | |
| 389 input_params.channels(), | |
| 390 input_params.frames_per_buffer(), base::Bind( | |
| 391 &OnMoreDataResampler::SourceCallback_Locked, | |
| 392 base::Unretained(this)))); | |
| 393 } | |
| 394 } | 287 } |
| 395 | 288 |
| 396 OnMoreDataResampler::~OnMoreDataResampler() {} | 289 OnMoreDataTransform::~OnMoreDataTransform() {} |
| 397 | 290 |
| 398 void OnMoreDataResampler::Start( | 291 void OnMoreDataTransform::Start( |
| 399 AudioOutputStream::AudioSourceCallback* callback) { | 292 AudioOutputStream::AudioSourceCallback* callback) { |
| 400 base::AutoLock auto_lock(source_lock_); | 293 base::AutoLock auto_lock(source_lock_); |
| 401 DCHECK(!source_callback_); | 294 DCHECK(!source_callback_); |
| 402 source_callback_ = callback; | 295 source_callback_ = callback; |
| 296 audio_transform_.AddInput(this); | |
|
Chris Rogers
2012/11/14 23:50:49
It seems like AudioTransform is capable of doing m
DaleCurtis
2012/11/16 23:51:05
Done.
| |
| 403 } | 297 } |
| 404 | 298 |
| 405 void OnMoreDataResampler::Stop() { | 299 void OnMoreDataTransform::Stop() { |
| 406 base::AutoLock auto_lock(source_lock_); | 300 base::AutoLock auto_lock(source_lock_); |
| 407 source_callback_ = NULL; | 301 source_callback_ = NULL; |
| 408 outstanding_audio_bytes_ = 0; | 302 audio_transform_.RemoveInput(this); |
| 409 if (audio_fifo_) | |
| 410 audio_fifo_->Clear(); | |
| 411 if (resampler_) | |
| 412 resampler_->Flush(); | |
| 413 } | 303 } |
| 414 | 304 |
| 415 int OnMoreDataResampler::OnMoreData(AudioBus* dest, | 305 int OnMoreDataTransform::OnMoreData(AudioBus* dest, |
| 416 AudioBuffersState buffers_state) { | 306 AudioBuffersState buffers_state) { |
| 417 return OnMoreIOData(NULL, dest, buffers_state); | 307 return OnMoreIOData(NULL, dest, buffers_state); |
| 418 } | 308 } |
| 419 | 309 |
| 420 int OnMoreDataResampler::OnMoreIOData(AudioBus* source, | 310 int OnMoreDataTransform::OnMoreIOData(AudioBus* source, |
| 421 AudioBus* dest, | 311 AudioBus* dest, |
| 422 AudioBuffersState buffers_state) { | 312 AudioBuffersState buffers_state) { |
| 423 base::AutoLock auto_lock(source_lock_); | 313 base::AutoLock auto_lock(source_lock_); |
| 424 // While we waited for |source_lock_| the callback might have been cleared. | 314 // While we waited for |source_lock_| the callback might have been cleared. |
| 425 if (!source_callback_) { | 315 if (!source_callback_) { |
| 426 dest->Zero(); | 316 dest->Zero(); |
| 427 return dest->frames(); | 317 return dest->frames(); |
| 428 } | 318 } |
| 429 | 319 |
| 320 source_bus_ = source; | |
| 430 current_buffers_state_ = buffers_state; | 321 current_buffers_state_ = buffers_state; |
| 322 audio_transform_.Transform(dest); | |
| 431 | 323 |
| 432 bool needs_mixing = channel_mixer_ && !downmix_early_; | 324 // Always return the full number of frames requested, ProvideInput_Locked() |
| 433 AudioBus* temp_dest = needs_mixing ? unmixed_audio_.get() : dest; | 325 // will pad with silence if it wasn't able to acquire enough data. |
| 434 | |
| 435 if (!resampler_ && !audio_fifo_) { | |
| 436 // We have no internal buffers, so clear any outstanding audio data. | |
| 437 outstanding_audio_bytes_ = 0; | |
| 438 SourceIOCallback_Locked(source, temp_dest); | |
| 439 } else { | |
| 440 if (resampler_) | |
| 441 resampler_->Resample(temp_dest, temp_dest->frames()); | |
| 442 else | |
| 443 ProvideInput(temp_dest); | |
| 444 | |
| 445 // Calculate how much data is left in the internal FIFO and resampler. | |
| 446 outstanding_audio_bytes_ -= temp_dest->frames() * output_bytes_per_frame_; | |
| 447 } | |
| 448 | |
| 449 if (needs_mixing) { | |
| 450 DCHECK_EQ(temp_dest->frames(), dest->frames()); | |
| 451 channel_mixer_->Transform(temp_dest, dest); | |
| 452 } | |
| 453 | |
| 454 // Due to rounding errors while multiplying against |io_ratio_|, | |
| 455 // |outstanding_audio_bytes_| might (rarely) slip below zero. | |
| 456 if (outstanding_audio_bytes_ < 0) { | |
| 457 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " | |
| 458 << outstanding_audio_bytes_; | |
| 459 outstanding_audio_bytes_ = 0; | |
| 460 } | |
| 461 | |
| 462 // Always return the full number of frames requested, ProvideInput() will pad | |
| 463 // with silence if it wasn't able to acquire enough data. | |
| 464 return dest->frames(); | 326 return dest->frames(); |
| 465 } | 327 } |
| 466 | 328 |
| 467 void OnMoreDataResampler::SourceCallback_Locked(AudioBus* dest) { | 329 float OnMoreDataTransform::ProvideAudioTransformInput( |
| 468 SourceIOCallback_Locked(NULL, dest); | 330 AudioBus* dest, base::TimeDelta buffer_delay) { |
| 331 source_lock_.AssertAcquired(); | |
| 332 | |
| 333 // Adjust playback delay to include |buffer_delay|. | |
| 334 AudioBuffersState new_buffers_state; | |
| 335 new_buffers_state.pending_bytes = | |
| 336 io_ratio_ * (current_buffers_state_.total_bytes() + | |
| 337 buffer_delay.InSecondsF() * input_bytes_per_second_); | |
| 338 | |
| 339 // Retrieve data from the original callback. | |
| 340 int frames = source_callback_->OnMoreIOData( | |
| 341 source_bus_, dest, new_buffers_state); | |
| 342 | |
|
Chris Rogers
2012/11/14 23:50:49
If ProvideAudioTransformInput() is called multiple
DaleCurtis
2012/11/15 00:30:59
Hmmm, should I just NULL the source_bus_ after the
| |
| 343 // Zero any unfilled frames if anything was filled, otherwise we'll just | |
| 344 // return a volume of zero and let AudioTransform drop the output. | |
| 345 if (frames > 0 && frames < dest->frames()) | |
| 346 dest->ZeroFramesPartial(frames, dest->frames() - frames); | |
| 347 | |
| 348 // TODO(dalecurtis): Return the correct volume here. | |
| 349 return frames > 0 ? 1 : 0; | |
| 469 } | 350 } |
| 470 | 351 |
| 471 void OnMoreDataResampler::SourceIOCallback_Locked(AudioBus* source, | 352 void OnMoreDataTransform::OnError(AudioOutputStream* stream, int code) { |
| 472 AudioBus* dest) { | |
| 473 source_lock_.AssertAcquired(); | |
| 474 | |
| 475 // Adjust playback delay to include the state of the internal buffers used by | |
| 476 // the resampler and/or the FIFO. Since the sample rate and bits per channel | |
| 477 // may be different, we need to scale this value appropriately. | |
| 478 AudioBuffersState new_buffers_state; | |
| 479 new_buffers_state.pending_bytes = io_ratio_ * | |
| 480 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); | |
| 481 | |
| 482 bool needs_downmix = channel_mixer_ && downmix_early_; | |
| 483 AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest; | |
| 484 | |
| 485 // Retrieve data from the original callback. Zero any unfilled frames. | |
| 486 int frames = source_callback_->OnMoreIOData( | |
| 487 source, temp_dest, new_buffers_state); | |
| 488 if (frames < temp_dest->frames()) | |
| 489 temp_dest->ZeroFramesPartial(frames, temp_dest->frames() - frames); | |
| 490 | |
| 491 // Scale the number of frames we got back in terms of input bytes to output | |
| 492 // bytes accordingly. | |
| 493 outstanding_audio_bytes_ += | |
| 494 (temp_dest->frames() * input_bytes_per_frame_) / io_ratio_; | |
| 495 | |
| 496 if (needs_downmix) { | |
| 497 DCHECK_EQ(temp_dest->frames(), dest->frames()); | |
| 498 channel_mixer_->Transform(temp_dest, dest); | |
| 499 } | |
| 500 } | |
| 501 | |
| 502 void OnMoreDataResampler::ProvideInput(AudioBus* audio_bus) { | |
| 503 audio_fifo_->Consume(audio_bus, audio_bus->frames()); | |
| 504 } | |
| 505 | |
| 506 void OnMoreDataResampler::OnError(AudioOutputStream* stream, int code) { | |
| 507 base::AutoLock auto_lock(source_lock_); | 353 base::AutoLock auto_lock(source_lock_); |
| 508 if (source_callback_) | 354 if (source_callback_) |
| 509 source_callback_->OnError(stream, code); | 355 source_callback_->OnError(stream, code); |
| 510 } | 356 } |
| 511 | 357 |
| 512 void OnMoreDataResampler::WaitTillDataReady() { | 358 void OnMoreDataTransform::WaitTillDataReady() { |
| 513 base::AutoLock auto_lock(source_lock_); | 359 base::AutoLock auto_lock(source_lock_); |
| 514 if (source_callback_ && !outstanding_audio_bytes_) | 360 if (source_callback_) |
| 515 source_callback_->WaitTillDataReady(); | 361 source_callback_->WaitTillDataReady(); |
| 516 } | 362 } |
| 517 | 363 |
| 518 } // namespace media | 364 } // namespace media |
| OLD | NEW |