Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: media/audio/audio_output_resampler.cc

Issue 10918098: Introduce AudioOutputResampler for browser side resampling. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Comments. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698