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/stringprintf.h" | |
| 10 #include "base/compiler_specific.h" | |
|
scherkus (not reviewing)
2012/09/07 13:30:16
a->z ordering
DaleCurtis
2012/09/10 12:06:24
Removed.
| |
| 11 #include "base/message_loop.h" | |
| 12 #include "base/time.h" | |
| 13 #include "media/audio/audio_io.h" | |
| 14 #include "media/audio/audio_output_dispatcher_impl.h" | |
| 15 #include "media/audio/audio_output_proxy.h" | |
| 16 #include "media/audio/audio_util.h" | |
| 17 #include "media/base/audio_fifo.h" | |
| 18 #include "media/base/multi_channel_resampler.h" | |
| 19 | |
| 20 | |
| 21 namespace media { | |
| 22 | |
| 23 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, | |
| 24 const AudioParameters& input_params, | |
| 25 const base::TimeDelta& close_delay) | |
| 26 : AudioOutputDispatcher(audio_manager, input_params), | |
| 27 source_callback_(NULL), | |
| 28 outstanding_audio_bytes_(0), | |
| 29 io_ratio_(0), | |
| 30 output_bytes_per_frame_(0), | |
| 31 input_bytes_per_frame_(0) { | |
| 32 AudioParameters output_params = input_params; | |
| 33 int hardware_sample_rate = GetAudioHardwareSampleRate(); | |
| 34 int hardware_buffer_size = GetAudioHardwareBufferSize(); | |
| 35 | |
| 36 // Only resample if necessary since it's expensive. | |
| 37 // TODO(dalecurtis): Restrict to PCM low latency before landing. Right now it | |
| 38 // converts all non-low latency calls as well for testing. | |
| 39 if (input_params.sample_rate() != hardware_sample_rate || | |
| 40 input_params.frames_per_buffer() != hardware_buffer_size) { | |
| 41 // Create output parameters based on the audio hardware configuration. | |
| 42 // TODO(dalecurtis): This should include channel count and bit depth. | |
| 43 output_params = AudioParameters( | |
| 44 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(), | |
| 45 hardware_sample_rate, 16, GetAudioHardwareBufferSize()); | |
| 46 input_bytes_per_frame_ = input_params.GetBytesPerFrame(); | |
| 47 output_bytes_per_frame_ = output_params.GetBytesPerFrame(); | |
| 48 | |
| 49 // Calculate the first part of the I/O ratio for the resampler. | |
| 50 io_ratio_ = | |
| 51 input_params.sample_rate() / static_cast<double>(hardware_sample_rate); | |
| 52 resampler_.reset(new MultiChannelResampler( | |
| 53 output_params.channels(), | |
| 54 io_ratio_, | |
| 55 base::Bind( | |
| 56 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); | |
| 57 | |
| 58 // Include the difference in output bits per sample. | |
| 59 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / | |
| 60 output_params.bits_per_sample(); | |
| 61 | |
| 62 // TODO(dalecurtis): Initialize AudioPullFIFO(input_params.buffer_frames()). | |
| 63 temp_bus_ = AudioBus::Create(input_params); | |
| 64 audio_fifo_.reset(new AudioFifo( | |
|
Chris Rogers
2012/09/06 19:18:25
Even if we're not doing sample-rate conversion, we
DaleCurtis
2012/09/07 14:17:35
Good point, I'll rework it so buffer size differen
DaleCurtis
2012/09/10 12:06:24
Done.
| |
| 65 input_params.channels(), input_params.frames_per_buffer())); | |
| 66 } | |
| 67 | |
| 68 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | |
| 69 // we've stabilized the issues there. | |
| 70 dispatcher_ = new AudioOutputDispatcherImpl( | |
| 71 audio_manager, output_params, close_delay); | |
| 72 } | |
| 73 | |
| 74 AudioOutputResampler::~AudioOutputResampler() {} | |
| 75 | |
| 76 bool AudioOutputResampler::OpenStream() { | |
| 77 return dispatcher_->OpenStream(); | |
| 78 } | |
| 79 | |
| 80 bool AudioOutputResampler::StartStream( | |
| 81 AudioOutputStream::AudioSourceCallback* callback, | |
| 82 AudioOutputProxy* stream_proxy) { | |
| 83 { | |
| 84 base::AutoLock auto_lock(source_lock_); | |
| 85 source_callback_ = callback; | |
| 86 } | |
| 87 return dispatcher_->StartStream(this, stream_proxy); | |
|
scherkus (not reviewing)
2012/09/07 13:30:16
which thread is calling these methods?
is dispatc
DaleCurtis
2012/09/07 14:17:35
Called by the audio thread, AudioOutputDispatcherI
| |
| 88 } | |
| 89 | |
| 90 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { | |
| 91 { | |
| 92 base::AutoLock auto_lock(source_lock_); | |
| 93 source_callback_ = NULL; | |
| 94 outstanding_audio_bytes_ = 0; | |
| 95 audio_fifo_->Clear(); | |
| 96 resampler_->Flush(); | |
| 97 } | |
| 98 dispatcher_->StopStream(stream_proxy); | |
| 99 } | |
| 100 | |
| 101 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, | |
| 102 double volume) { | |
| 103 dispatcher_->StreamVolumeSet(stream_proxy, volume); | |
| 104 } | |
| 105 | |
| 106 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { | |
| 107 { | |
| 108 base::AutoLock auto_lock(source_lock_); | |
| 109 source_callback_ = NULL; | |
| 110 outstanding_audio_bytes_ = 0; | |
| 111 audio_fifo_->Clear(); | |
| 112 resampler_->Flush(); | |
| 113 } | |
| 114 dispatcher_->CloseStream(stream_proxy); | |
| 115 } | |
| 116 | |
| 117 void AudioOutputResampler::Shutdown() { | |
| 118 { | |
| 119 base::AutoLock auto_lock(source_lock_); | |
| 120 source_callback_ = NULL; | |
| 121 outstanding_audio_bytes_ = 0; | |
| 122 audio_fifo_->Clear(); | |
| 123 resampler_->Flush(); | |
| 124 } | |
| 125 dispatcher_->Shutdown(); | |
| 126 } | |
| 127 | |
| 128 int AudioOutputResampler::OnMoreData(AudioBus* audio_bus, | |
| 129 AudioBuffersState buffers_state) { | |
| 130 if (!resampler_.get()) { | |
| 131 // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? | |
|
scherkus (not reviewing)
2012/09/07 13:30:16
what's ASB?
DaleCurtis
2012/09/07 14:17:35
A typo :) Should be ASCB.. Essentially instead of
| |
| 132 base::AutoLock auto_lock(source_lock_); | |
| 133 return source_callback_->OnMoreData(audio_bus, buffers_state); | |
|
Chris Rogers
2012/09/06 19:18:25
We need to have a conditional here to check if we
DaleCurtis
2012/09/07 14:17:35
Will investigate, shouldn't be too hard.
DaleCurtis
2012/09/10 12:06:24
Done.
| |
| 134 } | |
| 135 | |
| 136 current_buffers_state_ = buffers_state; | |
| 137 resampler_->Resample(audio_bus, audio_bus->frames()); | |
| 138 | |
| 139 // Calculate how much data is left in the internal FIFO and resampler buffers. | |
| 140 outstanding_audio_bytes_ -= audio_bus->frames() * output_bytes_per_frame_; | |
| 141 CHECK_GE(outstanding_audio_bytes_, 0); | |
| 142 | |
| 143 // Always return the full number of frames requested, ProvideInput() will pad | |
| 144 // with silence if it wasn't able to acquire enough data. | |
| 145 // NOTE: This will not work with the current non-low latency <audio> pipeline. | |
| 146 return audio_bus->frames(); | |
| 147 } | |
| 148 | |
| 149 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { | |
| 150 // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? | |
| 151 base::AutoLock auto_lock(source_lock_); | |
| 152 // We may have waited for |source_lock_| while a call cleared the callback. | |
| 153 if (!source_callback_) | |
| 154 return; | |
| 155 | |
| 156 // Serve frames out of the FIFO if they're available. | |
| 157 int frames_to_write = audio_bus->frames(); | |
| 158 if (audio_fifo_->frames_in_fifo() > 0) { | |
| 159 int frames = std::min(audio_bus->frames(), audio_fifo_->frames_in_fifo()); | |
| 160 CHECK(audio_fifo_->Consume(audio_bus, 0, frames)); | |
|
scherkus (not reviewing)
2012/09/07 13:30:17
:)
| |
| 161 frames_to_write -= frames; | |
| 162 } | |
| 163 | |
| 164 // If we were able to fulfill the request from the FIFO, we're done. | |
| 165 if (frames_to_write == 0) | |
| 166 return; | |
| 167 | |
| 168 // Adjust playback delay to include the state of the internal buffers used by | |
| 169 // the resampler and the FIFO. Since the sample rate and bits per channel | |
| 170 // may be different, we need to scale this value appropriately | |
| 171 // TODO(dalecurtis): Move to the FIFO side callback once the FIFO is added. | |
| 172 AudioBuffersState new_buffers_state; | |
| 173 new_buffers_state.pending_bytes = io_ratio_ * | |
| 174 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); | |
| 175 | |
| 176 // Retrieve data from the original callback. | |
| 177 int frames = source_callback_->OnMoreData(temp_bus_.get(), new_buffers_state); | |
| 178 | |
| 179 // TODO(dalecurtis): Technically only <audio> returns fewer frames than | |
| 180 // requested and it won't use this path... but since I tested with <audio> on | |
| 181 // the high latency path it was necessary. Remove later. | |
| 182 | |
| 183 // Scale the number of frames we got back in terms of input bytes to output | |
| 184 // bytes accordingly. | |
| 185 outstanding_audio_bytes_ += (frames * input_bytes_per_frame_) / io_ratio_; | |
| 186 | |
| 187 // Put everything into the FIFO and fulfill the rest of the request. | |
| 188 CHECK(audio_fifo_->Push(temp_bus_.get(), frames)); | |
| 189 CHECK(audio_fifo_->Consume( | |
| 190 audio_bus, audio_bus->frames() - frames_to_write, frames_to_write)); | |
| 191 } | |
| 192 | |
| 193 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { | |
| 194 // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? | |
| 195 base::AutoLock auto_lock(source_lock_); | |
| 196 if (source_callback_) | |
| 197 source_callback_->OnError(stream, code); | |
| 198 } | |
| 199 | |
| 200 void AudioOutputResampler::WaitTillDataReady() { | |
| 201 // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? | |
| 202 base::AutoLock auto_lock(source_lock_); | |
| 203 if (source_callback_ && !outstanding_audio_bytes_) | |
| 204 source_callback_->WaitTillDataReady(); | |
| 205 } | |
| 206 | |
| 207 } // namespace media | |
| OLD | NEW |