Report errors from ChromiumEnv::GetChildren in Posix.
[chromium-blink-merge.git] / media / audio / audio_output_resampler.cc
blob30f4ec12cdca4efa8719acc9094eb5d2081c587d
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.
5 #include "media/audio/audio_output_resampler.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/time/time.h"
13 #include "build/build_config.h"
14 #include "media/audio/audio_io.h"
15 #include "media/audio/audio_output_dispatcher_impl.h"
16 #include "media/audio/audio_output_proxy.h"
17 #include "media/audio/sample_rates.h"
18 #include "media/base/audio_converter.h"
19 #include "media/base/limits.h"
21 namespace media {
23 class OnMoreDataConverter
24 : public AudioOutputStream::AudioSourceCallback,
25 public AudioConverter::InputCallback {
26 public:
27 OnMoreDataConverter(const AudioParameters& input_params,
28 const AudioParameters& output_params);
29 virtual ~OnMoreDataConverter();
31 // AudioSourceCallback interface.
32 virtual int OnMoreData(AudioBus* dest,
33 AudioBuffersState buffers_state) OVERRIDE;
34 virtual int OnMoreIOData(AudioBus* source,
35 AudioBus* dest,
36 AudioBuffersState buffers_state) OVERRIDE;
37 virtual void OnError(AudioOutputStream* stream) OVERRIDE;
39 // Sets |source_callback_|. If this is not a new object, then Stop() must be
40 // called before Start().
41 void Start(AudioOutputStream::AudioSourceCallback* callback);
43 // Clears |source_callback_| and flushes the resampler.
44 void Stop();
46 private:
47 // AudioConverter::InputCallback implementation.
48 virtual double ProvideInput(AudioBus* audio_bus,
49 base::TimeDelta buffer_delay) OVERRIDE;
51 // Ratio of input bytes to output bytes used to correct playback delay with
52 // regard to buffering and resampling.
53 double io_ratio_;
55 // Source callback and associated lock.
56 base::Lock source_lock_;
57 AudioOutputStream::AudioSourceCallback* source_callback_;
59 // |source| passed to OnMoreIOData() which should be passed downstream.
60 AudioBus* source_bus_;
62 // Last AudioBuffersState object received via OnMoreData(), used to correct
63 // playback delay by ProvideInput() and passed on to |source_callback_|.
64 AudioBuffersState current_buffers_state_;
66 const int input_bytes_per_second_;
68 // Handles resampling, buffering, and channel mixing between input and output
69 // parameters.
70 AudioConverter audio_converter_;
72 DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
75 // Record UMA statistics for hardware output configuration.
76 static void RecordStats(const AudioParameters& output_params) {
77 UMA_HISTOGRAM_ENUMERATION(
78 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(),
79 limits::kMaxBitsPerSample);
80 UMA_HISTOGRAM_ENUMERATION(
81 "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
82 CHANNEL_LAYOUT_MAX);
83 UMA_HISTOGRAM_ENUMERATION(
84 "Media.HardwareAudioChannelCount", output_params.channels(),
85 limits::kMaxChannels);
87 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
88 if (asr != kUnexpectedAudioSampleRate) {
89 UMA_HISTOGRAM_ENUMERATION(
90 "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate);
91 } else {
92 UMA_HISTOGRAM_COUNTS(
93 "Media.HardwareAudioSamplesPerSecondUnexpected",
94 output_params.sample_rate());
98 // Record UMA statistics for hardware output configuration after fallback.
99 static void RecordFallbackStats(const AudioParameters& output_params) {
100 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
101 UMA_HISTOGRAM_ENUMERATION(
102 "Media.FallbackHardwareAudioBitsPerChannel",
103 output_params.bits_per_sample(), limits::kMaxBitsPerSample);
104 UMA_HISTOGRAM_ENUMERATION(
105 "Media.FallbackHardwareAudioChannelLayout",
106 output_params.channel_layout(), CHANNEL_LAYOUT_MAX);
107 UMA_HISTOGRAM_ENUMERATION(
108 "Media.FallbackHardwareAudioChannelCount",
109 output_params.channels(), limits::kMaxChannels);
111 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
112 if (asr != kUnexpectedAudioSampleRate) {
113 UMA_HISTOGRAM_ENUMERATION(
114 "Media.FallbackHardwareAudioSamplesPerSecond",
115 asr, kUnexpectedAudioSampleRate);
116 } else {
117 UMA_HISTOGRAM_COUNTS(
118 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
119 output_params.sample_rate());
123 // Only Windows has a high latency output driver that is not the same as the low
124 // latency path.
125 #if defined(OS_WIN)
126 // Converts low latency based |output_params| into high latency appropriate
127 // output parameters in error situations.
128 static AudioParameters SetupFallbackParams(
129 const AudioParameters& input_params, const AudioParameters& output_params) {
130 // Choose AudioParameters appropriate for opening the device in high latency
131 // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
132 // MAXIMUM frame size for low latency.
133 static const int kMinLowLatencyFrameSize = 2048;
134 const int frames_per_buffer =
135 std::max(input_params.frames_per_buffer(), kMinLowLatencyFrameSize);
137 return AudioParameters(
138 AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(),
139 input_params.sample_rate(), input_params.bits_per_sample(),
140 frames_per_buffer);
142 #endif
144 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
145 const AudioParameters& input_params,
146 const AudioParameters& output_params,
147 const std::string& output_device_id,
148 const std::string& input_device_id,
149 const base::TimeDelta& close_delay)
150 : AudioOutputDispatcher(audio_manager, input_params, output_device_id,
151 input_device_id),
152 close_delay_(close_delay),
153 output_params_(output_params),
154 streams_opened_(false) {
155 DCHECK(input_params.IsValid());
156 DCHECK(output_params.IsValid());
157 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
159 // Record UMA statistics for the hardware configuration.
160 RecordStats(output_params);
162 Initialize();
165 AudioOutputResampler::~AudioOutputResampler() {
166 DCHECK(callbacks_.empty());
169 void AudioOutputResampler::Initialize() {
170 DCHECK(!streams_opened_);
171 DCHECK(callbacks_.empty());
172 dispatcher_ = new AudioOutputDispatcherImpl(
173 audio_manager_, output_params_, output_device_id_, input_device_id_,
174 close_delay_);
177 bool AudioOutputResampler::OpenStream() {
178 DCHECK(message_loop_->BelongsToCurrentThread());
180 if (dispatcher_->OpenStream()) {
181 // Only record the UMA statistic if we didn't fallback during construction
182 // and only for the first stream we open.
183 if (!streams_opened_ &&
184 output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
185 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
187 streams_opened_ = true;
188 return true;
191 // If we've already tried to open the stream in high latency mode or we've
192 // successfully opened a stream previously, there's nothing more to be done.
193 if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
194 streams_opened_ || !callbacks_.empty()) {
195 return false;
198 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
200 // Record UMA statistics about the hardware which triggered the failure so
201 // we can debug and triage later.
202 RecordFallbackStats(output_params_);
204 // Only Windows has a high latency output driver that is not the same as the
205 // low latency path.
206 #if defined(OS_WIN)
207 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
208 << "back to high latency audio output.";
210 output_params_ = SetupFallbackParams(params_, output_params_);
211 Initialize();
212 if (dispatcher_->OpenStream()) {
213 streams_opened_ = true;
214 return true;
216 #endif
218 DLOG(ERROR) << "Unable to open audio device in high latency mode. Falling "
219 << "back to fake audio output.";
221 // Finally fall back to a fake audio output device.
222 output_params_.Reset(
223 AudioParameters::AUDIO_FAKE, params_.channel_layout(),
224 params_.channels(), params_.input_channels(), params_.sample_rate(),
225 params_.bits_per_sample(), params_.frames_per_buffer());
226 Initialize();
227 if (dispatcher_->OpenStream()) {
228 streams_opened_ = true;
229 return true;
232 return false;
235 bool AudioOutputResampler::StartStream(
236 AudioOutputStream::AudioSourceCallback* callback,
237 AudioOutputProxy* stream_proxy) {
238 DCHECK(message_loop_->BelongsToCurrentThread());
240 OnMoreDataConverter* resampler_callback = NULL;
241 CallbackMap::iterator it = callbacks_.find(stream_proxy);
242 if (it == callbacks_.end()) {
243 resampler_callback = new OnMoreDataConverter(params_, output_params_);
244 callbacks_[stream_proxy] = resampler_callback;
245 } else {
246 resampler_callback = it->second;
249 resampler_callback->Start(callback);
250 bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
251 if (!result)
252 resampler_callback->Stop();
253 return result;
256 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
257 double volume) {
258 DCHECK(message_loop_->BelongsToCurrentThread());
259 dispatcher_->StreamVolumeSet(stream_proxy, volume);
262 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
263 DCHECK(message_loop_->BelongsToCurrentThread());
264 dispatcher_->StopStream(stream_proxy);
266 // Now that StopStream() has completed the underlying physical stream should
267 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
268 // OnMoreDataConverter.
269 CallbackMap::iterator it = callbacks_.find(stream_proxy);
270 if (it != callbacks_.end())
271 it->second->Stop();
274 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
275 DCHECK(message_loop_->BelongsToCurrentThread());
276 dispatcher_->CloseStream(stream_proxy);
278 // We assume that StopStream() is always called prior to CloseStream(), so
279 // that it is safe to delete the OnMoreDataConverter here.
280 CallbackMap::iterator it = callbacks_.find(stream_proxy);
281 if (it != callbacks_.end()) {
282 delete it->second;
283 callbacks_.erase(it);
287 void AudioOutputResampler::Shutdown() {
288 DCHECK(message_loop_->BelongsToCurrentThread());
290 // No AudioOutputProxy objects should hold a reference to us when we get
291 // to this stage.
292 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
294 dispatcher_->Shutdown();
295 DCHECK(callbacks_.empty());
298 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
299 const AudioParameters& output_params)
300 : source_callback_(NULL),
301 source_bus_(NULL),
302 input_bytes_per_second_(input_params.GetBytesPerSecond()),
303 audio_converter_(input_params, output_params, false) {
304 io_ratio_ =
305 static_cast<double>(input_params.GetBytesPerSecond()) /
306 output_params.GetBytesPerSecond();
309 OnMoreDataConverter::~OnMoreDataConverter() {
310 // Ensure Stop() has been called so we don't end up with an AudioOutputStream
311 // calling back into OnMoreData() after destruction.
312 CHECK(!source_callback_);
315 void OnMoreDataConverter::Start(
316 AudioOutputStream::AudioSourceCallback* callback) {
317 base::AutoLock auto_lock(source_lock_);
318 CHECK(!source_callback_);
319 source_callback_ = callback;
321 // While AudioConverter can handle multiple inputs, we're using it only with
322 // a single input currently. Eventually this may be the basis for a browser
323 // side mixer.
324 audio_converter_.AddInput(this);
327 void OnMoreDataConverter::Stop() {
328 base::AutoLock auto_lock(source_lock_);
329 CHECK(source_callback_);
330 source_callback_ = NULL;
331 audio_converter_.RemoveInput(this);
334 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
335 AudioBuffersState buffers_state) {
336 return OnMoreIOData(NULL, dest, buffers_state);
339 int OnMoreDataConverter::OnMoreIOData(AudioBus* source,
340 AudioBus* dest,
341 AudioBuffersState buffers_state) {
342 base::AutoLock auto_lock(source_lock_);
343 // While we waited for |source_lock_| the callback might have been cleared.
344 if (!source_callback_) {
345 dest->Zero();
346 return dest->frames();
349 source_bus_ = source;
350 current_buffers_state_ = buffers_state;
351 audio_converter_.Convert(dest);
353 // Always return the full number of frames requested, ProvideInput_Locked()
354 // will pad with silence if it wasn't able to acquire enough data.
355 return dest->frames();
358 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
359 base::TimeDelta buffer_delay) {
360 source_lock_.AssertAcquired();
362 // Adjust playback delay to include |buffer_delay|.
363 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
364 // AudioBus is just float data. Use TimeDelta instead.
365 AudioBuffersState new_buffers_state;
366 new_buffers_state.pending_bytes =
367 io_ratio_ * (current_buffers_state_.total_bytes() +
368 buffer_delay.InSecondsF() * input_bytes_per_second_);
370 // Retrieve data from the original callback.
371 int frames = source_callback_->OnMoreIOData(
372 source_bus_, dest, new_buffers_state);
374 // |source_bus_| should only be provided once.
375 // TODO(dalecurtis, crogers): This is not a complete fix. If ProvideInput()
376 // is called multiple times, we need to do something more clever here.
377 source_bus_ = NULL;
379 // Zero any unfilled frames if anything was filled, otherwise we'll just
380 // return a volume of zero and let AudioConverter drop the output.
381 if (frames > 0 && frames < dest->frames())
382 dest->ZeroFramesPartial(frames, dest->frames() - frames);
384 // TODO(dalecurtis): Return the correct volume here.
385 return frames > 0 ? 1 : 0;
388 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
389 base::AutoLock auto_lock(source_lock_);
390 if (source_callback_)
391 source_callback_->OnError(stream);
394 } // namespace media