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_resampler.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/compiler_specific.h" | |
| 10 #include "base/message_loop.h" | |
| 11 #include "base/time.h" | |
| 12 #include "media/audio/audio_io.h" | |
| 13 #include "media/audio/audio_output_dispatcher_impl.h" | |
| 14 #include "media/audio/audio_output_proxy.h" | |
| 15 #include "media/audio/audio_util.h" | |
| 16 #include "media/base/audio_pull_fifo.h" | |
| 17 #include "media/base/multi_channel_resampler.h" | |
| 18 | |
| 19 namespace media { | |
| 20 | |
| 21 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, | |
| 22 const AudioParameters& input_params, | |
| 23 const AudioParameters& output_params, | |
| 24 const base::TimeDelta& close_delay) | |
| 25 : AudioOutputDispatcher(audio_manager, input_params), | |
| 26 source_callback_(NULL), | |
| 27 io_ratio_(1), | |
| 28 input_bytes_per_frame_(input_params.GetBytesPerFrame()), | |
| 29 output_bytes_per_frame_(output_params.GetBytesPerFrame()), | |
| 30 outstanding_audio_bytes_(0) { | |
| 31 // Only resample or rebuffer if the input parameters don't match the output | |
| 32 // parameters to avoid any unnecessary work. | |
| 33 if (input_params.channels() != output_params.channels() || | |
| 34 input_params.sample_rate() != output_params.sample_rate() || | |
| 35 input_params.bits_per_sample() != output_params.bits_per_sample() || | |
| 36 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | |
| 37 // Only resample if necessary since it's expensive. | |
| 38 if (input_params.sample_rate() != output_params.sample_rate()) { | |
| 39 double io_sample_rate_ratio = input_params.sample_rate() / | |
| 40 static_cast<double>(output_params.sample_rate()); | |
| 41 // Include the I/O resampling ratio in our global I/O ratio. | |
| 42 io_ratio_ *= io_sample_rate_ratio; | |
| 43 resampler_.reset(new MultiChannelResampler( | |
| 44 output_params.channels(), io_sample_rate_ratio, base::Bind( | |
| 45 &AudioOutputResampler::ProvideInput, this))); | |
| 46 } | |
| 47 | |
| 48 // Include bits per channel differences. | |
| 49 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / | |
| 50 output_params.bits_per_sample(); | |
| 51 | |
| 52 // Include channel count differences. | |
| 53 io_ratio_ *= static_cast<double>(input_params.channels()) / | |
| 54 output_params.channels(); | |
| 55 | |
| 56 // Since the resampler / output device may want a different buffer size than | |
| 57 // the caller asked for, we need to use a FIFO to ensure that both sides | |
| 58 // read in chunk sizes they're configured for. | |
| 59 if (input_params.sample_rate() != output_params.sample_rate() || | |
| 60 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | |
| 61 audio_fifo_.reset(new AudioPullFifo( | |
| 62 input_params.channels(), input_params.frames_per_buffer(), base::Bind( | |
| 63 &AudioOutputResampler::SourceCallback, this))); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | |
| 68 // we've stabilized the issues there. | |
| 69 dispatcher_ = new AudioOutputDispatcherImpl( | |
| 70 audio_manager, output_params, close_delay); | |
| 71 } | |
| 72 | |
| 73 AudioOutputResampler::~AudioOutputResampler() {} | |
| 74 | |
| 75 bool AudioOutputResampler::OpenStream() { | |
| 76 // TODO(dalecurtis): Automatically revert to high latency path if OpenStream() | |
| 77 // fails; use default high latency output values + rebuffering / resampling. | |
| 78 return dispatcher_->OpenStream(); | |
| 79 } | |
| 80 | |
| 81 bool AudioOutputResampler::StartStream( | |
| 82 AudioOutputStream::AudioSourceCallback* callback, | |
| 83 AudioOutputProxy* stream_proxy) { | |
| 84 { | |
| 85 base::AutoLock auto_lock(source_lock_); | |
| 86 source_callback_ = callback; | |
| 87 } | |
| 88 return dispatcher_->StartStream(this, stream_proxy); | |
| 89 } | |
| 90 | |
| 91 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, | |
| 92 double volume) { | |
| 93 dispatcher_->StreamVolumeSet(stream_proxy, volume); | |
| 94 } | |
| 95 | |
| 96 void AudioOutputResampler::Reset() { | |
| 97 base::AutoLock auto_lock(source_lock_); | |
| 98 source_callback_ = NULL; | |
| 99 outstanding_audio_bytes_ = 0; | |
| 100 if (audio_fifo_.get()) | |
| 101 audio_fifo_->Clear(); | |
| 102 if (resampler_.get()) | |
| 103 resampler_->Flush(); | |
| 104 } | |
| 105 | |
| 106 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { | |
| 107 Reset(); | |
| 108 dispatcher_->StopStream(stream_proxy); | |
| 109 } | |
| 110 | |
| 111 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { | |
| 112 Reset(); | |
| 113 dispatcher_->CloseStream(stream_proxy); | |
| 114 } | |
| 115 | |
| 116 void AudioOutputResampler::Shutdown() { | |
| 117 Reset(); | |
| 118 dispatcher_->Shutdown(); | |
| 119 } | |
| 120 | |
| 121 int AudioOutputResampler::OnMoreData(AudioBus* audio_bus, | |
| 122 AudioBuffersState buffers_state) { | |
| 123 current_buffers_state_ = buffers_state; | |
| 124 | |
| 125 if (!resampler_.get() && !audio_fifo_.get()) { | |
| 126 // We have no internal buffers, so clear any outstanding audio data. | |
| 127 outstanding_audio_bytes_ = 0; | |
| 128 SourceCallback(audio_bus); | |
| 129 return audio_bus->frames(); | |
| 130 } | |
| 131 | |
| 132 if (resampler_.get()) | |
| 133 resampler_->Resample(audio_bus, audio_bus->frames()); | |
| 134 else | |
| 135 ProvideInput(audio_bus); | |
| 136 | |
| 137 // Calculate how much data is left in the internal FIFO and resampler buffers. | |
| 138 outstanding_audio_bytes_ -= audio_bus->frames() * output_bytes_per_frame_; | |
| 139 // Due to rounding errors while multiplying against |io_ratio_|, | |
| 140 // |outstanding_audio_bytes_| might (rarely) slip below zero. | |
| 141 if (outstanding_audio_bytes_ < 0) { | |
| 142 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " | |
| 143 << outstanding_audio_bytes_; | |
| 144 outstanding_audio_bytes_ = 0; | |
| 145 } | |
| 146 | |
| 147 // Always return the full number of frames requested, ProvideInput() will pad | |
| 148 // with silence if it wasn't able to acquire enough data. | |
| 149 return audio_bus->frames(); | |
| 150 } | |
| 151 | |
| 152 void AudioOutputResampler::SourceCallback(AudioBus* audio_bus) { | |
| 153 base::AutoLock auto_lock(source_lock_); | |
| 154 // While we waited for |source_lock_| it might have been cleared. | |
| 155 // TODO(dalecurtis): Copy |source_callback_| to a local variable and release | |
| 156 // lock before calling OnMoreData? | |
| 157 if (!source_callback_) { | |
| 158 audio_bus->Zero(); | |
| 159 return; | |
| 160 } | |
| 161 | |
| 162 // Adjust playback delay to include the state of the internal buffers used by | |
| 163 // the resampler and/or the FIFO. Since the sample rate and bits per channel | |
| 164 // may be different, we need to scale this value appropriately. | |
| 165 AudioBuffersState new_buffers_state; | |
| 166 new_buffers_state.pending_bytes = io_ratio_ * | |
| 167 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); | |
| 168 | |
| 169 // Retrieve data from the original callback. Zero any unfilled frames. | |
| 170 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); | |
| 171 if (frames < audio_bus->frames()) | |
| 172 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); | |
| 173 | |
| 174 // Scale the number of frames we got back in terms of input bytes to output | |
| 175 // bytes accordingly. | |
| 176 outstanding_audio_bytes_ += | |
| 177 (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; | |
| 178 } | |
| 179 | |
| 180 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { | |
| 181 audio_fifo_->Consume(audio_bus, audio_bus->frames()); | |
| 182 } | |
| 183 | |
| 184 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { | |
| 185 // TODO(dalecurtis): Copy ASCB to local variable, release lock prior to call? | |
|
scherkus (not reviewing)
2012/09/10 14:25:32
I see that source_callback_ gets set to NULL but w
DaleCurtis
2012/09/10 14:53:51
Yes, that's my concern as well which is why I adde
| |
| 186 base::AutoLock auto_lock(source_lock_); | |
| 187 if (source_callback_) | |
| 188 source_callback_->OnError(stream, code); | |
| 189 } | |
| 190 | |
| 191 void AudioOutputResampler::WaitTillDataReady() { | |
| 192 // TODO(dalecurtis): Copy ASCB to local variable, release lock prior to call? | |
| 193 base::AutoLock auto_lock(source_lock_); | |
| 194 if (source_callback_ && !outstanding_audio_bytes_) | |
|
scherkus (not reviewing)
2012/09/10 14:25:32
does outstanding_audio_bytes_ need to be protected
DaleCurtis
2012/09/10 14:53:51
No, it's called on the same thread as OnMoreData (
| |
| 195 source_callback_->WaitTillDataReady(); | |
| 196 } | |
| 197 | |
| 198 } // namespace media | |
| OLD | NEW |